mirror of
https://github.com/soypat/lneto.git
synced 2026-08-16 21:03:25 +00:00
add ARP; add tap internal calls; more lneto2 additions
This commit is contained in:
@@ -15,6 +15,9 @@ vendor/
|
||||
*.so
|
||||
*.dylib
|
||||
*.hex
|
||||
# example binaries.
|
||||
/tap
|
||||
/stack
|
||||
**__debug_bin*
|
||||
# `__debug_bin` Debug binary generated in VSCode when using the built-in debugger.
|
||||
*bin
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package arp
|
||||
|
||||
import "errors"
|
||||
|
||||
//go:generate stringer -type=Operation -linecomment -output stringers.go .
|
||||
|
||||
const (
|
||||
sizeHeader = 8
|
||||
sizeHeaderv4 = sizeHeader + 6*2 + 4*2
|
||||
sizeHeaderv6 = sizeHeader + 6*2 + 16*2
|
||||
)
|
||||
|
||||
var (
|
||||
errARPBufferFull = errors.New("ARP client need handling:too many ops pending")
|
||||
errShortARP = errors.New("packet too short to be ARP")
|
||||
errARPUnsupported = errors.New("ARP not supprortedf")
|
||||
)
|
||||
|
||||
// Operation represents the type of ARP packet, either request or reply/response.
|
||||
type Operation uint8
|
||||
|
||||
const (
|
||||
OpRequest Operation = 1 // request
|
||||
OpReply Operation = 2 // reply
|
||||
)
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package arp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto/lneto2"
|
||||
)
|
||||
|
||||
// NewARPFrame returns a ARPFrame with data set to buf.
|
||||
// An error is returned if the buffer size is smaller than 28 (IPv4 min size).
|
||||
// Users should still call [ARPFrame.ValidateSize] before working
|
||||
// with payload/options of frames to avoid panics.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < sizeHeaderv4 {
|
||||
return Frame{buf: nil}, errors.New("ARP packet too short")
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
|
||||
// Frame encapsulates the raw data of an ARP packet
|
||||
// and provides methods for manipulating, validating and
|
||||
// retrieving fields and payload data. See [RFC826].
|
||||
//
|
||||
// [RFC826]: https://tools.ietf.org/html/rfc826
|
||||
type Frame struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// RawData returns the underlying slice with which the frame was created.
|
||||
func (afrm Frame) RawData() []byte { return afrm.buf }
|
||||
|
||||
// HardwareType specifies the network link protocol type. Example: Ethernet is 1.
|
||||
func (afrm Frame) Hardware() (Type uint16, length uint8) {
|
||||
Type = binary.BigEndian.Uint16(afrm.buf[0:2])
|
||||
return Type, afrm.hwlen()
|
||||
}
|
||||
|
||||
func (afrm Frame) hwlen() uint8 {
|
||||
return afrm.buf[4]
|
||||
}
|
||||
|
||||
// SetHardware sets the networl link protocol type. See [Frame.SetHardware].
|
||||
func (afrm Frame) SetHardware(Type uint16, length uint8) {
|
||||
binary.BigEndian.PutUint16(afrm.buf[0:2], Type)
|
||||
afrm.buf[4] = length
|
||||
}
|
||||
|
||||
// Protocol returns the internet protocol type and length. See [lneto2.EtherType].
|
||||
func (afrm Frame) Protocol() (Type lneto2.EtherType, length uint8) {
|
||||
Type = lneto2.EtherType(binary.BigEndian.Uint16(afrm.buf[2:4]))
|
||||
return Type, afrm.protolen()
|
||||
}
|
||||
|
||||
func (afrm Frame) protolen() uint8 { return afrm.buf[5] }
|
||||
|
||||
// SetProtocol sets the protocol type and length fields of the ARP frame. See [Frame.Protocol] and [lneto2.EtherType].
|
||||
func (afrm Frame) SetProtocol(Type lneto2.EtherType, length uint8) {
|
||||
binary.BigEndian.PutUint16(afrm.buf[2:4], uint16(Type))
|
||||
afrm.buf[5] = length
|
||||
}
|
||||
|
||||
// Operation returns the ARP header operation field. See [Operation].
|
||||
func (afrm Frame) Operation() Operation { return Operation(afrm.buf[6]) }
|
||||
|
||||
// SetOperation sets the ARP header operation field. See [Operation].
|
||||
func (afrm Frame) SetOperation(b Operation) { afrm.buf[6] = uint8(b) }
|
||||
|
||||
// Sender returns the hardware (MAC) and protocol addresses of sender of ARP packet.
|
||||
// In an ARP request MAC address is used to indicate
|
||||
// the address of the host sending the request. In an ARP reply MAC address is
|
||||
// used to indicate the address of the host that the request was looking for.
|
||||
func (afrm Frame) Sender() (hardwareAddr []byte, proto []byte) {
|
||||
_, hlen := afrm.Hardware()
|
||||
_, ilen := afrm.Protocol()
|
||||
return afrm.buf[8 : 8+hlen], afrm.buf[8+hlen : 8+hlen+ilen]
|
||||
}
|
||||
|
||||
// Target returns the hardware (MAC) and protocol addresses of target of ARP packet.
|
||||
// In an ARP request MAC target is ignored. In ARP reply MAC is used to indicate the address of host that originated request.
|
||||
func (afrm Frame) Target() (hardwareAddr []byte, proto []byte) {
|
||||
_, hlen := afrm.Hardware()
|
||||
_, ilen := afrm.Protocol()
|
||||
toff := 8 + hlen + ilen
|
||||
return afrm.buf[toff : toff+hlen], afrm.buf[toff+hlen : toff+hlen+ilen]
|
||||
}
|
||||
|
||||
// Sender4 returns the IPv4 sender addresses. See [Frame.Sender].
|
||||
func (afrm Frame) Sender4() (hardwareAddr *[6]byte, proto *[4]byte) {
|
||||
return (*[6]byte)(afrm.buf[8:14]), (*[4]byte)(afrm.buf[14:18])
|
||||
}
|
||||
|
||||
// Target4 returns the IPv4 target addresses. See [Frame.Sender].
|
||||
func (afrm Frame) Target4() (hardwareAddr *[6]byte, proto *[4]byte) {
|
||||
return (*[6]byte)(afrm.buf[18:24]), (*[4]byte)(afrm.buf[24:28])
|
||||
}
|
||||
|
||||
// Sender6 returns the IPv6 sender addresses. See [Frame.Sender].
|
||||
func (afrm Frame) Sender16() (hardwareAddr *[6]byte, proto *[16]byte) {
|
||||
return (*[6]byte)(afrm.buf[8:14]), (*[16]byte)(afrm.buf[14:30])
|
||||
}
|
||||
|
||||
// Target6 returns the IPv6 target addresses. See [Frame.Sender].
|
||||
func (afrm Frame) Target16() (hardwareAddr *[6]byte, proto *[16]byte) {
|
||||
return (*[6]byte)(afrm.buf[30:36]), (*[16]byte)(afrm.buf[36:52])
|
||||
}
|
||||
|
||||
// ClearHeader zeros out the fixed(non-variable) header contents.
|
||||
func (afrm Frame) ClearHeader() {
|
||||
for i := range afrm.buf[:8] {
|
||||
afrm.buf[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (afrm Frame) Clip() Frame {
|
||||
return Frame{buf: afrm.buf[:sizeHeader+2*int(afrm.hwlen())+2*int(afrm.protolen())]}
|
||||
}
|
||||
|
||||
func (afrm Frame) SwapTargetSender() {
|
||||
hwTarget, protoTarget := afrm.Target()
|
||||
hwSender, protoSender := afrm.Sender()
|
||||
for i := range hwTarget {
|
||||
hwTarget[i], hwSender[i] = hwSender[i], hwTarget[i]
|
||||
}
|
||||
for i := range protoTarget {
|
||||
protoTarget[i], protoSender[i] = protoSender[i], protoTarget[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Validation API
|
||||
//
|
||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
||||
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||
func (afrm Frame) ValidateSize(v *lneto2.Validator) {
|
||||
_, hlen := afrm.Hardware()
|
||||
_, ilen := afrm.Protocol()
|
||||
minLen := 8 + 2*(hlen+ilen)
|
||||
if len(afrm.buf) < int(minLen) {
|
||||
v.AddError(errShortARP)
|
||||
}
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
package arp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto/lneto2"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
ourHWAddr []byte
|
||||
ourProtoAddr []byte
|
||||
htype uint16
|
||||
protoType lneto2.EtherType
|
||||
pending [][sizeHeaderv6]byte
|
||||
queries []queryResult
|
||||
}
|
||||
|
||||
type HandlerConfig struct {
|
||||
HardwareAddr []byte
|
||||
ProtocolAddr []byte
|
||||
MaxQueries int
|
||||
MaxPending int
|
||||
HardwareType uint16
|
||||
ProtocolType lneto2.EtherType
|
||||
}
|
||||
|
||||
func NewHandler(cfg HandlerConfig) (*Handler, error) {
|
||||
if len(cfg.HardwareAddr) == 0 || len(cfg.HardwareAddr) > 255 ||
|
||||
len(cfg.ProtocolAddr) == 0 || len(cfg.ProtocolAddr) > 255 {
|
||||
return nil, errors.New("invalid Handler address config")
|
||||
} else if cfg.MaxQueries <= 0 || cfg.MaxPending <= 0 {
|
||||
return nil, errors.New("invalid Handler query or pending config")
|
||||
}
|
||||
h := &Handler{
|
||||
pending: make([][sizeHeaderv6]byte, 0, cfg.MaxPending),
|
||||
htype: cfg.HardwareType,
|
||||
protoType: cfg.ProtocolType,
|
||||
ourHWAddr: cfg.HardwareAddr,
|
||||
ourProtoAddr: cfg.ProtocolAddr,
|
||||
queries: make([]queryResult, 0, cfg.MaxQueries),
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
type queryResult struct {
|
||||
protoaddr []byte
|
||||
hwaddr []byte
|
||||
querysent bool
|
||||
}
|
||||
|
||||
// ResetState drops pending queries and incoming requests.
|
||||
func (c *Handler) ResetState() {
|
||||
c.pending = c.pending[:0]
|
||||
c.queries = c.queries[:0]
|
||||
}
|
||||
|
||||
func (c *Handler) expectSize() int {
|
||||
return sizeHeader + 2*len(c.ourHWAddr) + 2*len(c.ourProtoAddr)
|
||||
}
|
||||
|
||||
func (c *Handler) QueryResult(protoAddr []byte) (hwAddr []byte, err error) {
|
||||
for i := range c.queries {
|
||||
if bytes.Equal(protoAddr, c.queries[i].protoaddr) {
|
||||
if !c.queries[i].querysent {
|
||||
return nil, errors.New("query not yet sent")
|
||||
} else if len(c.queries[i].hwaddr) == 0 {
|
||||
return nil, errors.New("no response yet")
|
||||
}
|
||||
return c.queries[i].hwaddr, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("query not exist or dropped")
|
||||
}
|
||||
|
||||
func (c *Handler) StartQuery(proto []byte) error {
|
||||
if len(proto) != len(c.ourProtoAddr) {
|
||||
return errors.New("bad protocol address length")
|
||||
} else if len(c.queries) == cap(c.queries) {
|
||||
return errors.New("too many ongoing queries")
|
||||
}
|
||||
c.queries = c.queries[:len(c.queries)+1]
|
||||
q := &c.queries[len(c.queries)-1]
|
||||
q.hwaddr = q.hwaddr[:0]
|
||||
q.querysent = false
|
||||
q.protoaddr = append(q.protoaddr[:0], proto...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Handler) Send(b []byte) (int, error) {
|
||||
n := c.expectSize()
|
||||
if len(b) < n {
|
||||
return 0, errShortARP
|
||||
}
|
||||
if len(c.pending) > 0 {
|
||||
// pop frame.
|
||||
afrm, _ := NewFrame(c.pending[len(c.pending)-1][:])
|
||||
c.pending = c.pending[:len(c.pending)-1]
|
||||
afrm.SetOperation(OpReply)
|
||||
afrm.SwapTargetSender()
|
||||
hwsender, _ := afrm.Sender()
|
||||
copy(hwsender, c.ourHWAddr)
|
||||
n := copy(b, afrm.Clip().RawData())
|
||||
return n, nil
|
||||
}
|
||||
for i := range c.queries {
|
||||
if !c.queries[i].querysent {
|
||||
c.queries[i].querysent = true
|
||||
afrm, _ := NewFrame(b)
|
||||
afrm.SetHardware(c.htype, uint8(len(c.ourHWAddr)))
|
||||
afrm.SetProtocol(c.protoType, uint8(len(c.ourProtoAddr)))
|
||||
afrm.SetOperation(OpRequest)
|
||||
hwSender, protoSender := afrm.Sender()
|
||||
copy(hwSender, c.ourHWAddr)
|
||||
copy(protoSender, c.ourProtoAddr)
|
||||
hwTarget, protoTarget := afrm.Target()
|
||||
copy(protoTarget, c.queries[i].protoaddr)
|
||||
for j := range hwTarget {
|
||||
hwTarget[j] = 0
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (c *Handler) Recv(b []byte) error {
|
||||
if len(c.pending) == cap(c.pending) {
|
||||
return errARPBufferFull
|
||||
}
|
||||
afrm, err := NewFrame(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var vld lneto2.Validator
|
||||
afrm.ValidateSize(&vld)
|
||||
if vld.HasError() {
|
||||
return vld.Err()
|
||||
}
|
||||
htype, hlen := afrm.Hardware()
|
||||
if htype != c.htype || int(hlen) != len(c.ourHWAddr) {
|
||||
return errors.New("bad ARP hardware")
|
||||
}
|
||||
protoType, protoLen := afrm.Protocol()
|
||||
if protoType != c.protoType || int(protoLen) != len(c.ourProtoAddr) {
|
||||
return errors.New("bad ARP proto")
|
||||
}
|
||||
switch afrm.Operation() {
|
||||
case OpRequest:
|
||||
_, protoaddr := afrm.Target()
|
||||
if !bytes.Equal(protoaddr, c.ourProtoAddr) {
|
||||
return nil // Not for us.
|
||||
}
|
||||
c.pending = c.pending[:len(c.pending)+1] // Extend pending buffer.
|
||||
copy(c.pending[len(c.pending)-1][:], afrm.buf) // Set pending buffer.
|
||||
|
||||
case OpReply:
|
||||
hwaddr, protoaddr := afrm.Sender()
|
||||
for i := range c.queries {
|
||||
if len(c.queries[i].hwaddr) == 0 && bytes.Equal(c.queries[i].protoaddr, protoaddr) {
|
||||
c.queries[i].hwaddr = append(c.queries[i].hwaddr[:0], hwaddr...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return errARPUnsupported
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package arp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/soypat/lneto/lneto2"
|
||||
)
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
c1, err := NewHandler(HandlerConfig{
|
||||
HardwareAddr: []byte{0xde, 0xad, 0xbe, 0xef, 0x00, 0x00},
|
||||
ProtocolAddr: []byte{192, 168, 1, 1},
|
||||
MaxQueries: 1,
|
||||
MaxPending: 1,
|
||||
HardwareType: 1,
|
||||
ProtocolType: lneto2.EtherTypeIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c2, err := NewHandler(HandlerConfig{
|
||||
HardwareAddr: []byte{0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee},
|
||||
ProtocolAddr: []byte{192, 168, 1, 2},
|
||||
MaxQueries: 1,
|
||||
MaxPending: 1,
|
||||
HardwareType: 1,
|
||||
ProtocolType: lneto2.EtherTypeIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var buf, discard [64]byte
|
||||
n, err := c1.Send(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal("error on should be nop send:", err)
|
||||
} else if n > 0 {
|
||||
t.Fatal("should not send if no query")
|
||||
}
|
||||
n, err = c2.Send(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal("error on should be nop send:", err)
|
||||
} else if n > 0 {
|
||||
t.Fatal("should not send if no query")
|
||||
}
|
||||
|
||||
// Perform ARP exchange.
|
||||
expectHWAddr := c2.ourHWAddr
|
||||
queryAddr := c2.ourProtoAddr
|
||||
err = c1.StartQuery(queryAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, err = c1.Send(buf[:]) // Send Request.
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n == 0 {
|
||||
t.Fatal("expected send of data after first query")
|
||||
}
|
||||
err = c2.Recv(buf[:n]) // Receive request.
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
n, err = c2.Send(buf[:]) // Send response.
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n == 0 {
|
||||
t.Fatal("got no response to request")
|
||||
}
|
||||
n, err = c2.Send(discard[:]) // Double tap check, should send nothing.
|
||||
if err != nil {
|
||||
t.Fatal("double tap send error:", err)
|
||||
} else if n > 0 {
|
||||
t.Fatal("wanted no data sent after response sent")
|
||||
}
|
||||
|
||||
err = c1.Recv(buf[:]) // Receive response.
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hwaddr, err := c1.QueryResult(queryAddr)
|
||||
if err != nil {
|
||||
log.Fatal("expected query result:", err)
|
||||
} else if !bytes.Equal(hwaddr, expectHWAddr) {
|
||||
log.Fatalf("expected to get hwaddr %x!=%x", hwaddr, expectHWAddr)
|
||||
}
|
||||
n, err = c1.Send(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n > 0 {
|
||||
t.Fatal("expected no data")
|
||||
}
|
||||
n, err = c2.Send(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n > 0 {
|
||||
t.Fatal("expected no data")
|
||||
}
|
||||
}
|
||||
+62
-41
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"log/slog"
|
||||
"math/rand"
|
||||
"net/netip"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/internal"
|
||||
@@ -13,72 +14,92 @@ import (
|
||||
"github.com/soypat/lneto/tcp"
|
||||
)
|
||||
|
||||
const mtu = 2048
|
||||
|
||||
func main() {
|
||||
const mtu = 1500
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var gen ltesto.PacketGen
|
||||
gen.RandomizeAddrs(rng)
|
||||
slogger := logger{slog.Default()}
|
||||
lStack, handler, err := NewEthernetTCPStack(gen.DstMAC, netip.AddrPortFrom(netip.AddrFrom4(gen.DstIPv4), gen.DstTCP), slogger)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
iface := netip.MustParsePrefix("192.168.10.1/24")
|
||||
tap, err := internal.NewTap("tap0", iface)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
const port, iss = 80, 300
|
||||
err = handler.OpenListen(port, iss)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer tap.Close()
|
||||
var buf [mtu]byte
|
||||
for {
|
||||
n, err := tap.Read(buf[:])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
} else if n > 0 {
|
||||
err = lStack.RecvEth(buf[:n])
|
||||
if err != nil {
|
||||
slogger.error("recv", slog.String("err", err.Error()), slog.Int("plen", n))
|
||||
} else {
|
||||
slogger.info("recv", slog.Int("plen", n))
|
||||
}
|
||||
}
|
||||
n, err = lStack.HandleEth(buf[:])
|
||||
if err != nil {
|
||||
slogger.error("handle", slog.String("err", err.Error()))
|
||||
} else if n > 0 {
|
||||
_, err = tap.Write(buf[:n])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
slogger.info("write", slog.Int("plen", n))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewEthernetTCPStack(mac [6]byte, ip netip.AddrPort, slogger logger) (*LinkStack, *tcp.Handler, error) {
|
||||
lStack := LinkStack{
|
||||
logger: slogger,
|
||||
mac: gen.DstMAC,
|
||||
mac: mac,
|
||||
mtu: mtu,
|
||||
}
|
||||
iStack := &IPv4Stack{
|
||||
ip: gen.DstIPv4,
|
||||
ipStack := &IPv4Stack{
|
||||
ip: ip.Addr().As4(),
|
||||
logger: slogger,
|
||||
}
|
||||
tStack := &TCPStack{
|
||||
tcpStack := &TCPStack{
|
||||
logger: slogger,
|
||||
}
|
||||
pStack := &TCPPort{
|
||||
tcpPortStack := &TCPPort{
|
||||
handler: tcp.Handler{},
|
||||
}
|
||||
iss := tcp.Value(100)
|
||||
port := ip.Port()
|
||||
txbuf := make([]byte, mtu)
|
||||
rxbuf := make([]byte, mtu)
|
||||
err := pStack.handler.SetBuffers(txbuf, rxbuf, 3)
|
||||
err := tcpPortStack.handler.SetBuffers(txbuf, rxbuf, 3)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
err = pStack.handler.OpenListen(gen.DstTCP, iss)
|
||||
err = ipStack.Register(tcpStack, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
err = iStack.Register(tStack, &gen.SrcIPv4)
|
||||
err = lStack.Register(ipStack, [6]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
err = lStack.Register(iStack, gen.SrcMAC)
|
||||
err = tcpStack.Register(tcpPortStack, port)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = tStack.Register(pStack, pStack.handler.LocalPort())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
seg := tcp.Segment{
|
||||
SEQ: 300,
|
||||
ACK: iss,
|
||||
DATALEN: 0,
|
||||
WND: 256,
|
||||
Flags: tcp.FlagSYN,
|
||||
}
|
||||
buf := make([]byte, lStack.mtu)
|
||||
packet := gen.AppendRandomIPv4TCPPacket(buf[:0], rng, seg)
|
||||
err = lStack.RecvEth(packet)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("success receiving packet")
|
||||
n, err := lStack.HandleEth(buf)
|
||||
if err != nil {
|
||||
log.Fatal(n, err)
|
||||
} else if n > 0 {
|
||||
log.Println("success sending packet")
|
||||
} else {
|
||||
log.Println("no packet sent")
|
||||
return nil, nil, err
|
||||
}
|
||||
return &lStack, &tcpPortStack.handler, nil
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/netip"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto/internal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
if err != nil {
|
||||
log.Fatalln("failed:", err)
|
||||
}
|
||||
fmt.Println("finished")
|
||||
|
||||
}
|
||||
|
||||
func run() error {
|
||||
ip := netip.MustParsePrefix("192.168.10.1/24")
|
||||
tap, err := internal.NewTap("tap0", ip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tap.Close()
|
||||
var buf [2048]byte
|
||||
pkt := 0
|
||||
for {
|
||||
n, err := tap.Read(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
} else if n == 0 {
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
pkt++
|
||||
fmt.Fprintf(os.Stdout, "rx%d (%d): %q\n\n", pkt, n, buf[:n])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//go:build linux && !baremetal
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Tap struct {
|
||||
fd int
|
||||
name string
|
||||
}
|
||||
|
||||
func NewTap(name string, ip netip.Prefix) (*Tap, error) {
|
||||
if len(name) >= syscall.IFNAMSIZ {
|
||||
return nil, errors.New("name too large")
|
||||
}
|
||||
fd, err := syscall.Open("/dev/net/tun", os.O_RDWR, 0777)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open tun device: %w", err)
|
||||
}
|
||||
|
||||
var ifr [syscall.IFNAMSIZ + 64]byte // extra space for compatibility
|
||||
|
||||
// Set the name; it will be zero-padded automatically.
|
||||
copy(ifr[:syscall.IFNAMSIZ-1], name)
|
||||
|
||||
// Set the flags (starting at offset IFNAMSIZ).
|
||||
flags := uint16(syscall.IFF_TAP | syscall.IFF_NO_PI)
|
||||
*(*uint16)(unsafe.Pointer(&ifr[syscall.IFNAMSIZ])) = flags
|
||||
// Issue the ioctl to create the interface.
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TUNSETIFF), uintptr(unsafe.Pointer(&ifr[0])))
|
||||
if errno != 0 {
|
||||
return nil, fmt.Errorf("creating tap interface: %w", errno)
|
||||
}
|
||||
if ip.IsValid() {
|
||||
// Optionally, bring the interface up and assign an IP address.
|
||||
// You can do this using the 'ip' command for simplicity.
|
||||
err = exec.Command("ip", "link", "set", "dev", name, "up").Run()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set ip link: %w", err)
|
||||
}
|
||||
|
||||
err = exec.Command("ip", "addr", "add", ip.String(), "dev", name).Run()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to assign IP address: %w", err)
|
||||
}
|
||||
}
|
||||
return &Tap{fd: fd, name: name}, nil
|
||||
}
|
||||
|
||||
func (tap *Tap) Read(b []byte) (int, error) {
|
||||
return syscall.Read(tap.fd, b)
|
||||
}
|
||||
|
||||
func (tap *Tap) Write(b []byte) (int, error) {
|
||||
return syscall.Write(tap.fd, b)
|
||||
}
|
||||
|
||||
func (tap *Tap) Close() error {
|
||||
return syscall.Close(tap.fd)
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package lneto2
|
||||
|
||||
//go:generate stringer -type=EtherType,IPProto,ARPOp -linecomment -output stringers.go .
|
||||
|
||||
type EtherType uint16
|
||||
|
||||
// IsSize returns true if the EtherType is actually the size of the payload
|
||||
// and should NOT be interpreted as an EtherType.
|
||||
func (et EtherType) IsSize() bool { return et <= 1500 }
|
||||
|
||||
// Ethernet type flags
|
||||
const (
|
||||
EtherTypeIPv4 EtherType = 0x0800 // IPv4
|
||||
EtherTypeARP EtherType = 0x0806 // ARP
|
||||
EtherTypeWakeOnLAN EtherType = 0x0842 // wake on LAN
|
||||
EtherTypeTRILL EtherType = 0x22F3 // TRILL
|
||||
EtherTypeDECnetPhase4 EtherType = 0x6003 // DECnetPhase4
|
||||
EtherTypeRARP EtherType = 0x8035 // RARP
|
||||
EtherTypeAppleTalk EtherType = 0x809B // AppleTalk
|
||||
EtherTypeAARP EtherType = 0x80F3 // AARP
|
||||
EtherTypeIPX1 EtherType = 0x8137 // IPx1
|
||||
EtherTypeIPX2 EtherType = 0x8138 // IPx2
|
||||
EtherTypeQNXQnet EtherType = 0x8204 // QNXQnet
|
||||
EtherTypeIPv6 EtherType = 0x86DD // IPv6
|
||||
EtherTypeEthernetFlowControl EtherType = 0x8808 // EthernetFlowCtl
|
||||
EtherTypeIEEE802_3 EtherType = 0x8809 // IEEE802.3
|
||||
EtherTypeCobraNet EtherType = 0x8819 // CobraNet
|
||||
EtherTypeMPLSUnicast EtherType = 0x8847 // MPLS Unicast
|
||||
EtherTypeMPLSMulticast EtherType = 0x8848 // MPLS Multicast
|
||||
EtherTypePPPoEDiscovery EtherType = 0x8863 // PPPoE discovery
|
||||
EtherTypePPPoESession EtherType = 0x8864 // PPPoE session
|
||||
EtherTypeJumboFrames EtherType = 0x8870 // jumbo frames
|
||||
EtherTypeHomePlug1_0MME EtherType = 0x887B // home plug 1 0mme
|
||||
EtherTypeIEEE802_1X EtherType = 0x888E // IEEE 802.1x
|
||||
EtherTypePROFINET EtherType = 0x8892 // profinet
|
||||
EtherTypeHyperSCSI EtherType = 0x889A // hyper SCSI
|
||||
EtherTypeAoE EtherType = 0x88A2 // AoE
|
||||
EtherTypeEtherCAT EtherType = 0x88A4 // EtherCAT
|
||||
EtherTypeEthernetPowerlink EtherType = 0x88AB // Ethernet powerlink
|
||||
EtherTypeLLDP EtherType = 0x88CC // LLDP
|
||||
EtherTypeSERCOS3 EtherType = 0x88CD // SERCOS3
|
||||
EtherTypeHomePlugAVMME EtherType = 0x88E1 // home plug AVMME
|
||||
EtherTypeMRP EtherType = 0x88E3 // MRP
|
||||
EtherTypeIEEE802_1AE EtherType = 0x88E5 // IEEE 802.1ae
|
||||
EtherTypeIEEE1588 EtherType = 0x88F7 // IEEE 1588
|
||||
EtherTypeIEEE802_1ag EtherType = 0x8902 // IEEE 802.1ag
|
||||
EtherTypeFCoE EtherType = 0x8906 // FCoE
|
||||
EtherTypeFCoEInit EtherType = 0x8914 // FCoE init
|
||||
EtherTypeRoCE EtherType = 0x8915 // RoCE
|
||||
EtherTypeCTP EtherType = 0x9000 // CTP
|
||||
EtherTypeVeritasLLT EtherType = 0xCAFE // Veritas LLT
|
||||
EtherTypeVLAN EtherType = 0x8100 // VLAN
|
||||
EtherTypeServiceVLAN EtherType = 0x88a8 // service VLAN
|
||||
// minEthPayload is the minimum payload size for an Ethernet frame, assuming
|
||||
// that no 802.1Q VLAN tags are present.
|
||||
minEthPayload = 46
|
||||
)
|
||||
|
||||
// VLANTag holds priority (PCP) Drop indicator (DEI) and VLAN ID bits of the VLAN tag field.
|
||||
type VLANTag uint16
|
||||
|
||||
// DropEligibleIndicator returns true if the DEI bit is set.
|
||||
// DEI may be used separately or in conjunction with PCP to indicate frames eligible to be dropped in the presence of congestion.
|
||||
func (vt VLANTag) DropEligibleIndicator() bool { return vt&(1<<3) != 0 }
|
||||
|
||||
// PriorityCodePoint is 3-bit field which refers to the IEEE 802.1p class of service (CoS) and maps to the frame priority level. Different PCP values can be used to prioritize different classes of traffic
|
||||
func (vt VLANTag) PriorityCodePoint() uint8 { return uint8(vt & 0b111) }
|
||||
|
||||
// VLANIdentifier 12 bit field which specifies which VLAN the frame belongs to. Values of 0 and 4095 are reserved.
|
||||
func (vt VLANTag) VLANIdentifier() uint16 { return uint16(vt) >> 4 }
|
||||
|
||||
// IPToS represents the Traffic Class (a.k.a Type of Service).
|
||||
type IPToS uint8
|
||||
|
||||
// DS returns the top 6 bits of the IPv4 ToS holding the Differentiated Services field
|
||||
// which is used to classify packets.
|
||||
func (tos IPToS) DS() uint8 { return uint8(tos) >> 2 }
|
||||
|
||||
// ECN is the Explicit Congestion Notification which provides congestion control and non-congestion control traffic.
|
||||
func (tos IPToS) ECN() uint8 { return uint8(tos & 0b11) }
|
||||
|
||||
// IPv4Flags holds fragmentation field data of an IPv4 header.
|
||||
type IPv4Flags uint16
|
||||
|
||||
// IsEvil returns true if evil bit set as per [RFC3514].
|
||||
//
|
||||
// [RFC3514]: https://datatracker.ietf.org/doc/html/rfc3514
|
||||
func (f IPv4Flags) IsEvil() bool { return f&2000 != 0 }
|
||||
|
||||
// DontFragment specifies whether the datagram can not be fragmented.
|
||||
// This can be used when sending packets to a host that does not have resources to perform reassembly of fragments.
|
||||
// If the DontFragment(DF) flag is set, and fragmentation is required to route the packet, then the packet is dropped.
|
||||
func (f IPv4Flags) DontFragment() bool { return f&0x4000 != 0 }
|
||||
|
||||
// MoreFragments is cleared for unfragmented packets.
|
||||
// For fragmented packets, all fragments except the last have the MF flag set.
|
||||
// The last fragment has a non-zero Fragment Offset field, so it can still be differentiated from an unfragmented packet.
|
||||
func (f IPv4Flags) MoreFragments() bool { return f&0x8000 != 0 }
|
||||
|
||||
// FragmentOffset specifies the offset of a particular fragment relative to the beginning of the original unfragmented IP datagram.
|
||||
// Fragments are specified in units of 8 bytes, which is why fragment lengths are always a multiple of 8; except the last, which may be smaller.
|
||||
// The fragmentation offset value for the first fragment is always 0.
|
||||
func (f IPv4Flags) FragmentOffset() uint16 { return uint16(f) & 0x1fff }
|
||||
|
||||
const (
|
||||
sizeHeaderIPv4 = 20
|
||||
sizeHeaderTCP = 20
|
||||
sizeHeaderEthNoVLAN = 14
|
||||
sizeHeaderUDP = 8
|
||||
sizeHeaderARPv4 = 28
|
||||
sizeHeaderIPv6 = 40
|
||||
)
|
||||
|
||||
// IPProto represents the IP protocol number.
|
||||
type IPProto uint8
|
||||
|
||||
// IP protocol numbers.
|
||||
const (
|
||||
IPProtoHopByHop IPProto = 0 // IPv6 Hop-by-Hop Option [RFC8200]
|
||||
IPProtoICMP IPProto = 1 // Internet Control Message [RFC792]
|
||||
IPProtoIGMP IPProto = 2 // Internet Group Management [RFC1112]
|
||||
IPProtoGGP IPProto = 3 // Gateway-to-Gateway [RFC823]
|
||||
IPProtoIPv4 IPProto = 4 // IPv4 encapsulation [RFC2003]
|
||||
IPProtoST IPProto = 5 // Stream [RFC1190, RFC1819]
|
||||
IPProtoTCP IPProto = 6 // Transmission Control [RFC9293]
|
||||
IPProtoCBT IPProto = 7 // CBT [Ballardie]
|
||||
IPProtoEGP IPProto = 8 // Exterior Gateway Protocol [RFC888]
|
||||
IPProtoIGP IPProto = 9 // any private interior gateway (used by Cisco for their IGRP)
|
||||
IPProtoBBNRCCMON IPProto = 10 // BBN RCC Monitoring
|
||||
IPProtoNVP IPProto = 11 // Network Voice Protocol [RFC741]
|
||||
IPProtoPUP IPProto = 12 // PUP
|
||||
IPProtoARGUS IPProto = 13 // ARGUS
|
||||
IPProtoEMCON IPProto = 14 // EMCON
|
||||
IPProtoXNET IPProto = 15 // Cross Net Debugger
|
||||
IPProtoCHAOS IPProto = 16 // Chaos
|
||||
IPProtoUDP IPProto = 17 // User Datagram [RFC768]
|
||||
IPProtoMUX IPProto = 18 // Multiplexing
|
||||
IPProtoDCNMEAS IPProto = 19 // DCN Measurement Subsystems
|
||||
IPProtoHMP IPProto = 20 // Host Monitoring [RFC869]
|
||||
IPProtoPRM IPProto = 21 // Packet Radio Measurement
|
||||
IPProtoXNSIDP IPProto = 22 // XEROX NS IDP
|
||||
IPProtoTRUNK1 IPProto = 23 // Trunk-1
|
||||
IPProtoTRUNK2 IPProto = 24 // Trunk-2
|
||||
IPProtoLEAF1 IPProto = 25 // Leaf-1
|
||||
IPProtoLEAF2 IPProto = 26 // Leaf-2
|
||||
IPProtoRDP IPProto = 27 // Reliable Data Protocol [RFC908]
|
||||
IPProtoIRTP IPProto = 28 // Internet Reliable Transaction [RFC938]
|
||||
IPProtoISO_TP4 IPProto = 29 // ISO Transport Protocol Class 4 [RFC905]
|
||||
IPProtoNETBLT IPProto = 30 // Bulk Data Transfer Protocol [RFC998]
|
||||
IPProtoMFE_NSP IPProto = 31 // MFE Network Services Protocol
|
||||
IPProtoMERIT_INP IPProto = 32 // MERIT Internodal Protocol
|
||||
IPProtoDCCP IPProto = 33 // Datagram Congestion Control Protocol [RFC4340]
|
||||
IPProto3PC IPProto = 34 // Third Party Connect Protocol
|
||||
IPProtoIDPR IPProto = 35 // Inter-Domain Policy Routing Protocol
|
||||
IPProtoXTP IPProto = 36 // XTP
|
||||
IPProtoDDP IPProto = 37 // Datagram Delivery Protocol
|
||||
IPProtoIDPRCMTP IPProto = 38 // IDPR Control Message Transport Proto
|
||||
IPProtoTPPLUSPLUS IPProto = 39 // TP++ Transport Protocol
|
||||
IPProtoIL IPProto = 40 // IL Transport Protocol
|
||||
IPProtoIPv6 IPProto = 41 // IPv6 encapsulation [RFC2473]
|
||||
IPProtoSDRP IPProto = 42 // Source Demand Routing Protocol
|
||||
IPProtoIPv6Route IPProto = 43 // Routing Header for IPv6 [RFC8200]
|
||||
IPProtoIPv6Frag IPProto = 44 // Fragment Header for IPv6 [RFC8200]
|
||||
IPProtoIDRP IPProto = 45 // Inter-Domain Routing Protocol
|
||||
IPProtoRSVP IPProto = 46 // Reservation Protocol [RFC2205]
|
||||
IPProtoGRE IPProto = 47 // Generic Routing Encapsulation [RFC2784]
|
||||
IPProtoDSR IPProto = 48 // Dynamic Source Routing Protocol
|
||||
IPProtoBNA IPProto = 49 // BNA
|
||||
IPProtoESP IPProto = 50 // Encap Security Payload [RFC4303]
|
||||
IPProtoAH IPProto = 51 // Authentication Header [RFC4302]
|
||||
IPProtoINLSP IPProto = 52 // Integrated Net Layer Security TUBA
|
||||
IPProtoSWIPE IPProto = 53 // IP with Encryption
|
||||
IPProtoNARP IPProto = 54 // NBMA Address Resolution Protocol
|
||||
IPProtoMOBILE IPProto = 55 // IP Mobility
|
||||
IPProtoTLSP IPProto = 56 // Transport Layer Security Protocol using Kryptonet key management
|
||||
IPProtoSKIP IPProto = 57 // SKIP
|
||||
IPProtoIPv6ICMP IPProto = 58 // ICMP for IPv6 [RFC8200]
|
||||
IPProtoIPv6NoNxt IPProto = 59 // No Next Header for IPv6 [RFC8200]
|
||||
IPProtoIPv6Opts IPProto = 60 // Destination Options for IPv6 [RFC8200]
|
||||
IPProtoCFTP IPProto = 62 // CFTP
|
||||
IPProtoSATEXPAK IPProto = 64 // SATNET and Backroom EXPAK
|
||||
IPProtoKRYPTOLAN IPProto = 65 // Kryptolan
|
||||
IPProtoRVD IPProto = 66 // MIT Remote Virtual Disk Protocol
|
||||
IPProtoIPPC IPProto = 67 // Internet Pluribus Packet Core
|
||||
IPProtoSATMON IPProto = 69 // SATNET Monitoring
|
||||
IPProtoVISA IPProto = 70 // VISA Protocol
|
||||
IPProtoIPCV IPProto = 71 // Internet Packet Core Utility
|
||||
IPProtoCPNX IPProto = 72 // Computer Protocol Network Executive
|
||||
IPProtoCPHB IPProto = 73 // Computer Protocol Heart Beat
|
||||
IPProtoWSN IPProto = 74 // Wang Span Network
|
||||
IPProtoPVP IPProto = 75 // Packet Video Protocol
|
||||
IPProtoBRSATMON IPProto = 76 // Backroom SATNET Monitoring
|
||||
IPProtoSUNND IPProto = 77 // SUN ND PROTOCOL-Temporary
|
||||
IPProtoWBMON IPProto = 78 // WIDEBAND Monitoring
|
||||
IPProtoWBEXPAK IPProto = 79 // WIDEBAND EXPAK
|
||||
IPProtoISOIP IPProto = 80 // ISO Internet Protocol
|
||||
IPProtoVMTP IPProto = 81 // VMTP
|
||||
IPProtoSECUREVMTP IPProto = 82 // SECURE-VMTP
|
||||
IPProtoVINES IPProto = 83 // VINES
|
||||
IPProtoTTP IPProto = 84 // TTP
|
||||
IPProtoNSFNETIGP IPProto = 85 // NSFNET-IGP
|
||||
IPProtoDGP IPProto = 86 // Dissimilar Gateway Protocol
|
||||
IPProtoTCF IPProto = 87 // TCF
|
||||
IPProtoEIGRP IPProto = 88 // EIGRP
|
||||
IPProtoOSPFIGP IPProto = 89 // OSPFIGP
|
||||
IPProtoSpriteRPC IPProto = 90 // Sprite RPC Protocol
|
||||
IPProtoLARP IPProto = 91 // Locus Address Resolution Protocol
|
||||
IPProtoMTP IPProto = 92 // Multicast Transport Protocol
|
||||
IPProtoAX25 IPProto = 93 // AX.25 Frames
|
||||
IPProtoIPIP IPProto = 94 // IP-within-IP Encapsulation Protocol
|
||||
IPProtoMICP IPProto = 95 // Mobile Internetworking Control Pro.
|
||||
IPProtoSCCSP IPProto = 96 // Semaphore Communications Sec. Pro.
|
||||
IPProtoETHERIP IPProto = 97 // Ethernet-within-IP Encapsulation
|
||||
IPProtoENCAP IPProto = 98 // Encapsulation Header
|
||||
IPProtoGMTP IPProto = 100 // GMTP
|
||||
IPProtoIFMP IPProto = 101 // Ipsilon Flow Management Protocol
|
||||
IPProtoPNNI IPProto = 102 // PNNI over IP
|
||||
IPProtoPIM IPProto = 103 // Protocol Independent Multicast
|
||||
IPProtoARIS IPProto = 104 // ARIS
|
||||
IPProtoSCPS IPProto = 105 // SCPS
|
||||
IPProtoQNX IPProto = 106 // QNX
|
||||
IPProtoAN IPProto = 107 // Active Networks
|
||||
IPProtoIPComp IPProto = 108 // IP Payload Compression Protocol
|
||||
IPProtoSNP IPProto = 109 // Sitara Networks Protocol
|
||||
IPProtoCompaqPeer IPProto = 110 // Compaq Peer Protocol
|
||||
IPProtoIPXInIP IPProto = 111 // IPX in IP
|
||||
IPProtoVRRP IPProto = 112 // Virtual Router Redundancy Protocol
|
||||
IPProtoPGM IPProto = 113 // PGM Reliable Transport Protocol
|
||||
IPProtoL2TP IPProto = 115 // Layer Two Tunneling Protocol v3
|
||||
IPProtoDDX IPProto = 116 // D-II Data Exchange (DDX)
|
||||
IPProtoIATP IPProto = 117 // Interactive Agent Transfer Protocol
|
||||
IPProtoSTP IPProto = 118 // Schedule Transfer Protocol
|
||||
IPProtoSRP IPProto = 119 // SpectraLink Radio Protocol
|
||||
IPProtoUTI IPProto = 120 // UTI
|
||||
IPProtoSMP IPProto = 121 // Simple Message Protocol
|
||||
IPProtoSM IPProto = 122 // SM
|
||||
IPProtoPTP IPProto = 123 // Performance Transparency Protocol
|
||||
IPProtoISIS IPProto = 124 // ISIS over IPv4
|
||||
IPProtoFIRE IPProto = 125 // FIRE
|
||||
IPProtoCRTP IPProto = 126 // Combat Radio Transport Protocol
|
||||
IPProtoCRUDP IPProto = 127 // Combat Radio User Datagram
|
||||
IPProtoSSCOPMCE IPProto = 128 // SSCOPMCE
|
||||
IPProtoIPLT IPProto = 129 // IPLT
|
||||
IPProtoSPS IPProto = 130 // Secure Packet Shield
|
||||
IPProtoPIPE IPProto = 131 // Private IP Encapsulation within IP
|
||||
IPProtoSCTP IPProto = 132 // Stream Control Transmission Protocol
|
||||
IPProtoFC IPProto = 133 // Fibre Channel
|
||||
IPProtoRSVP_E2E_IGNORE IPProto = 134 // RSVP-E2E-IGNORE
|
||||
IPProtoMobilityHeader IPProto = 135 // Mobility Header
|
||||
IPProtoUDPLite IPProto = 136 // UDPLite
|
||||
IPProtoMPLSInIP IPProto = 137 // MPLS-in-IP
|
||||
IPProtoMANET IPProto = 138 // MANET Protocols
|
||||
IPProtoHIP IPProto = 139 // Host Identity Protocol
|
||||
IPProtoShim6 IPProto = 140 // Shim6 Protocol
|
||||
IPProtoWESP IPProto = 141 // Wrapped Encapsulating Security Payload
|
||||
IPProtoROHC IPProto = 142 // Robust Header Compression
|
||||
IPProtoEthernet IPProto = 143 // Ethernet
|
||||
IPProtoAGGFRAG IPProto = 144 // AGGFRAG Encapsulation payload for ESP
|
||||
IPProtoNSH IPProto = 145 // Network Service Header
|
||||
)
|
||||
|
||||
// ARPOp represents the type of ARP packet, either request or reply/response.
|
||||
type ARPOp uint8
|
||||
|
||||
const (
|
||||
ARPRequest ARPOp = 1 // request
|
||||
ARPReply ARPOp = 2 // reply
|
||||
)
|
||||
Reference in New Issue
Block a user