remove legacy remnants and move ip,udp,ethernet logic into subpackages

This commit is contained in:
soypat
2025-02-20 16:00:55 -03:00
parent 0606a04f45
commit f86bb6f01a
22 changed files with 590 additions and 1885 deletions
+7 -6
View File
@@ -7,6 +7,7 @@ import (
"net"
"net/netip"
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/lneto2"
)
@@ -49,16 +50,16 @@ func (afrm Frame) SetHardware(Type uint16, length uint8) {
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]))
// Protocol returns the internet protocol type and length. See [ethernet.Type].
func (afrm Frame) Protocol() (Type ethernet.Type, length uint8) {
Type = ethernet.Type(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) {
// SetProtocol sets the protocol type and length fields of the ARP frame. See [Frame.Protocol] and [ethernet.Type].
func (afrm Frame) SetProtocol(Type ethernet.Type, length uint8) {
binary.BigEndian.PutUint16(afrm.buf[2:4], uint16(Type))
afrm.buf[5] = length
}
@@ -150,7 +151,7 @@ func (afrm Frame) String() string {
sndhw, sndpt := afrm.Sender()
tgthw, tgtpt := afrm.Target()
var sndstr, tgtstr string
if ptt == lneto2.EtherTypeIPv4 || ptt == lneto2.EtherTypeIPv6 {
if ptt == ethernet.TypeIPv4 || ptt == ethernet.TypeIPv6 {
sender, _ := netip.AddrFromSlice(sndpt)
target, _ := netip.AddrFromSlice(tgtpt)
sndstr = sender.String()
+3 -2
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/lneto2"
)
@@ -11,7 +12,7 @@ type Handler struct {
ourHWAddr []byte
ourProtoAddr []byte
htype uint16
protoType lneto2.EtherType
protoType ethernet.Type
pending [][sizeHeaderv6]byte
queries []queryResult
}
@@ -22,7 +23,7 @@ type HandlerConfig struct {
MaxQueries int
MaxPending int
HardwareType uint16
ProtocolType lneto2.EtherType
ProtocolType ethernet.Type
}
func NewHandler(cfg HandlerConfig) (*Handler, error) {
+3 -3
View File
@@ -5,7 +5,7 @@ import (
"log"
"testing"
"github.com/soypat/lneto/lneto2"
"github.com/soypat/lneto/ethernet"
)
func TestHandler(t *testing.T) {
@@ -15,7 +15,7 @@ func TestHandler(t *testing.T) {
MaxQueries: 1,
MaxPending: 1,
HardwareType: 1,
ProtocolType: lneto2.EtherTypeIPv4,
ProtocolType: ethernet.TypeIPv4,
})
if err != nil {
t.Fatal(err)
@@ -26,7 +26,7 @@ func TestHandler(t *testing.T) {
MaxQueries: 1,
MaxPending: 1,
HardwareType: 1,
ProtocolType: lneto2.EtherTypeIPv4,
ProtocolType: ethernet.TypeIPv4,
})
if err != nil {
t.Fatal(err)
-84
View File
@@ -1,84 +0,0 @@
package lneto
import (
"encoding/binary"
)
// CRC791 function as defined by RFC 791. The Checksum field for TCP+IP
// is the 16-bit ones' complement of the ones' complement sum of
// all 16-bit words in the header. In case of uneven number of octet the
// last word is LSB padded with zeros.
//
// The zero value of CRC791 is ready to use.
type CRC791 struct {
sum uint32
excedent uint8
needPad bool
}
// Write adds the bytes in p to the running checksum.
func (c *CRC791) Write(buff []byte) (n int, err error) {
if len(buff) == 0 {
return 0, nil
}
if c.needPad {
c.sum += uint32(c.excedent)<<8 + uint32(buff[0])
buff = buff[1:]
c.excedent = 0
c.needPad = false
if len(buff) == 0 {
return 1, nil
}
}
count := len(buff)
for count > 1 {
c.sum += uint32(binary.BigEndian.Uint16(buff[len(buff)-count:]))
count -= 2
}
if count != 0 {
c.excedent = buff[len(buff)-1]
c.needPad = true
}
return len(buff), nil
}
// AddUint32 adds a 32 bit value to the running checksum interpreted as BigEndian (network order).
func (c *CRC791) AddUint32(value uint32) {
c.AddUint16(uint16(value >> 16))
c.AddUint16(uint16(value))
}
// Add16 adds a 16 bit value to the running checksum interpreted as BigEndian (network order).
func (c *CRC791) AddUint16(value uint16) {
if c.needPad {
c.sum += uint32(c.excedent)<<8 | uint32(value>>8)
c.excedent = byte(value)
} else {
c.sum += uint32(value)
}
}
// Add16 adds value to the running checksum interpreted as BigEndian (network order).
func (c *CRC791) AddUint8(value uint8) {
if c.needPad {
c.sum += uint32(c.excedent)<<8 | uint32(value)
} else {
c.excedent = value
}
c.needPad = !c.needPad
}
// Sum16 calculates the checksum with the data written to c thus far.
func (c *CRC791) Sum16() uint16 {
sum := c.sum
if c.needPad {
sum += uint32(c.excedent) << 8
}
for sum>>16 != 0 {
sum = (sum & 0xffff) + (sum >> 16)
}
return uint16(^sum)
}
// Reset zeros out the CRC791, resetting it to the initial state.
func (c *CRC791) Reset() { *c = CRC791{} }
-268
View File
@@ -1,268 +0,0 @@
package lneto
//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
)
+74
View File
@@ -0,0 +1,74 @@
package ethernet
const (
sizeHeaderNoVLAN = 14
)
//go:generate stringer -type=Type -linecomment -output stringers.go .
type Type uint16
// IsSize returns true if the EtherType is actually the size of the payload
// and should NOT be interpreted as an EtherType.
func (et Type) IsSize() bool { return et <= 1500 }
// Ethernet type flags
const (
TypeIPv4 Type = 0x0800 // IPv4
TypeARP Type = 0x0806 // ARP
TypeWakeOnLAN Type = 0x0842 // wake on LAN
TypeTRILL Type = 0x22F3 // TRILL
TypeDECnetPhase4 Type = 0x6003 // DECnetPhase4
TypeRARP Type = 0x8035 // RARP
TypeAppleTalk Type = 0x809B // AppleTalk
TypeAARP Type = 0x80F3 // AARP
TypeIPX1 Type = 0x8137 // IPx1
TypeIPX2 Type = 0x8138 // IPx2
TypeQNXQnet Type = 0x8204 // QNXQnet
TypeIPv6 Type = 0x86DD // IPv6
TypeEthernetFlowControl Type = 0x8808 // EthernetFlowCtl
TypeIEEE802_3 Type = 0x8809 // IEEE802.3
TypeCobraNet Type = 0x8819 // CobraNet
TypeMPLSUnicast Type = 0x8847 // MPLS Unicast
TypeMPLSMulticast Type = 0x8848 // MPLS Multicast
TypePPPoEDiscovery Type = 0x8863 // PPPoE discovery
TypePPPoESession Type = 0x8864 // PPPoE session
TypeJumboFrames Type = 0x8870 // jumbo frames
TypeHomePlug1_0MME Type = 0x887B // home plug 1 0mme
TypeIEEE802_1X Type = 0x888E // IEEE 802.1x
TypePROFINET Type = 0x8892 // profinet
TypeHyperSCSI Type = 0x889A // hyper SCSI
TypeAoE Type = 0x88A2 // AoE
TypeEtherCAT Type = 0x88A4 // EtherCAT
TypeEthernetPowerlink Type = 0x88AB // Ethernet powerlink
TypeLLDP Type = 0x88CC // LLDP
TypeSERCOS3 Type = 0x88CD // SERCOS3
TypeHomePlugAVMME Type = 0x88E1 // home plug AVMME
TypeMRP Type = 0x88E3 // MRP
TypeIEEE802_1AE Type = 0x88E5 // IEEE 802.1ae
TypeIEEE1588 Type = 0x88F7 // IEEE 1588
TypeIEEE802_1ag Type = 0x8902 // IEEE 802.1ag
TypeFCoE Type = 0x8906 // FCoE
TypeFCoEInit Type = 0x8914 // FCoE init
TypeRoCE Type = 0x8915 // RoCE
TypeCTP Type = 0x9000 // CTP
TypeVeritasLLT Type = 0xCAFE // Veritas LLT
TypeVLAN Type = 0x8100 // VLAN
TypeServiceVLAN Type = 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 }
+131
View File
@@ -0,0 +1,131 @@
package ethernet
import (
"encoding/binary"
"errors"
"github.com/soypat/lneto/lneto2"
)
// NewFrame returns a EthFrame with data set to buf.
// An error is returned if the buffer size is smaller than 14.
// Users should still call [EthFrame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewFrame(buf []byte) (Frame, error) {
if len(buf) < sizeHeaderNoVLAN {
return Frame{buf: nil}, errShort
}
return Frame{buf: buf}, nil
}
// Frame encapsulates the raw data of an Ethernet frame
// without including preamble (first byte is start of destination address)
// and provides methods for manipulating, validating and
// retrieving fields and payload data. See [IEEE 802.3].
//
// [IEEE 802.3]: https://standards.ieee.org/ieee/802.3/7071/
type Frame struct {
buf []byte
}
// RawData returns the underlying slice with which the frame was created.
func (efrm Frame) RawData() []byte { return efrm.buf }
// HeaderLength returns the length of the ethernet packet header. Nominally returns 14; or 18 for VLAN packets.
func (efrm Frame) HeaderLength() int {
if efrm.IsVLAN() {
return 18
}
return sizeHeaderNoVLAN
}
// Payload returns the data portion of the ethernet packet with handling of VLAN packets.
func (efrm Frame) Payload() []byte {
hl := efrm.HeaderLength()
et := efrm.EtherTypeOrSize()
if et.IsSize() {
return efrm.buf[hl:et]
}
return efrm.buf[hl:]
}
// DestinationHardwareAddr returns the target's MAC/hardware address for the ethernet packet.
func (efrm Frame) DestinationHardwareAddr() (dst *[6]byte) {
return (*[6]byte)(efrm.buf[0:6])
}
// IsBroadcast returns true if the destination is the broadcast address ff:ff:ff:ff:ff:ff, false otherwise.
func (efrm Frame) IsBroadcast() bool {
return efrm.buf[0] == 0xff && efrm.buf[1] == 0xff && efrm.buf[2] == 0xff &&
efrm.buf[3] == 0xff && efrm.buf[4] == 0xff && efrm.buf[5] == 0xff
}
// SourceHardwareAddr returns the sender's MAC/hardware address of the ethernet packet.
func (efrm Frame) SourceHardwareAddr() (src *[6]byte) {
return (*[6]byte)(efrm.buf[6:12])
}
// EtherTypeOrSize returns the EtherType/Size field of the ethernet packet.
// Caller should check if the field is actually a valid EtherType or if it represents the Ethernet payload size with [EtherType.IsSize].
func (efrm Frame) EtherTypeOrSize() Type {
return Type(binary.BigEndian.Uint16(efrm.buf[12:14]))
}
// SetEtherType sets the EtherType field of the ethernet packet. See [EtherType] and [Frame.EtherTypeOrSize].
func (efrm Frame) SetEtherType(v Type) {
binary.BigEndian.PutUint16(efrm.buf[12:14], uint16(v))
}
// VLANTag returns the VLAN tag field following the TPID=0x8100. See [VLANTag]. Call [Frame.ValidateSize] to ensure this function does not panic.
func (efrm Frame) VLANTag() VLANTag { return VLANTag(binary.BigEndian.Uint16(efrm.buf[14:16])) }
// SetVLANTag sets the VLAN tag field of the Ethernet Header. See [VLANTag]. Call [Frame.ValidateSize] to ensure this function does not panic.
func (efrm Frame) SetVLANTag(vt VLANTag) { binary.BigEndian.PutUint16(efrm.buf[14:16], uint16(vt)) }
// VLANEtherType returns the [EtherType] for a VLAN ethernet frame (octet position 16). Call [Frame.ValidateSize] to ensure this function does not panic.
func (efrm Frame) VLANEtherType() Type {
return Type(binary.BigEndian.Uint16(efrm.buf[16:18]))
}
// SetVLANEtherType sets the [EtherType] for a VLAN ethernet frame (octet position 16). Call [Frame.ValidateSize] to ensure this function does not panic.
func (efrm Frame) SetVLANEtherType(vt Type) {
binary.BigEndian.PutUint16(efrm.buf[16:18], uint16(vt))
}
// IsVLAN returns true if the SizeOrEtherType is set to the VLAN tag 0x8100. This
// indicates the EthernetHeader is invalid as-is and instead of EtherType the field
// contains the first two octets of a 4 octet 802.1Q VLAN tag. In this case 4 more bytes
// must be read from the wire, of which the last 2 of these bytes contain the actual
// SizeOrEtherType field, which needs to be validated yet again in case the packet is
// a VLAN double-tap packet.
func (efrm Frame) IsVLAN() bool {
return efrm.EtherTypeOrSize() == TypeVLAN
}
// ClearHeader zeros out the fixed(non-variable) header contents.
func (frm Frame) ClearHeader() {
for i := range frm.buf[:sizeHeaderNoVLAN] {
frm.buf[i] = 0
}
}
//
// Validation API.
//
var (
errShort = errors.New("ethernet: too short")
errShortVLAN = errors.New("ethernet: short VLAN")
)
// 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 (efrm Frame) ValidateSize(v *lneto2.Validator) {
sz := efrm.EtherTypeOrSize()
if sz.IsSize() && len(efrm.buf) < int(sz) {
v.AddError(errShort)
}
if sz == TypeVLAN && len(efrm.buf) < 18 {
v.AddError(errShortVLAN)
}
}
+105
View File
@@ -0,0 +1,105 @@
// Code generated by "stringer -type=Type -linecomment -output stringers.go ."; DO NOT EDIT.
package ethernet
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[TypeIPv4-2048]
_ = x[TypeARP-2054]
_ = x[TypeWakeOnLAN-2114]
_ = x[TypeTRILL-8947]
_ = x[TypeDECnetPhase4-24579]
_ = x[TypeRARP-32821]
_ = x[TypeAppleTalk-32923]
_ = x[TypeAARP-33011]
_ = x[TypeIPX1-33079]
_ = x[TypeIPX2-33080]
_ = x[TypeQNXQnet-33284]
_ = x[TypeIPv6-34525]
_ = x[TypeEthernetFlowControl-34824]
_ = x[TypeIEEE802_3-34825]
_ = x[TypeCobraNet-34841]
_ = x[TypeMPLSUnicast-34887]
_ = x[TypeMPLSMulticast-34888]
_ = x[TypePPPoEDiscovery-34915]
_ = x[TypePPPoESession-34916]
_ = x[TypeJumboFrames-34928]
_ = x[TypeHomePlug1_0MME-34939]
_ = x[TypeIEEE802_1X-34958]
_ = x[TypePROFINET-34962]
_ = x[TypeHyperSCSI-34970]
_ = x[TypeAoE-34978]
_ = x[TypeEtherCAT-34980]
_ = x[TypeEthernetPowerlink-34987]
_ = x[TypeLLDP-35020]
_ = x[TypeSERCOS3-35021]
_ = x[TypeHomePlugAVMME-35041]
_ = x[TypeMRP-35043]
_ = x[TypeIEEE802_1AE-35045]
_ = x[TypeIEEE1588-35063]
_ = x[TypeIEEE802_1ag-35074]
_ = x[TypeFCoE-35078]
_ = x[TypeFCoEInit-35092]
_ = x[TypeRoCE-35093]
_ = x[TypeCTP-36864]
_ = x[TypeVeritasLLT-51966]
_ = x[TypeVLAN-33024]
_ = x[TypeServiceVLAN-34984]
}
const _Type_name = "IPv4ARPwake on LANTRILLDECnetPhase4RARPAppleTalkAARPVLANIPx1IPx2QNXQnetIPv6EthernetFlowCtlIEEE802.3CobraNetMPLS UnicastMPLS MulticastPPPoE discoveryPPPoE sessionjumbo frameshome plug 1 0mmeIEEE 802.1xprofinethyper SCSIAoEEtherCATservice VLANEthernet powerlinkLLDPSERCOS3home plug AVMMEMRPIEEE 802.1aeIEEE 1588IEEE 802.1agFCoEFCoE initRoCECTPVeritas LLT"
var _Type_map = map[Type]string{
2048: _Type_name[0:4],
2054: _Type_name[4:7],
2114: _Type_name[7:18],
8947: _Type_name[18:23],
24579: _Type_name[23:35],
32821: _Type_name[35:39],
32923: _Type_name[39:48],
33011: _Type_name[48:52],
33024: _Type_name[52:56],
33079: _Type_name[56:60],
33080: _Type_name[60:64],
33284: _Type_name[64:71],
34525: _Type_name[71:75],
34824: _Type_name[75:90],
34825: _Type_name[90:99],
34841: _Type_name[99:107],
34887: _Type_name[107:119],
34888: _Type_name[119:133],
34915: _Type_name[133:148],
34916: _Type_name[148:161],
34928: _Type_name[161:173],
34939: _Type_name[173:189],
34958: _Type_name[189:200],
34962: _Type_name[200:208],
34970: _Type_name[208:218],
34978: _Type_name[218:221],
34980: _Type_name[221:229],
34984: _Type_name[229:241],
34987: _Type_name[241:259],
35020: _Type_name[259:263],
35021: _Type_name[263:270],
35041: _Type_name[270:285],
35043: _Type_name[285:288],
35045: _Type_name[288:300],
35063: _Type_name[300:309],
35074: _Type_name[309:321],
35078: _Type_name[321:325],
35092: _Type_name[325:334],
35093: _Type_name[334:338],
36864: _Type_name[338:341],
51966: _Type_name[341:352],
}
func (i Type) String() string {
if str, ok := _Type_map[i]; ok {
return str
}
return "Type(" + strconv.FormatInt(int64(i), 10) + ")"
}
+39 -31
View File
@@ -9,9 +9,11 @@ import (
"net"
"net/netip"
"github.com/soypat/lneto"
"github.com/soypat/lneto/arp"
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/internal"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/ipv6"
"github.com/soypat/lneto/lneto2"
"github.com/soypat/lneto/tcp"
)
@@ -92,9 +94,9 @@ func NewEthernetTCPStack(mac [6]byte, ip netip.AddrPort, slogger logger) (*LinkS
tcpPortStack := &TCPPort{
handler: tcp.Handler{},
}
proto := lneto2.EtherTypeIPv4
proto := ethernet.TypeIPv4
if ip.Addr().Is6() {
proto = lneto2.EtherTypeIPv6
proto = ethernet.TypeIPv6
}
arphandler, err := arp.NewHandler(arp.HandlerConfig{
HardwareAddr: mac[:],
@@ -177,7 +179,8 @@ func (ls *LinkStack) Register(h Handler, remoteHWAddr [6]byte) error {
}
func (ls *LinkStack) RecvEth(ethFrame []byte) (err error) {
efrm, err := lneto.NewEthFrame(ethFrame)
efrm, err := ethernet.NewFrame(ethFrame)
if err != nil {
return err
}
@@ -186,7 +189,7 @@ func (ls *LinkStack) RecvEth(ethFrame []byte) (err error) {
if !efrm.IsBroadcast() && ls.mac != *dstaddr {
return fmt.Errorf("incoming %s mismatch hwaddr %s", etype.String(), net.HardwareAddr(dstaddr[:]).String())
}
var vld lneto.Validator
var vld lneto2.Validator
efrm.ValidateSize(&vld)
if err := vld.Err(); err != nil {
return err
@@ -210,15 +213,15 @@ func (ls *LinkStack) HandleEth(dst []byte) (n int, err error) {
h := &ls.handlers[i]
n, err = h.handle(dst[:ls.mtu], 14)
if err != nil {
ls.error("handling", slog.String("proto", lneto.EtherType(h.proto).String()), slog.String("err", err.Error()))
ls.error("handling", slog.String("proto", ethernet.Type(h.proto).String()), slog.String("err", err.Error()))
continue
}
if n > 0 {
// Found packet
efrm, _ := lneto.NewEthFrame(dst[:14])
efrm, _ := ethernet.NewFrame(dst[:14])
copy(efrm.DestinationHardwareAddr()[:], h.raddr)
*efrm.SourceHardwareAddr() = ls.mac
efrm.SetEtherType(lneto.EtherType(h.proto))
efrm.SetEtherType(ethernet.Type(h.proto))
return n + 14, nil
}
@@ -228,12 +231,12 @@ func (ls *LinkStack) HandleEth(dst []byte) (n int, err error) {
type IPv4Stack struct {
ip [4]byte
validator lneto.Validator
validator lneto2.Validator
handlers []handler
logger
}
func (is *IPv4Stack) Protocol() uint32 { return uint32(lneto.EtherTypeIPv4) }
func (is *IPv4Stack) Protocol() uint32 { return uint32(ethernet.TypeIPv4) }
func (is *IPv4Stack) Register(h Handler, remoteAddr *[4]byte) error {
proto := h.Protocol()
@@ -256,7 +259,8 @@ func (is *IPv4Stack) Register(h Handler, remoteAddr *[4]byte) error {
}
func (is *IPv4Stack) Recv(ethFrame []byte, ipOff int) error {
ifrm, err := lneto.NewIPv4Frame(ethFrame[ipOff:])
ifrm, err := ipv4.NewFrame(ethFrame[ipOff:])
if err != nil {
return err
}
@@ -289,7 +293,7 @@ func (is *IPv4Stack) Handle(ethFrame []byte, ipOff int) (int, error) {
if len(ethFrame)-ipOff < 256 {
return 0, io.ErrShortBuffer
}
ifrm, _ := lneto.NewIPv4Frame(ethFrame[ipOff:])
ifrm, _ := ipv4.NewFrame(ethFrame[ipOff:])
const ihl = 5
const headerlen = ihl * 4
ifrm.SetVersionAndIHL(4, 5)
@@ -297,7 +301,7 @@ func (is *IPv4Stack) Handle(ethFrame []byte, ipOff int) (int, error) {
ifrm.SetToS(0)
for i := range is.handlers {
h := &is.handlers[i]
proto := lneto.IPProto(h.proto)
proto := lneto2.IPProto(h.proto)
ifrm.SetProtocol(proto)
if len(h.raddr) == 4 {
copy(ifrm.DestinationAddr()[:], h.raddr)
@@ -325,12 +329,13 @@ func (is *IPv4Stack) Handle(ethFrame []byte, ipOff int) (int, error) {
}
type TCPStack struct {
validator lneto.Validator
validator lneto2.Validator
handlers []handler
logger
crc lneto2.CRC791
}
func (ts *TCPStack) Protocol() uint32 { return uint32(lneto.IPProtoTCP) }
func (ts *TCPStack) Protocol() uint32 { return uint32(lneto2.IPProtoTCP) }
func (ts *TCPStack) Register(h Handler, lport uint16) error {
if lport == 0 {
@@ -349,7 +354,7 @@ func (ts *TCPStack) Recv(ipFrame []byte, tcpOff int) error {
if ipVersion != 4 && ipVersion != 6 {
return errors.New("invalid IP version")
}
tfrm, err := lneto.NewTCPFrame(ipFrame[tcpOff:])
tfrm, err := tcp.NewFrame(ipFrame[tcpOff:])
if err != nil {
return err
}
@@ -369,17 +374,19 @@ func (ts *TCPStack) Recv(ipFrame []byte, tcpOff int) error {
if err = ts.validator.Err(); err != nil {
return err
}
var crc uint16
ts.crc.Reset()
switch ipVersion {
case 4:
ifrm, _ := lneto.NewIPv4Frame(ipFrame)
crc = tfrm.CalculateIPv4CRC(ifrm)
ifrm, _ := ipv4.NewFrame(ipFrame)
ifrm.CRCWriteTCPPseudo(&ts.crc)
case 6:
ifrm, _ := lneto.NewIPv6Frame(ipFrame)
crc = tfrm.CalculateIPv6CRC(ifrm)
i6frm, _ := ipv6.NewFrame(ipFrame)
i6frm.CRCWritePseudo(&ts.crc)
}
tfrm.CRCWrite(&ts.crc)
crc := ts.crc.Sum16()
gotCRC := tfrm.CRC()
if crc != gotCRC {
if ts.crc.Sum16() != gotCRC {
ts.error("TCPStack:Recv:crc-mismatch", slog.Uint64("lport", uint64(lport)), slog.Uint64("want", uint64(crc)), slog.Uint64("got", uint64(gotCRC)))
return errors.New("TCP crc mismatch")
}
@@ -412,21 +419,22 @@ func (ts *TCPStack) Handle(ipFrame []byte, tcpOff int) (n int, err error) {
return 0, err
}
// TCP packet written.
tfrm, _ := lneto.NewTCPFrame(ipFrame[tcpOff : tcpOff+n])
tfrm, _ := tcp.NewFrame(ipFrame[tcpOff : tcpOff+n])
ts.validator.ResetErr()
tfrm.ValidateSize(&ts.validator) // Perform basic validation.
if err = ts.validator.Err(); err != nil {
return 0, err
}
var crc uint16
ts.crc.Reset()
switch ipVersion {
case 4:
ifrm, _ := lneto.NewIPv4Frame(ipFrame)
crc = tfrm.CalculateIPv4CRC(ifrm)
ifrm, _ := ipv4.NewFrame(ipFrame)
ifrm.CRCWriteTCPPseudo(&ts.crc)
case 6:
ifrm, _ := lneto.NewIPv6Frame(ipFrame)
crc = tfrm.CalculateIPv6CRC(ifrm)
i6frm, _ := ipv6.NewFrame(ipFrame)
i6frm.CRCWritePseudo(&ts.crc)
}
crc := ts.crc.Sum16()
tfrm.SetCRC(crc)
return n, nil
}
@@ -435,7 +443,7 @@ type ARPStack struct {
handler arp.Handler
}
func (as *ARPStack) Protocol() uint32 { return uint32(lneto.EtherTypeARP) }
func (as *ARPStack) Protocol() uint32 { return uint32(ethernet.TypeARP) }
func (as *ARPStack) Recv(EtherFrame []byte, arpOff int) error {
afrm, _ := arp.NewFrame(EtherFrame[arpOff:])
@@ -450,7 +458,7 @@ func (as *ARPStack) Handle(EtherFrame []byte, arpOff int) (int, error) {
}
afrm, _ := arp.NewFrame(EtherFrame[arpOff:])
hwaddr, _ := afrm.Target()
efrm, _ := lneto.NewEthFrame(EtherFrame)
efrm, _ := ethernet.NewFrame(EtherFrame)
copy(efrm.DestinationHardwareAddr()[:], hwaddr)
slog.Info("handle", slog.String("out", afrm.String()))
return n, err
@@ -460,7 +468,7 @@ type TCPPort struct {
handler tcp.Handler
}
func (tp *TCPPort) Protocol() uint32 { return uint32(lneto.IPProtoTCP) }
func (tp *TCPPort) Protocol() uint32 { return uint32(lneto2.IPProtoTCP) }
func (tp *TCPPort) Recv(tcpFrame []byte, off int) error {
if off != 0 {
-765
View File
@@ -1,765 +0,0 @@
package lneto
import (
"encoding/binary"
"errors"
"fmt"
"math"
"github.com/soypat/lneto/tcp"
)
// NewEthFrame returns a EthFrame with data set to buf.
// An error is returned if the buffer size is smaller than 14.
// Users should still call [EthFrame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewEthFrame(buf []byte) (EthFrame, error) {
if len(buf) < sizeHeaderEthNoVLAN {
return EthFrame{buf: nil}, errors.New("ethernet packet too short")
}
return EthFrame{buf: buf}, nil
}
// 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 NewARPFrame(buf []byte) (ARPFrame, error) {
if len(buf) < sizeHeaderARPv4 {
return ARPFrame{buf: nil}, errors.New("ARP packet too short")
}
return ARPFrame{buf: buf}, nil
}
// NewIPv4Frame returns a new IPv4Frame with data set to buf.
// An error is returned if the buffer size is smaller than 20.
// Users should still call [IPv4Frame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewIPv4Frame(buf []byte) (IPv4Frame, error) {
if len(buf) < sizeHeaderIPv4 {
return IPv4Frame{buf: nil}, errors.New("IPv4 packet too short")
}
return IPv4Frame{buf: buf}, nil
}
// NewIPv6Frame returns a new IPv6Frame with data set to buf.
// An error is returned if the buffer size is smaller than 40.
// Users should still call [IPv6Frame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewIPv6Frame(buf []byte) (IPv6Frame, error) {
if len(buf) < sizeHeaderIPv6 {
return IPv6Frame{buf: nil}, errors.New("IPv6 packet too short")
}
return IPv6Frame{buf: buf}, nil
}
// NewTCPFrame returns a new TCPFrame with data set to buf.
// An error is returned if the buffer size is smaller than 20.
// Users should still call [TCPFrame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewTCPFrame(buf []byte) (TCPFrame, error) {
if len(buf) < sizeHeaderTCP {
return TCPFrame{buf: nil}, errors.New("TCP packet too short")
}
return TCPFrame{buf: buf}, nil
}
// NewUDPFrame returns a new UDPFrame with data set to buf.
// An error is returned if the buffer size is smaller than 8.
// Users should still call [UDPFrame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewUDPFrame(buf []byte) (UDPFrame, error) {
if len(buf) < sizeHeaderUDP {
return UDPFrame{buf: buf}, errors.New("UDP packet too short")
}
return UDPFrame{buf: buf}, nil
}
// EthFrame encapsulates the raw data of an Ethernet frame
// without including preamble (first byte is start of destination address)
// and provides methods for manipulating, validating and
// retrieving fields and payload data. See [IEEE 802.3].
//
// [IEEE 802.3]: https://standards.ieee.org/ieee/802.3/7071/
type EthFrame struct {
buf []byte
}
// RawData returns the underlying slice with which the frame was created.
func (efrm EthFrame) RawData() []byte { return efrm.buf }
// HeaderLength returns the length of the ethernet packet header. Nominally returns 14; or 18 for VLAN packets.
func (efrm EthFrame) HeaderLength() int {
if efrm.IsVLAN() {
return 18
}
return sizeHeaderEthNoVLAN
}
// Payload returns the data portion of the ethernet packet with handling of VLAN packets.
func (efrm EthFrame) Payload() []byte {
hl := efrm.HeaderLength()
et := efrm.EtherTypeOrSize()
if et.IsSize() {
return efrm.buf[hl:et]
}
return efrm.buf[hl:]
}
// DestinationHardwareAddr returns the target's MAC/hardware address for the ethernet packet.
func (efrm EthFrame) DestinationHardwareAddr() (dst *[6]byte) {
return (*[6]byte)(efrm.buf[0:6])
}
// IsBroadcast returns true if the destination is the broadcast address ff:ff:ff:ff:ff:ff, false otherwise.
func (efrm EthFrame) IsBroadcast() bool {
return efrm.buf[0] == 0xff && efrm.buf[1] == 0xff && efrm.buf[2] == 0xff &&
efrm.buf[3] == 0xff && efrm.buf[4] == 0xff && efrm.buf[5] == 0xff
}
// SourceHardwareAddr returns the sender's MAC/hardware address of the ethernet packet.
func (efrm EthFrame) SourceHardwareAddr() (src *[6]byte) {
return (*[6]byte)(efrm.buf[6:12])
}
// EtherTypeOrSize returns the EtherType/Size field of the ethernet packet.
// Caller should check if the field is actually a valid EtherType or if it represents the Ethernet payload size with [EtherType.IsSize].
func (efrm EthFrame) EtherTypeOrSize() EtherType {
return EtherType(binary.BigEndian.Uint16(efrm.buf[12:14]))
}
// SetEtherType sets the EtherType field of the ethernet packet. See [EtherType] and [EthFrame.EtherTypeOrSize].
func (efrm EthFrame) SetEtherType(v EtherType) {
binary.BigEndian.PutUint16(efrm.buf[12:14], uint16(v))
}
// VLANTag returns the VLAN tag field following the TPID=0x8100. See [VLANTag]. Call [EthFrame.ValidateSize] to ensure this function does not panic.
func (efrm EthFrame) VLANTag() VLANTag { return VLANTag(binary.BigEndian.Uint16(efrm.buf[14:16])) }
// SetVLANTag sets the VLAN tag field of the Ethernet Header. See [VLANTag]. Call [EthFrame.ValidateSize] to ensure this function does not panic.
func (efrm EthFrame) SetVLANTag(vt VLANTag) { binary.BigEndian.PutUint16(efrm.buf[14:16], uint16(vt)) }
// VLANEtherType returns the [EtherType] for a VLAN ethernet frame (octet position 16). Call [EthFrame.ValidateSize] to ensure this function does not panic.
func (efrm EthFrame) VLANEtherType() EtherType {
return EtherType(binary.BigEndian.Uint16(efrm.buf[16:18]))
}
// SetVLANEtherType sets the [EtherType] for a VLAN ethernet frame (octet position 16). Call [EthFrame.ValidateSize] to ensure this function does not panic.
func (efrm EthFrame) SetVLANEtherType(vt EtherType) {
binary.BigEndian.PutUint16(efrm.buf[16:18], uint16(vt))
}
// IsVLAN returns true if the SizeOrEtherType is set to the VLAN tag 0x8100. This
// indicates the EthernetHeader is invalid as-is and instead of EtherType the field
// contains the first two octets of a 4 octet 802.1Q VLAN tag. In this case 4 more bytes
// must be read from the wire, of which the last 2 of these bytes contain the actual
// SizeOrEtherType field, which needs to be validated yet again in case the packet is
// a VLAN double-tap packet.
func (efrm EthFrame) IsVLAN() bool {
return efrm.EtherTypeOrSize() == EtherTypeVLAN
}
// ClearHeader zeros out the fixed(non-variable) header contents.
func (frm EthFrame) ClearHeader() {
for i := range frm.buf[:sizeHeaderEthNoVLAN] {
frm.buf[i] = 0
}
}
// ARPFrame 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 ARPFrame struct {
buf []byte
}
// RawData returns the underlying slice with which the frame was created.
func (afrm ARPFrame) RawData() []byte { return afrm.buf }
// HardwareType specifies the network link protocol type. Example: Ethernet is 1.
func (afrm ARPFrame) Hardware() (Type uint16, length uint8) {
Type = binary.BigEndian.Uint16(afrm.buf[0:2])
length = afrm.buf[4]
return Type, length
}
// SetHardware sets the networl link protocol type. See [ARPFrame.SetHardware].
func (afrm ARPFrame) 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 [EtherType].
func (afrm ARPFrame) Protocol() (Type EtherType, length uint8) {
Type = EtherType(binary.BigEndian.Uint16(afrm.buf[2:4]))
length = afrm.buf[5]
return Type, length
}
// SetProtocol sets the protocol type and length fields of the ARP frame. See [ARPFrame.Protocol] and [EtherType].
func (afrm ARPFrame) SetProtocol(Type EtherType, length uint8) {
binary.BigEndian.PutUint16(afrm.buf[2:4], uint16(Type))
afrm.buf[5] = length
}
// Operation returns the ARP header operation field. See [ARPOp].
func (afrm ARPFrame) Operation() ARPOp { return ARPOp(afrm.buf[6]) }
// SetOperation sets the ARP header operation field. See [ARPOp].
func (afrm ARPFrame) SetOperation(b ARPOp) { 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 ARPFrame) 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 ARPFrame) 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 [ARPFrame.Sender].
func (afrm ARPFrame) 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 [ARPFrame.Sender].
func (afrm ARPFrame) 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 [ARPFrame.Sender].
func (afrm ARPFrame) 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 [ARPFrame.Sender].
func (afrm ARPFrame) 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 (frm ARPFrame) ClearHeader() {
for i := range frm.buf[:8] {
frm.buf[i] = 0
}
}
// IPv4Frame encapsulates the raw data of an IPv4 packet
// and provides methods for manipulating, validating and
// retreiving fields and payload data. See [RFC791].
//
// [RFC791]: https://tools.ietf.org/html/rfc791
type IPv4Frame struct {
buf []byte
}
// RawData returns the underlying slice with which the frame was created.
func (ifrm IPv4Frame) RawData() []byte { return ifrm.buf }
// HeaderLength returns the length of the IPv4 header as calculated using IHL. It includes IP options.
func (ifrm IPv4Frame) HeaderLength() int {
return int(ifrm.ihl()) * 4
}
func (ifrm IPv4Frame) ihl() uint8 { return ifrm.buf[0] & 0xf }
func (ifrm IPv4Frame) version() uint8 { return ifrm.buf[0] >> 4 }
// VersionAndIHL returns the version and IHL fields in the IPv4 header. Version should always be 4.
func (ifrm IPv4Frame) VersionAndIHL() (version, IHL uint8) {
v := ifrm.buf[0]
return v >> 4, v & 0xf
}
// SetVersionAndIHL sets the version and IHL fields in the IPv4 header. Version should always be 4.
func (ifrm IPv4Frame) SetVersionAndIHL(version, IHL uint8) { ifrm.buf[0] = version<<4 | IHL&0xf }
// ToS (Type of Service) contains Differential Services Code Point (DSCP) and
// Explicit Congestion Notification (ECN) union data.
//
// DSCP originally defined as the type of service (ToS), this field specifies
// differentiated services (DiffServ) per RFC 2474. Real-time data streaming
// makes use of the DSCP field. An example is Voice over IP (VoIP), which is
// used for interactive voice services.
//
// ECN is defined in RFC 3168 and allows end-to-end notification of
// network congestion without dropping packets. ECN is an optional feature available
// when both endpoints support it and effective when also supported by the underlying network.
func (ifrm IPv4Frame) ToS() IPToS {
return IPToS(ifrm.buf[1])
}
// SetToS sets ToS field. See [IPv4Frame.ToS].
func (ifrm IPv4Frame) SetToS(tos IPToS) { ifrm.buf[1] = byte(tos) }
// TotalLength defines the entire packet size in bytes, including IP header and data.
// The minimum size is 20 bytes (IPv4 header without data) and the maximum is 65,535 bytes.
// All hosts are required to be able to reassemble datagrams of size up to 576 bytes,
// but most modern hosts handle much larger packets.
//
// Links may impose further restrictions on the packet size, in which case datagrams
// must be fragmented. Fragmentation in IPv4 is performed in either the
// sending host or in routers. Reassembly is performed at the receiving host.
func (ifrm IPv4Frame) TotalLength() uint16 {
return binary.BigEndian.Uint16(ifrm.buf[2:4])
}
// SetTotalLength sets TotalLength field. See [IPv4Frame.TotalLength].
func (ifrm IPv4Frame) SetTotalLength(tl uint16) { binary.BigEndian.PutUint16(ifrm.buf[2:4], tl) }
// ID is an identification field and is primarily used for uniquely
// identifying the group of fragments of a single IP datagram.
func (ifrm IPv4Frame) ID() uint16 {
return binary.BigEndian.Uint16(ifrm.buf[4:6])
}
// SetID sets ID field. See [IPv4Frame.ID].
func (ifrm IPv4Frame) SetID(id uint16) { binary.BigEndian.PutUint16(ifrm.buf[4:6], id) }
// Flags returns the [IPv4Flags] of the IP packet.
func (ifrm IPv4Frame) Flags() IPv4Flags {
return IPv4Flags(binary.BigEndian.Uint16(ifrm.buf[6:8]))
}
// SetFlags sets the IPv4 flags field. See [IPv4Flags].
func (ifrm IPv4Frame) SetFlags(flags IPv4Flags) {
binary.BigEndian.PutUint16(ifrm.buf[6:8], uint16(flags))
}
// TTL is an eight-bit time to live field limits a datagram's lifetime to prevent
// network failure in the event of a routing loop. In practice, the field
// is used as a hop count—when the datagram arrives at a router,
// the router decrements the TTL field by one. When the TTL field hits zero,
// the router discards the packet and typically sends an ICMP time exceeded message to the sender.
func (ifrm IPv4Frame) TTL() uint8 { return ifrm.buf[8] }
// SetTTL sets the IP frame's TTL field. See [IPv4Frame.TTL].
func (ifrm IPv4Frame) SetTTL(ttl uint8) { ifrm.buf[8] = ttl }
// Protocol field defines the protocol used in the data portion of the IP datagram. TCP is 6, UDP is 17.
// See [IPProto].
func (ifrm IPv4Frame) Protocol() IPProto { return IPProto(ifrm.buf[9]) }
// SetProtocol sets protocol field. See [IPv4Frame.Protocol] and [IPProto].
func (ifrm IPv4Frame) SetProtocol(proto IPProto) { ifrm.buf[9] = uint8(proto) }
// CRC returns the cyclic-redundancy-check (checksum) field of the IPv4 header.
func (ifrm IPv4Frame) CRC() uint16 {
return binary.BigEndian.Uint16(ifrm.buf[10:12])
}
// SetCRC sets the CRC field of the IP packet. See [IPv4Frame.CRC].
func (ifrm IPv4Frame) SetCRC(cs uint16) {
binary.BigEndian.PutUint16(ifrm.buf[10:12], cs)
}
// CalculateHeaderCRC calculates the CRC for this IPv4 frame.
func (ifrm IPv4Frame) CalculateHeaderCRC() uint16 {
var crc CRC791
crc.Write(ifrm.buf[0:10])
crc.Write(ifrm.buf[12:20])
return crc.Sum16()
}
func (ifrm IPv4Frame) crcWriteTCPPseudo(crc *CRC791) {
crc.Write(ifrm.SourceAddr()[:])
crc.Write(ifrm.DestinationAddr()[:])
crc.AddUint16(ifrm.TotalLength() - 4*uint16(ifrm.ihl()))
crc.AddUint16(uint16(ifrm.Protocol()))
}
func (ifrm IPv4Frame) crcWriteUDPPseudo(crc *CRC791) {
crc.Write(ifrm.SourceAddr()[:])
crc.Write(ifrm.DestinationAddr()[:])
crc.AddUint16(uint16(ifrm.Protocol()))
}
// SourceAddr returns pointer to the source IPv4 address in the IP header.
func (ifrm IPv4Frame) SourceAddr() *[4]byte {
return (*[4]byte)(ifrm.buf[12:16])
}
// DestinationAddr returns pointer to the destination IPv4 address in the IP header.
func (ifrm IPv4Frame) DestinationAddr() *[4]byte {
return (*[4]byte)(ifrm.buf[16:20])
}
// Payload returns the contents of the IPv4 packet, which may be zero sized.
// Be sure to call [IPv4Frame.ValidateSize] beforehand to avoid panic.
func (ifrm IPv4Frame) Payload() []byte {
off := ifrm.HeaderLength()
l := ifrm.TotalLength()
return ifrm.buf[off:l]
}
// Options returns the options portion of the IPv4 header. May be zero lengthed.
// Be sure to call [IPv4Frame.ValidateSize] beforehand to avoid panic.
func (ifrm IPv4Frame) Options() []byte {
off := ifrm.HeaderLength()
return ifrm.buf[sizeHeaderIPv4:off]
}
// ClearHeader zeros out the fixed(non-variable) header contents.
func (frm IPv4Frame) ClearHeader() {
for i := range frm.buf[:sizeHeaderIPv4] {
frm.buf[i] = 0
}
}
// IPv6Frame encapsulates the raw data of an IPv6 packet
// and provides methods for manipulating, validating and
// retrieving fields and payload data. See [RFC8200].
//
// [RFC8200]: https://tools.ietf.org/html/rfc8200
type IPv6Frame struct {
buf []byte
}
// RawData returns the underlying slice with which the frame was created.
func (i6frm IPv6Frame) RawData() []byte { return i6frm.buf }
// Payload returns the contents of the IPv6 packet, which may be zero sized.
// Be sure to call [IPv6Frame.ValidateSize] beforehand to avoid panic.
func (i6frm IPv6Frame) Payload() []byte {
pl := i6frm.PayloadLength()
return i6frm.buf[sizeHeaderIPv6 : sizeHeaderIPv6+pl]
}
// VersionTrafficAndFlow returns the version, Traffic and Flow label fields of the IPv6 header.
// See [IPToS] Traffic Class. Version should be 6 for IPv6.
func (i6frm IPv6Frame) VersionTrafficAndFlow() (version uint8, tos IPToS, flow uint32) {
v := binary.BigEndian.Uint32(i6frm.buf[0:4])
version = uint8(v >> (32 - 4))
tos = IPToS(v >> (32 - 12))
flow = v & 0x000f_ffff
return version, tos, flow
}
// SetVersionTrafficAndFlow sets the version, ToS and Flow label in the IPv6 header. Version must be equal to 6.
// See [IPv6Frame.VersionTrafficAndFlow].
func (i6frm IPv6Frame) SetVersionTrafficAndFlow(version uint8, tos IPToS, flow uint32) {
v := flow | uint32(tos)<<(32-12) | uint32(version)<<(32-4)
binary.BigEndian.PutUint32(i6frm.buf[0:4], v)
}
// PayloadLength returns the size of payload in octets(bytes) including any extension headers.
// The length is set to zero when a Hop-by-Hop extension header carries a Jumbo Payload option.
func (i6frm IPv6Frame) PayloadLength() uint16 {
return binary.BigEndian.Uint16(i6frm.buf[4:6])
}
// SetPayloadLength sets the payload length field of the IPv6 header. See [IPv6Frame.PayloadLength].
func (i6frm IPv6Frame) SetPayloadLength(pl uint16) {
binary.BigEndian.PutUint16(i6frm.buf[4:6], pl)
}
// NextHeader returns the Next Header field of the IPv6 header which usually specifies the transport layer
// protocol used by packet's payload.
func (i6frm IPv6Frame) NextHeader() IPProto {
return IPProto(i6frm.buf[6])
}
// SetNextHeader sets the Next Header (protocol) field of the IPv6 header. See [IPv6Frame.NextHeader].
func (i6frm IPv6Frame) SetNextHeader(proto IPProto) {
i6frm.buf[6] = uint8(proto)
}
// HopLimit returns the Hop Limit of the IPv6 header.
// This value is decremented by one at each forwarding node and the packet is discarded if it becomes 0.
// However, the destination node should process the packet normally even if received with a hop limit of 0.
func (i6frm IPv6Frame) HopLimit() uint8 {
return i6frm.buf[7]
}
// SetHopLimit sets the Hop Limit field of the IPv6 header. See [IPv6Frame.HopLimiy].
func (i6frm IPv6Frame) SetHopLimit(hop uint8) {
i6frm.buf[7] = hop
}
// SourceAddr returns pointer to the sending node unicast IPv6 address in the IP header.
func (i6frm IPv6Frame) SourceAddr() *[16]byte {
return (*[16]byte)(i6frm.buf[8:24])
}
// DestinationAddr returns pointer to the destination node unicast or multicast IPv6 address in the IP header.
func (i6frm IPv6Frame) DestinationAddr() *[16]byte {
return (*[16]byte)(i6frm.buf[24:40])
}
func (ifrm IPv6Frame) crcWritePseudo(crc *CRC791) {
crc.Write(ifrm.SourceAddr()[:])
crc.Write(ifrm.DestinationAddr()[:])
crc.AddUint32(uint32(ifrm.PayloadLength()))
crc.AddUint32(uint32(ifrm.NextHeader()))
}
// ClearHeader zeros out the header contents.
func (frm IPv6Frame) ClearHeader() {
for i := range frm.buf[:sizeHeaderIPv6] {
frm.buf[i] = 0
}
}
// TCPFrame encapsulates the raw data of a TCP segment
// and provides methods for manipulating, validating and
// retrieving fields and payload data. See [RFC9293].
//
// [RFC9293]: https://datatracker.ietf.org/doc/html/rfc9293
type TCPFrame struct {
buf []byte
}
// RawData returns the underlying slice with which the frame was created.
func (tfrm TCPFrame) RawData() []byte { return tfrm.buf }
// SourcePort identifies the sending port of the TCP packet. Must be non-zero.
func (tfrm TCPFrame) SourcePort() uint16 {
return binary.BigEndian.Uint16(tfrm.buf[0:2])
}
// SetSourcePort sets TCP source port. See [TCPFrame.SetSourcePort]
func (tfrm TCPFrame) SetSourcePort(src uint16) {
binary.BigEndian.PutUint16(tfrm.buf[0:2], src)
}
// DestinationPort identifies the receiving port for the TCP packet. Must be non-zero.
func (tfrm TCPFrame) DestinationPort() uint16 {
return binary.BigEndian.Uint16(tfrm.buf[2:4])
}
// SetDestinationPort sets TCP destination port. See [TCPFrame.DestinationPort]
func (tfrm TCPFrame) SetDestinationPort(dst uint16) {
binary.BigEndian.PutUint16(tfrm.buf[2:4], dst)
}
// Seq returns sequence number of the first data octet in this segment (except when SYN present)
// If SYN present this is the Initial Sequence Number (ISN) and the first data octet would be ISN+1.
func (tfrm TCPFrame) Seq() tcp.Value {
return tcp.Value(binary.BigEndian.Uint32(tfrm.buf[4:8]))
}
// SetSeq sets Seq field. See [TCPFrame.Seq].
func (tfrm TCPFrame) SetSeq(v tcp.Value) {
binary.BigEndian.PutUint32(tfrm.buf[4:8], uint32(v))
}
// Ack is the next sequence number (Seq field) the sender is expecting to receive (when ACK is present).
// In other words an Ack of X indicates all octets up to but not including X have been received.
// Once a connection is established the ACK flag should always be set.
func (tfrm TCPFrame) Ack() tcp.Value {
return tcp.Value(binary.BigEndian.Uint32(tfrm.buf[8:12]))
}
// SetAck sets Ack field. See [TCPFrame.Ack].
func (tfrm TCPFrame) SetAck(v tcp.Value) {
binary.BigEndian.PutUint32(tfrm.buf[8:12], uint32(v))
}
// OffsetAndFlags returns the offset and flag fields of TCP header.
// Offset is amount of 32-bit words used for TCP header including TCP options (see [TCPFrame.HeaderLength]).
// See [tcp.Flags] for more information on TCP flags.
func (tfrm TCPFrame) OffsetAndFlags() (offset uint8, flags tcp.Flags) {
v := binary.BigEndian.Uint16(tfrm.buf[12:14])
offset = uint8(v >> 12)
flags = tcp.Flags(v).Mask()
return offset, flags
}
// SetOffsetAndFlags returns offset and flag fields of TCP header. See [TCPFrame.OffsetAndFlags].
func (tfrm TCPFrame) SetOffsetAndFlags(offset uint8, flags tcp.Flags) {
v := uint16(offset)<<12 | uint16(flags.Mask())
binary.BigEndian.PutUint16(tfrm.buf[12:14], v)
}
// HeaderLength uses Offset field to calculate the total length of
// the TCP header including options. Performs no validation.
func (tfrm TCPFrame) HeaderLength() (tcpWords int) {
offset, _ := tfrm.OffsetAndFlags()
return 4 * int(offset)
}
func (tfrm TCPFrame) WindowSize() uint16 { return binary.BigEndian.Uint16(tfrm.buf[14:16]) }
func (tfrm TCPFrame) SetWindowSize(v uint16) {
binary.BigEndian.PutUint16(tfrm.buf[14:16], v)
}
// CRC returns the checksum field in the TCP header.
func (tfrm TCPFrame) CRC() uint16 {
return binary.BigEndian.Uint16(tfrm.buf[16:18])
}
// SetCRC sets the checksum field of the TCP header. See [TCPFrame.CRC].
func (tfrm TCPFrame) SetCRC(checksum uint16) {
binary.BigEndian.PutUint16(tfrm.buf[16:18], checksum)
}
// CalculateIPv4CRC returns the CRC for the TCP header over an IPv4 protocol.
func (tfrm TCPFrame) CalculateIPv4CRC(ifrm IPv4Frame) uint16 {
var crc CRC791
ifrm.crcWriteTCPPseudo(&crc)
expectLen := int(ifrm.TotalLength()) - ifrm.HeaderLength()
if expectLen != len(tfrm.buf) {
println("unexpected TCP buffer length mismatches IPv4 header total length", len(tfrm.buf), expectLen)
}
tfrm.crcWrite(&crc)
return crc.Sum16()
}
// CalculateIPv4CRC returns the CRC for the TCP header over an IPv4 protocol.
func (tfrm TCPFrame) CalculateIPv6CRC(ifrm IPv6Frame) uint16 {
var crc CRC791
ifrm.crcWritePseudo(&crc)
expectLen := int(ifrm.PayloadLength())
if expectLen != len(tfrm.buf) {
println("unexpected TCP buffer length mismatches IPv4 header total length", len(tfrm.buf), expectLen)
}
tfrm.crcWrite(&crc)
return crc.Sum16()
}
func (tfrm TCPFrame) crcWrite(crc *CRC791) {
// Write excluding CRC
crc.Write(tfrm.buf[:16])
crc.Write(tfrm.buf[18:])
}
func (tfrm TCPFrame) UrgentPtr() uint16 { return binary.BigEndian.Uint16(tfrm.buf[18:20]) }
func (tfrm TCPFrame) SetUrgentPtr(up uint16) { binary.BigEndian.PutUint16(tfrm.buf[18:20], up) }
// Payload returns the payload content section of the TCP packet (not including TCP options).
// Be sure to call [TCPFrame.ValidateSize] beforehand to avoid panic.
func (tfrm TCPFrame) Payload() []byte {
return tfrm.buf[tfrm.HeaderLength():]
}
// Segment returns the [tcp.Segment] representation of the TCP header and data length.
func (tfrm TCPFrame) Segment(payloadSize int) tcp.Segment {
if payloadSize > math.MaxUint32 {
panic("TCP overflow payload size")
}
return tcp.Segment{
SEQ: tfrm.Seq(),
ACK: tfrm.Ack(),
WND: tcp.Size(tfrm.WindowSize()),
DATALEN: tcp.Size(payloadSize),
Flags: tcp.Flags(binary.BigEndian.Uint16(tfrm.buf[12:14])).Mask(),
}
}
// Options returns the TCP option buffer portion of the frame. The returned slice may be zero length.
// Be sure to call [TCPFrame.ValidateSize] beforehand to avoid panic.
func (tfrm TCPFrame) Options() []byte {
return tfrm.buf[sizeHeaderTCP:tfrm.HeaderLength()]
}
// ClearHeader zeros out the fixed(non-variable) header contents.
func (frm TCPFrame) ClearHeader() {
for i := range frm.buf[:sizeHeaderTCP] {
frm.buf[i] = 0
}
}
func (tfrm TCPFrame) String() string {
seg := tfrm.Segment(len(tfrm.Payload()))
return fmt.Sprintf("%+v", seg)
}
// UDPFrame encapsulates the raw data of a UDP datagram
// and provides methods for manipulating, validating and
// retrieving fields and payload data. See [RFC768].
//
// [RFC768]: https://tools.ietf.org/html/rfc768
type UDPFrame struct {
buf []byte
}
// RawData returns the underlying slice with which the frame was created.
func (ufrm UDPFrame) RawData() []byte { return ufrm.buf }
// SourcePort identifies the sending port for the UDP packet. Must be non-zero.
func (ufrm UDPFrame) SourcePort() uint16 {
return binary.BigEndian.Uint16(ufrm.buf[0:2])
}
// SetSourcePort sets UDP source port. See [UDPFrame.SourcePort]
func (ufrm UDPFrame) SetSourcePort(src uint16) {
binary.BigEndian.PutUint16(ufrm.buf[0:2], src)
}
// DestinationPort identifies the receiving port for the UDP packet. Must be non-zero.
func (ufrm UDPFrame) DestinationPort() uint16 {
return binary.BigEndian.Uint16(ufrm.buf[2:4])
}
// SetDestinationPort sets UDP destination port. See [UDPFrame.DestinationPort]
func (ufrm UDPFrame) SetDestinationPort(dst uint16) {
binary.BigEndian.PutUint16(ufrm.buf[2:4], dst)
}
// Length specifies length in bytes of UDP header and UDP payload. The minimum length
// is 8 bytes (UDP header length). This field should match the result of the IP header
// TotalLength field minus the IP header size: udp.Length == ip.TotalLength - 4*ip.IHL
func (ufrm UDPFrame) Length() uint16 {
return binary.BigEndian.Uint16(ufrm.buf[4:6])
}
// SetLength sets the UDP header's length field. See [UDPFrame.Length].
func (ufrm UDPFrame) SetLength(length uint16) {
binary.BigEndian.PutUint16(ufrm.buf[4:6], length)
}
// CRC returns the checksum field in the UDP header.
func (ufrm UDPFrame) CRC() uint16 {
return binary.BigEndian.Uint16(ufrm.buf[6:8])
}
// SetCRC sets the UDP header's CRC field. See [UDPFrame.CRC].
func (ufrm UDPFrame) SetCRC(checksum uint16) {
binary.BigEndian.PutUint16(ufrm.buf[6:8], checksum)
}
// Payload returns the payload content section of the UDP packet.
// Be sure to call [UDPFrame.ValidateSize] beforehand to avoid panic.
func (ufrm UDPFrame) Payload() []byte {
l := ufrm.Length()
return ufrm.buf[sizeHeaderUDP:l]
}
func (ufrm UDPFrame) CalculateIPv4Checksum(ifrm IPv4Frame) uint16 {
var crc CRC791
ifrm.crcWriteUDPPseudo(&crc)
crc.AddUint16(ufrm.Length())
crc.AddUint16(ufrm.SourcePort())
crc.AddUint16(ufrm.DestinationPort())
crc.AddUint16(ufrm.Length()) // Length double tap.
crc.Write(ufrm.Payload())
return crc.Sum16()
}
func (ufrm UDPFrame) CalculateIPv6Checksum(ifrm IPv6Frame) uint16 {
var crc CRC791
ifrm.crcWritePseudo(&crc)
crc.AddUint16(ufrm.SourcePort())
crc.AddUint16(ufrm.DestinationPort())
crc.AddUint16(ufrm.Length()) // Length double tap.
crc.Write(ufrm.Payload())
return crc.Sum16()
}
// ClearHeader zeros out the header contents.
func (frm UDPFrame) ClearHeader() {
for i := range frm.buf[:sizeHeaderUDP] {
frm.buf[i] = 0
}
}
+16 -11
View File
@@ -5,7 +5,9 @@ import (
"math"
"math/rand"
"github.com/soypat/lneto"
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/lneto2"
"github.com/soypat/lneto/tcp"
)
@@ -47,14 +49,14 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg
hasIPOpt = ri&(1<<1) != 0
hasTCPOpt = ri&(1<<2) != 0
)
var etherType lneto.EtherType = lneto.EtherTypeIPv4
var etherType ethernet.Type = ethernet.TypeIPv4
var ipOpts []byte
if hasIPOpt {
ipOpts = []byte{1, 2, 3, 4}
}
ethsize := 14
if gen.EnableVLAN && isVLAN {
etherType = lneto.EtherTypeVLAN
etherType = ethernet.TypeVLAN
ethsize = 18
}
var tcpOpts []byte
@@ -66,7 +68,7 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg
tcpOptWlen := sizeWord(len(tcpOpts))
off := len(dst)
dst = append(dst, make([]byte, ethsize+sizeHeaderIPv4+4*int(ipOptWLen)+sizeHeaderTCP+4*int(tcpOptWlen)+int(seg.DATALEN))...)
efrm, err := lneto.NewEthFrame(dst[off:])
efrm, err := ethernet.NewFrame(dst[off:])
if err != nil {
panic(err)
}
@@ -75,11 +77,11 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg
efrm.SetEtherType(etherType)
if isVLAN {
efrm.SetVLANEtherType(lneto.EtherTypeIPv4)
efrm.SetVLANEtherType(ethernet.TypeIPv4)
efrm.SetVLANTag(1 << 4)
}
ethernetPayload := efrm.Payload()
ifrm, err := lneto.NewIPv4Frame(ethernetPayload)
ifrm, err := ipv4.NewFrame(ethernetPayload)
if err != nil {
panic(err)
}
@@ -89,13 +91,13 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg
ifrm.SetID(uint16(rng.Uint32()))
ifrm.SetFlags(0x4001) // Don't fragment.
ifrm.SetTTL(64)
ifrm.SetProtocol(lneto.IPProtoTCP)
ifrm.SetProtocol(lneto2.IPProtoTCP)
*ifrm.SourceAddr() = gen.SrcIPv4
*ifrm.DestinationAddr() = gen.DstIPv4
ifrm.SetCRC(ifrm.CalculateHeaderCRC())
ipPayload := ifrm.Payload()
tfrm, err := lneto.NewTCPFrame(ipPayload)
tfrm, err := tcp.NewFrame(ipPayload)
if err != nil {
panic(err)
}
@@ -120,8 +122,11 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg
// Set Variable section of data.
copy(ifrm.Options(), ipOpts)
copy(tfrm.Options(), tcpOpts)
tcpCRC := tfrm.CalculateIPv4CRC(ifrm)
tfrm.SetCRC(tcpCRC)
var crc lneto2.CRC791
ifrm.CRCWriteTCPPseudo(&crc)
tfrm.CRCWrite(&crc)
tfrm.SetCRC(crc.Sum16())
switch {
case gen.SrcTCP != tfrm.SourcePort():
panic("IP options overwrite TCP header")
@@ -136,7 +141,7 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg
case len(tcpPayload) > 0 && firstPayloadByte != tcpPayload[0]:
panic("TCP options overwrite payload")
}
var vld lneto.Validator
var vld lneto2.Validator
efrm.ValidateSize(&vld)
if err = vld.Err(); err != nil {
panic(err)
+13 -2
View File
@@ -7,6 +7,17 @@ import (
"github.com/soypat/lneto/lneto2"
)
// NewIPv4Frame returns a new IPv4Frame with data set to buf.
// An error is returned if the buffer size is smaller than 20.
// Users should still call [IPv4Frame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewFrame(buf []byte) (Frame, error) {
if len(buf) < sizeHeader {
return Frame{buf: nil}, errors.New("ipv4: short buffer")
}
return Frame{buf: buf}, nil
}
// Frame encapsulates the raw data of an IPv4 packet
// and provides methods for manipulating, validating and
// retreiving fields and payload data. See [RFC791].
@@ -123,14 +134,14 @@ func (ifrm Frame) CalculateHeaderCRC() uint16 {
return crc.Sum16()
}
func (ifrm Frame) crcWriteTCPPseudo(crc *lneto2.CRC791) {
func (ifrm Frame) CRCWriteTCPPseudo(crc *lneto2.CRC791) {
crc.Write(ifrm.SourceAddr()[:])
crc.Write(ifrm.DestinationAddr()[:])
crc.AddUint16(ifrm.TotalLength() - 4*uint16(ifrm.ihl()))
crc.AddUint16(uint16(ifrm.Protocol()))
}
func (ifrm Frame) crcWriteUDPPseudo(crc *lneto2.CRC791) {
func (ifrm Frame) CRCWriteUDPPseudo(crc *lneto2.CRC791) {
crc.Write(ifrm.SourceAddr()[:])
crc.Write(ifrm.DestinationAddr()[:])
crc.AddUint16(uint16(ifrm.Protocol()))
+31 -1
View File
@@ -2,10 +2,22 @@ package ipv6
import (
"encoding/binary"
"errors"
"github.com/soypat/lneto/lneto2"
)
// NewIPv6Frame returns a new IPv6Frame with data set to buf.
// An error is returned if the buffer size is smaller than 40.
// Users should still call [IPv6Frame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewFrame(buf []byte) (Frame, error) {
if len(buf) < sizeHeader {
return Frame{buf: nil}, errShortBuf
}
return Frame{buf: buf}, nil
}
// Frame encapsulates the raw data of an IPv6 packet
// and provides methods for manipulating, validating and
// retrieving fields and payload data. See [RFC8200].
@@ -86,7 +98,7 @@ func (i6frm Frame) DestinationAddr() *[16]byte {
return (*[16]byte)(i6frm.buf[24:40])
}
func (i6frm Frame) crcWritePseudo(crc *lneto2.CRC791) {
func (i6frm Frame) CRCWritePseudo(crc *lneto2.CRC791) {
crc.Write(i6frm.SourceAddr()[:])
crc.Write(i6frm.DestinationAddr()[:])
crc.AddUint32(uint32(i6frm.PayloadLength()))
@@ -99,3 +111,21 @@ func (i6frm Frame) ClearHeader() {
i6frm.buf[i] = 0
}
}
//
// Validate API.
//
var (
errShortFrame = errors.New("ipv6: short frame")
errShortBuf = errors.New("ipv6: short buffer for frame")
)
// 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 (i6frm Frame) ValidateSize(v *lneto2.Validator) {
tl := i6frm.PayloadLength()
if int(tl)+sizeHeader > len(i6frm.RawData()) {
v.AddError(errShortFrame)
}
}
+1 -118
View File
@@ -1,115 +1,6 @@
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
)
//go:generate stringer -type=IPProto -linecomment -output stringers.go .
// IPProto represents the IP protocol number.
type IPProto uint8
@@ -258,11 +149,3 @@ const (
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
)
+10 -9
View File
@@ -1,12 +1,13 @@
package lneto_test
package lneto2_test
import (
"bytes"
"math/rand"
"testing"
"github.com/soypat/lneto"
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/internal/ltesto"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/tcp"
)
@@ -37,31 +38,31 @@ func testMoveTCPPacket(t *testing.T, src, dst []byte) {
if len(src) != len(dst) {
panic("expect src and dst same length")
}
efrm, err := lneto.NewEthFrame(src)
efrm, err := ethernet.NewFrame(src)
if err != nil {
t.Fatal(err)
}
epl := efrm.Payload()
ifrm, err := lneto.NewIPv4Frame(epl)
ifrm, err := ipv4.NewFrame(epl)
if err != nil {
t.Fatal(err)
}
ipl := ifrm.Payload()
tfrm, err := lneto.NewTCPFrame(ipl)
tfrm, err := tcp.NewFrame(ipl)
if err != nil {
t.Fatal(err)
}
efrm2, _ := lneto.NewEthFrame(dst)
efrm2, _ := ethernet.NewFrame(dst)
*efrm2.DestinationHardwareAddr() = *efrm.DestinationHardwareAddr()
*efrm2.SourceHardwareAddr() = *efrm.SourceHardwareAddr()
efrm2.SetEtherType(efrm.EtherTypeOrSize())
if efrm.EtherTypeOrSize() == lneto.EtherTypeVLAN {
if efrm.EtherTypeOrSize() == ethernet.TypeVLAN {
efrm2.SetVLANTag(efrm.VLANTag())
efrm2.SetVLANEtherType(efrm.VLANEtherType())
}
ifrm2, _ := lneto.NewIPv4Frame(efrm2.Payload())
ifrm2, _ := ipv4.NewFrame(efrm2.Payload())
ifrm2.SetVersionAndIHL(ifrm.VersionAndIHL())
ifrm2.SetToS(ifrm.ToS())
ifrm2.SetFlags(ifrm.Flags())
@@ -73,7 +74,7 @@ func testMoveTCPPacket(t *testing.T, src, dst []byte) {
*ifrm2.SourceAddr() = *ifrm.SourceAddr()
*ifrm2.DestinationAddr() = *ifrm.DestinationAddr()
tfrm2, _ := lneto.NewTCPFrame(ifrm2.Payload())
tfrm2, _ := tcp.NewFrame(ifrm2.Payload())
tfrm2.SetSourcePort(tfrm.SourcePort())
tfrm2.SetDestinationPort(tfrm.DestinationPort())
tfrm2.SetSeq(tfrm.Seq())
+1 -119
View File
@@ -1,108 +1,9 @@
// Code generated by "stringer -type=EtherType,IPProto,ARPOp -linecomment -output stringers.go ."; DO NOT EDIT.
// Code generated by "stringer -type=IPProto -linecomment -output stringers.go ."; DO NOT EDIT.
package lneto2
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[EtherTypeIPv4-2048]
_ = x[EtherTypeARP-2054]
_ = x[EtherTypeWakeOnLAN-2114]
_ = x[EtherTypeTRILL-8947]
_ = x[EtherTypeDECnetPhase4-24579]
_ = x[EtherTypeRARP-32821]
_ = x[EtherTypeAppleTalk-32923]
_ = x[EtherTypeAARP-33011]
_ = x[EtherTypeIPX1-33079]
_ = x[EtherTypeIPX2-33080]
_ = x[EtherTypeQNXQnet-33284]
_ = x[EtherTypeIPv6-34525]
_ = x[EtherTypeEthernetFlowControl-34824]
_ = x[EtherTypeIEEE802_3-34825]
_ = x[EtherTypeCobraNet-34841]
_ = x[EtherTypeMPLSUnicast-34887]
_ = x[EtherTypeMPLSMulticast-34888]
_ = x[EtherTypePPPoEDiscovery-34915]
_ = x[EtherTypePPPoESession-34916]
_ = x[EtherTypeJumboFrames-34928]
_ = x[EtherTypeHomePlug1_0MME-34939]
_ = x[EtherTypeIEEE802_1X-34958]
_ = x[EtherTypePROFINET-34962]
_ = x[EtherTypeHyperSCSI-34970]
_ = x[EtherTypeAoE-34978]
_ = x[EtherTypeEtherCAT-34980]
_ = x[EtherTypeEthernetPowerlink-34987]
_ = x[EtherTypeLLDP-35020]
_ = x[EtherTypeSERCOS3-35021]
_ = x[EtherTypeHomePlugAVMME-35041]
_ = x[EtherTypeMRP-35043]
_ = x[EtherTypeIEEE802_1AE-35045]
_ = x[EtherTypeIEEE1588-35063]
_ = x[EtherTypeIEEE802_1ag-35074]
_ = x[EtherTypeFCoE-35078]
_ = x[EtherTypeFCoEInit-35092]
_ = x[EtherTypeRoCE-35093]
_ = x[EtherTypeCTP-36864]
_ = x[EtherTypeVeritasLLT-51966]
_ = x[EtherTypeVLAN-33024]
_ = x[EtherTypeServiceVLAN-34984]
}
const _EtherType_name = "IPv4ARPwake on LANTRILLDECnetPhase4RARPAppleTalkAARPVLANIPx1IPx2QNXQnetIPv6EthernetFlowCtlIEEE802.3CobraNetMPLS UnicastMPLS MulticastPPPoE discoveryPPPoE sessionjumbo frameshome plug 1 0mmeIEEE 802.1xprofinethyper SCSIAoEEtherCATservice VLANEthernet powerlinkLLDPSERCOS3home plug AVMMEMRPIEEE 802.1aeIEEE 1588IEEE 802.1agFCoEFCoE initRoCECTPVeritas LLT"
var _EtherType_map = map[EtherType]string{
2048: _EtherType_name[0:4],
2054: _EtherType_name[4:7],
2114: _EtherType_name[7:18],
8947: _EtherType_name[18:23],
24579: _EtherType_name[23:35],
32821: _EtherType_name[35:39],
32923: _EtherType_name[39:48],
33011: _EtherType_name[48:52],
33024: _EtherType_name[52:56],
33079: _EtherType_name[56:60],
33080: _EtherType_name[60:64],
33284: _EtherType_name[64:71],
34525: _EtherType_name[71:75],
34824: _EtherType_name[75:90],
34825: _EtherType_name[90:99],
34841: _EtherType_name[99:107],
34887: _EtherType_name[107:119],
34888: _EtherType_name[119:133],
34915: _EtherType_name[133:148],
34916: _EtherType_name[148:161],
34928: _EtherType_name[161:173],
34939: _EtherType_name[173:189],
34958: _EtherType_name[189:200],
34962: _EtherType_name[200:208],
34970: _EtherType_name[208:218],
34978: _EtherType_name[218:221],
34980: _EtherType_name[221:229],
34984: _EtherType_name[229:241],
34987: _EtherType_name[241:259],
35020: _EtherType_name[259:263],
35021: _EtherType_name[263:270],
35041: _EtherType_name[270:285],
35043: _EtherType_name[285:288],
35045: _EtherType_name[288:300],
35063: _EtherType_name[300:309],
35074: _EtherType_name[309:321],
35078: _EtherType_name[321:325],
35092: _EtherType_name[325:334],
35093: _EtherType_name[334:338],
36864: _EtherType_name[338:341],
51966: _EtherType_name[341:352],
}
func (i EtherType) String() string {
if str, ok := _EtherType_map[i]; ok {
return str
}
return "EtherType(" + strconv.FormatInt(int64(i), 10) + ")"
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
@@ -289,22 +190,3 @@ func (i IPProto) String() string {
return "IPProto(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ARPRequest-1]
_ = x[ARPReply-2]
}
const _ARPOp_name = "requestreply"
var _ARPOp_index = [...]uint8{0, 7, 12}
func (i ARPOp) String() string {
i -= 1
if i >= ARPOp(len(_ARPOp_index)-1) {
return "ARPOp(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _ARPOp_name[_ARPOp_index[i]:_ARPOp_index[i+1]]
}
-310
View File
@@ -1,310 +0,0 @@
// Code generated by "stringer -type=EtherType,IPProto,ARPOp -linecomment -output stringers.go ."; DO NOT EDIT.
package lneto
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[EtherTypeIPv4-2048]
_ = x[EtherTypeARP-2054]
_ = x[EtherTypeWakeOnLAN-2114]
_ = x[EtherTypeTRILL-8947]
_ = x[EtherTypeDECnetPhase4-24579]
_ = x[EtherTypeRARP-32821]
_ = x[EtherTypeAppleTalk-32923]
_ = x[EtherTypeAARP-33011]
_ = x[EtherTypeIPX1-33079]
_ = x[EtherTypeIPX2-33080]
_ = x[EtherTypeQNXQnet-33284]
_ = x[EtherTypeIPv6-34525]
_ = x[EtherTypeEthernetFlowControl-34824]
_ = x[EtherTypeIEEE802_3-34825]
_ = x[EtherTypeCobraNet-34841]
_ = x[EtherTypeMPLSUnicast-34887]
_ = x[EtherTypeMPLSMulticast-34888]
_ = x[EtherTypePPPoEDiscovery-34915]
_ = x[EtherTypePPPoESession-34916]
_ = x[EtherTypeJumboFrames-34928]
_ = x[EtherTypeHomePlug1_0MME-34939]
_ = x[EtherTypeIEEE802_1X-34958]
_ = x[EtherTypePROFINET-34962]
_ = x[EtherTypeHyperSCSI-34970]
_ = x[EtherTypeAoE-34978]
_ = x[EtherTypeEtherCAT-34980]
_ = x[EtherTypeEthernetPowerlink-34987]
_ = x[EtherTypeLLDP-35020]
_ = x[EtherTypeSERCOS3-35021]
_ = x[EtherTypeHomePlugAVMME-35041]
_ = x[EtherTypeMRP-35043]
_ = x[EtherTypeIEEE802_1AE-35045]
_ = x[EtherTypeIEEE1588-35063]
_ = x[EtherTypeIEEE802_1ag-35074]
_ = x[EtherTypeFCoE-35078]
_ = x[EtherTypeFCoEInit-35092]
_ = x[EtherTypeRoCE-35093]
_ = x[EtherTypeCTP-36864]
_ = x[EtherTypeVeritasLLT-51966]
_ = x[EtherTypeVLAN-33024]
_ = x[EtherTypeServiceVLAN-34984]
}
const _EtherType_name = "IPv4ARPwake on LANTRILLDECnetPhase4RARPAppleTalkAARPVLANIPx1IPx2QNXQnetIPv6EthernetFlowCtlIEEE802.3CobraNetMPLS UnicastMPLS MulticastPPPoE discoveryPPPoE sessionjumbo frameshome plug 1 0mmeIEEE 802.1xprofinethyper SCSIAoEEtherCATservice VLANEthernet powerlinkLLDPSERCOS3home plug AVMMEMRPIEEE 802.1aeIEEE 1588IEEE 802.1agFCoEFCoE initRoCECTPVeritas LLT"
var _EtherType_map = map[EtherType]string{
2048: _EtherType_name[0:4],
2054: _EtherType_name[4:7],
2114: _EtherType_name[7:18],
8947: _EtherType_name[18:23],
24579: _EtherType_name[23:35],
32821: _EtherType_name[35:39],
32923: _EtherType_name[39:48],
33011: _EtherType_name[48:52],
33024: _EtherType_name[52:56],
33079: _EtherType_name[56:60],
33080: _EtherType_name[60:64],
33284: _EtherType_name[64:71],
34525: _EtherType_name[71:75],
34824: _EtherType_name[75:90],
34825: _EtherType_name[90:99],
34841: _EtherType_name[99:107],
34887: _EtherType_name[107:119],
34888: _EtherType_name[119:133],
34915: _EtherType_name[133:148],
34916: _EtherType_name[148:161],
34928: _EtherType_name[161:173],
34939: _EtherType_name[173:189],
34958: _EtherType_name[189:200],
34962: _EtherType_name[200:208],
34970: _EtherType_name[208:218],
34978: _EtherType_name[218:221],
34980: _EtherType_name[221:229],
34984: _EtherType_name[229:241],
34987: _EtherType_name[241:259],
35020: _EtherType_name[259:263],
35021: _EtherType_name[263:270],
35041: _EtherType_name[270:285],
35043: _EtherType_name[285:288],
35045: _EtherType_name[288:300],
35063: _EtherType_name[300:309],
35074: _EtherType_name[309:321],
35078: _EtherType_name[321:325],
35092: _EtherType_name[325:334],
35093: _EtherType_name[334:338],
36864: _EtherType_name[338:341],
51966: _EtherType_name[341:352],
}
func (i EtherType) String() string {
if str, ok := _EtherType_map[i]; ok {
return str
}
return "EtherType(" + strconv.FormatInt(int64(i), 10) + ")"
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[IPProtoHopByHop-0]
_ = x[IPProtoICMP-1]
_ = x[IPProtoIGMP-2]
_ = x[IPProtoGGP-3]
_ = x[IPProtoIPv4-4]
_ = x[IPProtoST-5]
_ = x[IPProtoTCP-6]
_ = x[IPProtoCBT-7]
_ = x[IPProtoEGP-8]
_ = x[IPProtoIGP-9]
_ = x[IPProtoBBNRCCMON-10]
_ = x[IPProtoNVP-11]
_ = x[IPProtoPUP-12]
_ = x[IPProtoARGUS-13]
_ = x[IPProtoEMCON-14]
_ = x[IPProtoXNET-15]
_ = x[IPProtoCHAOS-16]
_ = x[IPProtoUDP-17]
_ = x[IPProtoMUX-18]
_ = x[IPProtoDCNMEAS-19]
_ = x[IPProtoHMP-20]
_ = x[IPProtoPRM-21]
_ = x[IPProtoXNSIDP-22]
_ = x[IPProtoTRUNK1-23]
_ = x[IPProtoTRUNK2-24]
_ = x[IPProtoLEAF1-25]
_ = x[IPProtoLEAF2-26]
_ = x[IPProtoRDP-27]
_ = x[IPProtoIRTP-28]
_ = x[IPProtoISO_TP4-29]
_ = x[IPProtoNETBLT-30]
_ = x[IPProtoMFE_NSP-31]
_ = x[IPProtoMERIT_INP-32]
_ = x[IPProtoDCCP-33]
_ = x[IPProto3PC-34]
_ = x[IPProtoIDPR-35]
_ = x[IPProtoXTP-36]
_ = x[IPProtoDDP-37]
_ = x[IPProtoIDPRCMTP-38]
_ = x[IPProtoTPPLUSPLUS-39]
_ = x[IPProtoIL-40]
_ = x[IPProtoIPv6-41]
_ = x[IPProtoSDRP-42]
_ = x[IPProtoIPv6Route-43]
_ = x[IPProtoIPv6Frag-44]
_ = x[IPProtoIDRP-45]
_ = x[IPProtoRSVP-46]
_ = x[IPProtoGRE-47]
_ = x[IPProtoDSR-48]
_ = x[IPProtoBNA-49]
_ = x[IPProtoESP-50]
_ = x[IPProtoAH-51]
_ = x[IPProtoINLSP-52]
_ = x[IPProtoSWIPE-53]
_ = x[IPProtoNARP-54]
_ = x[IPProtoMOBILE-55]
_ = x[IPProtoTLSP-56]
_ = x[IPProtoSKIP-57]
_ = x[IPProtoIPv6ICMP-58]
_ = x[IPProtoIPv6NoNxt-59]
_ = x[IPProtoIPv6Opts-60]
_ = x[IPProtoCFTP-62]
_ = x[IPProtoSATEXPAK-64]
_ = x[IPProtoKRYPTOLAN-65]
_ = x[IPProtoRVD-66]
_ = x[IPProtoIPPC-67]
_ = x[IPProtoSATMON-69]
_ = x[IPProtoVISA-70]
_ = x[IPProtoIPCV-71]
_ = x[IPProtoCPNX-72]
_ = x[IPProtoCPHB-73]
_ = x[IPProtoWSN-74]
_ = x[IPProtoPVP-75]
_ = x[IPProtoBRSATMON-76]
_ = x[IPProtoSUNND-77]
_ = x[IPProtoWBMON-78]
_ = x[IPProtoWBEXPAK-79]
_ = x[IPProtoISOIP-80]
_ = x[IPProtoVMTP-81]
_ = x[IPProtoSECUREVMTP-82]
_ = x[IPProtoVINES-83]
_ = x[IPProtoTTP-84]
_ = x[IPProtoNSFNETIGP-85]
_ = x[IPProtoDGP-86]
_ = x[IPProtoTCF-87]
_ = x[IPProtoEIGRP-88]
_ = x[IPProtoOSPFIGP-89]
_ = x[IPProtoSpriteRPC-90]
_ = x[IPProtoLARP-91]
_ = x[IPProtoMTP-92]
_ = x[IPProtoAX25-93]
_ = x[IPProtoIPIP-94]
_ = x[IPProtoMICP-95]
_ = x[IPProtoSCCSP-96]
_ = x[IPProtoETHERIP-97]
_ = x[IPProtoENCAP-98]
_ = x[IPProtoGMTP-100]
_ = x[IPProtoIFMP-101]
_ = x[IPProtoPNNI-102]
_ = x[IPProtoPIM-103]
_ = x[IPProtoARIS-104]
_ = x[IPProtoSCPS-105]
_ = x[IPProtoQNX-106]
_ = x[IPProtoAN-107]
_ = x[IPProtoIPComp-108]
_ = x[IPProtoSNP-109]
_ = x[IPProtoCompaqPeer-110]
_ = x[IPProtoIPXInIP-111]
_ = x[IPProtoVRRP-112]
_ = x[IPProtoPGM-113]
_ = x[IPProtoL2TP-115]
_ = x[IPProtoDDX-116]
_ = x[IPProtoIATP-117]
_ = x[IPProtoSTP-118]
_ = x[IPProtoSRP-119]
_ = x[IPProtoUTI-120]
_ = x[IPProtoSMP-121]
_ = x[IPProtoSM-122]
_ = x[IPProtoPTP-123]
_ = x[IPProtoISIS-124]
_ = x[IPProtoFIRE-125]
_ = x[IPProtoCRTP-126]
_ = x[IPProtoCRUDP-127]
_ = x[IPProtoSSCOPMCE-128]
_ = x[IPProtoIPLT-129]
_ = x[IPProtoSPS-130]
_ = x[IPProtoPIPE-131]
_ = x[IPProtoSCTP-132]
_ = x[IPProtoFC-133]
_ = x[IPProtoRSVP_E2E_IGNORE-134]
_ = x[IPProtoMobilityHeader-135]
_ = x[IPProtoUDPLite-136]
_ = x[IPProtoMPLSInIP-137]
_ = x[IPProtoMANET-138]
_ = x[IPProtoHIP-139]
_ = x[IPProtoShim6-140]
_ = x[IPProtoWESP-141]
_ = x[IPProtoROHC-142]
_ = x[IPProtoEthernet-143]
_ = x[IPProtoAGGFRAG-144]
_ = x[IPProtoNSH-145]
}
const (
_IPProto_name_0 = "IPv6 Hop-by-Hop Option [RFC8200]Internet Control Message [RFC792]Internet Group Management [RFC1112]Gateway-to-Gateway [RFC823]IPv4 encapsulation [RFC2003]Stream [RFC1190, RFC1819]Transmission Control [RFC9293]CBT [Ballardie]Exterior Gateway Protocol [RFC888]any private interior gateway (used by Cisco for their IGRP)BBN RCC MonitoringNetwork Voice Protocol [RFC741]PUPARGUSEMCONCross Net DebuggerChaosUser Datagram [RFC768]MultiplexingDCN Measurement SubsystemsHost Monitoring [RFC869]Packet Radio MeasurementXEROX NS IDPTrunk-1Trunk-2Leaf-1Leaf-2Reliable Data Protocol [RFC908]Internet Reliable Transaction [RFC938]ISO Transport Protocol Class 4 [RFC905]Bulk Data Transfer Protocol [RFC998]MFE Network Services ProtocolMERIT Internodal ProtocolDatagram Congestion Control Protocol [RFC4340]Third Party Connect ProtocolInter-Domain Policy Routing ProtocolXTPDatagram Delivery ProtocolIDPR Control Message Transport ProtoTP++ Transport ProtocolIL Transport ProtocolIPv6 encapsulation [RFC2473]Source Demand Routing ProtocolRouting Header for IPv6 [RFC8200]Fragment Header for IPv6 [RFC8200]Inter-Domain Routing ProtocolReservation Protocol [RFC2205]Generic Routing Encapsulation [RFC2784]Dynamic Source Routing ProtocolBNAEncap Security Payload [RFC4303]Authentication Header [RFC4302]Integrated Net Layer Security TUBAIP with EncryptionNBMA Address Resolution ProtocolIP MobilityTransport Layer Security Protocol using Kryptonet key managementSKIPICMP for IPv6 [RFC8200]No Next Header for IPv6 [RFC8200]Destination Options for IPv6 [RFC8200]"
_IPProto_name_1 = "CFTP"
_IPProto_name_2 = "SATNET and Backroom EXPAKKryptolanMIT Remote Virtual Disk ProtocolInternet Pluribus Packet Core"
_IPProto_name_3 = "SATNET MonitoringVISA ProtocolInternet Packet Core UtilityComputer Protocol Network ExecutiveComputer Protocol Heart BeatWang Span NetworkPacket Video ProtocolBackroom SATNET MonitoringSUN ND PROTOCOL-TemporaryWIDEBAND MonitoringWIDEBAND EXPAKISO Internet ProtocolVMTPSECURE-VMTPVINESTTPNSFNET-IGPDissimilar Gateway ProtocolTCFEIGRPOSPFIGPSprite RPC ProtocolLocus Address Resolution ProtocolMulticast Transport ProtocolAX.25 FramesIP-within-IP Encapsulation ProtocolMobile Internetworking Control Pro.Semaphore Communications Sec. Pro.Ethernet-within-IP EncapsulationEncapsulation Header"
_IPProto_name_4 = "GMTPIpsilon Flow Management ProtocolPNNI over IPProtocol Independent MulticastARISSCPSQNXActive NetworksIP Payload Compression ProtocolSitara Networks ProtocolCompaq Peer ProtocolIPX in IPVirtual Router Redundancy ProtocolPGM Reliable Transport Protocol"
_IPProto_name_5 = "Layer Two Tunneling Protocol v3D-II Data Exchange (DDX)Interactive Agent Transfer ProtocolSchedule Transfer ProtocolSpectraLink Radio ProtocolUTISimple Message ProtocolSMPerformance Transparency ProtocolISIS over IPv4FIRECombat Radio Transport ProtocolCombat Radio User DatagramSSCOPMCEIPLTSecure Packet ShieldPrivate IP Encapsulation within IPStream Control Transmission ProtocolFibre ChannelRSVP-E2E-IGNOREMobility HeaderUDPLiteMPLS-in-IPMANET ProtocolsHost Identity ProtocolShim6 ProtocolWrapped Encapsulating Security PayloadRobust Header CompressionEthernetAGGFRAG Encapsulation payload for ESPNetwork Service Header"
)
var (
_IPProto_index_0 = [...]uint16{0, 32, 65, 100, 127, 155, 180, 210, 225, 259, 318, 336, 367, 370, 375, 380, 398, 403, 425, 437, 463, 487, 511, 523, 530, 537, 543, 549, 580, 618, 657, 693, 722, 747, 793, 821, 857, 860, 886, 922, 945, 966, 994, 1024, 1057, 1091, 1120, 1150, 1189, 1220, 1223, 1255, 1286, 1320, 1338, 1370, 1381, 1445, 1449, 1472, 1505, 1543}
_IPProto_index_2 = [...]uint8{0, 25, 34, 66, 95}
_IPProto_index_3 = [...]uint16{0, 17, 30, 58, 93, 121, 138, 159, 185, 210, 229, 243, 264, 268, 279, 284, 287, 297, 324, 327, 332, 339, 358, 391, 419, 431, 466, 501, 535, 567, 587}
_IPProto_index_4 = [...]uint8{0, 4, 36, 48, 78, 82, 86, 89, 104, 135, 159, 179, 188, 222, 253}
_IPProto_index_5 = [...]uint16{0, 31, 55, 90, 116, 142, 145, 168, 170, 203, 217, 221, 252, 278, 286, 290, 310, 344, 380, 393, 408, 423, 430, 440, 455, 477, 491, 529, 554, 562, 599, 621}
)
func (i IPProto) String() string {
switch {
case i <= 60:
return _IPProto_name_0[_IPProto_index_0[i]:_IPProto_index_0[i+1]]
case i == 62:
return _IPProto_name_1
case 64 <= i && i <= 67:
i -= 64
return _IPProto_name_2[_IPProto_index_2[i]:_IPProto_index_2[i+1]]
case 69 <= i && i <= 98:
i -= 69
return _IPProto_name_3[_IPProto_index_3[i]:_IPProto_index_3[i+1]]
case 100 <= i && i <= 113:
i -= 100
return _IPProto_name_4[_IPProto_index_4[i]:_IPProto_index_4[i+1]]
case 115 <= i && i <= 145:
i -= 115
return _IPProto_name_5[_IPProto_index_5[i]:_IPProto_index_5[i+1]]
default:
return "IPProto(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ARPRequest-1]
_ = x[ARPReply-2]
}
const _ARPOp_name = "requestreply"
var _ARPOp_index = [...]uint8{0, 7, 12}
func (i ARPOp) String() string {
i -= 1
if i >= ARPOp(len(_ARPOp_index)-1) {
return "ARPOp(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _ARPOp_name[_ARPOp_index[i]:_ARPOp_index[i+1]]
}
+14 -8
View File
@@ -9,14 +9,6 @@ import (
"github.com/soypat/lneto/lneto2"
)
var (
errShortTCP = errors.New("TCP offset exceeds frame")
errBadTCPOff = errors.New("TCP offset invalid")
errEvilPacket = errors.New("evil packet")
errZeroDstPort = errors.New("TCP zero destination port")
errZeroSrcPort = errors.New("TCP zero source port")
)
const (
sizeHeaderTCP = 20
)
@@ -148,6 +140,12 @@ func (tfrm Frame) Segment(payloadSize int) Segment {
}
}
func (tfrm Frame) CRCWrite(crc *lneto2.CRC791) {
// Write excluding CRC
crc.Write(tfrm.buf[:16])
crc.Write(tfrm.buf[18:])
}
// SetSegment sets the sequence, acknowledgment, offset, window and flag fields of the TCP header from the the [Segment].
// Offset, like in [Frame.SetOffset], is expressed in words with minimum being 5.
func (tfrm Frame) SetSegment(seg Segment, offset uint8) {
@@ -184,6 +182,14 @@ func (tfrm Frame) String() string {
// Validation API
//
var (
errShortTCP = errors.New("TCP offset exceeds frame")
errBadTCPOff = errors.New("TCP offset invalid")
errEvilPacket = errors.New("evil packet")
errZeroDstPort = errors.New("TCP zero destination port")
errZeroSrcPort = errors.New("TCP zero source port")
)
// func (tfrm Frame) Validate(v *lneto2.Validator) {
// tfrm.ValidateSize(v)
// tfrm.ValidateExceptCRC(v)
+8 -6
View File
@@ -5,7 +5,9 @@ import (
"strconv"
"testing"
"github.com/soypat/lneto"
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/lneto2"
"github.com/soypat/lneto/tcp"
)
@@ -584,20 +586,20 @@ func TestExchange_helloworld_client(t *testing.T) {
}
func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) {
var vld lneto.Validator
var vld lneto2.Validator
t.Helper()
efrm, err := lneto.NewEthFrame(b)
efrm, err := ethernet.NewFrame(b)
if err != nil {
t.Fatal(err)
}
if efrm.EtherTypeOrSize() != lneto.EtherTypeIPv4 {
if efrm.EtherTypeOrSize() != ethernet.TypeIPv4 {
t.Fatalf("not IPv4")
}
efrm.ValidateSize(&vld)
if err := vld.Err(); err != nil {
t.Fatal(vld.Err())
}
ifrm, err := lneto.NewIPv4Frame(efrm.Payload())
ifrm, err := ipv4.NewFrame(efrm.Payload())
if err != nil {
t.Fatal(err)
}
@@ -614,7 +616,7 @@ func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) {
}
ipl := ifrm.Payload()
tfrm, err := lneto.NewTCPFrame(ipl)
tfrm, err := tcp.NewFrame(ipl)
if err != nil {
t.Fatal(err)
}
+5
View File
@@ -0,0 +1,5 @@
package udp
const (
sizeHeader = 8
)
+129
View File
@@ -0,0 +1,129 @@
package udp
import (
"encoding/binary"
"errors"
"github.com/soypat/lneto/lneto2"
)
// NewUDPFrame returns a new UDPFrame with data set to buf.
// An error is returned if the buffer size is smaller than 8.
// Users should still call [Frame.ValidateSize] before working
// with payload/options of frames to avoid panics.
func NewFrame(buf []byte) (Frame, error) {
if len(buf) < sizeHeader {
return Frame{buf: buf}, errors.New("UDP packet too short")
}
return Frame{buf: buf}, nil
}
// Frame encapsulates the raw data of a UDP datagram
// and provides methods for manipulating, validating and
// retrieving fields and payload data. See [RFC768].
//
// [RFC768]: https://tools.ietf.org/html/rfc768
type Frame struct {
buf []byte
}
// RawData returns the underlying slice with which the frame was created.
func (ufrm Frame) RawData() []byte { return ufrm.buf }
// SourcePort identifies the sending port for the UDP packet. Must be non-zero.
func (ufrm Frame) SourcePort() uint16 {
return binary.BigEndian.Uint16(ufrm.buf[0:2])
}
// SetSourcePort sets UDP source port. See [Frame.SourcePort]
func (ufrm Frame) SetSourcePort(src uint16) {
binary.BigEndian.PutUint16(ufrm.buf[0:2], src)
}
// DestinationPort identifies the receiving port for the UDP packet. Must be non-zero.
func (ufrm Frame) DestinationPort() uint16 {
return binary.BigEndian.Uint16(ufrm.buf[2:4])
}
// SetDestinationPort sets UDP destination port. See [Frame.DestinationPort]
func (ufrm Frame) SetDestinationPort(dst uint16) {
binary.BigEndian.PutUint16(ufrm.buf[2:4], dst)
}
// Length specifies length in bytes of UDP header and UDP payload. The minimum length
// is 8 bytes (UDP header length). This field should match the result of the IP header
// TotalLength field minus the IP header size: udp.Length == ip.TotalLength - 4*ip.IHL
func (ufrm Frame) Length() uint16 {
return binary.BigEndian.Uint16(ufrm.buf[4:6])
}
// SetLength sets the UDP header's length field. See [Frame.Length].
func (ufrm Frame) SetLength(length uint16) {
binary.BigEndian.PutUint16(ufrm.buf[4:6], length)
}
// CRC returns the checksum field in the UDP header.
func (ufrm Frame) CRC() uint16 {
return binary.BigEndian.Uint16(ufrm.buf[6:8])
}
// SetCRC sets the UDP header's CRC field. See [Frame.CRC].
func (ufrm Frame) SetCRC(checksum uint16) {
binary.BigEndian.PutUint16(ufrm.buf[6:8], checksum)
}
// Payload returns the payload content section of the UDP packet.
// Be sure to call [Frame.ValidateSize] beforehand to avoid panic.
func (ufrm Frame) Payload() []byte {
l := ufrm.Length()
return ufrm.buf[sizeHeader:l]
}
// func (ufrm Frame) CalculateIPv4Checksum(ifrm IPv4Frame) uint16 {
// var crc lneto2.CRC791
// ifrm.crcWriteUDPPseudo(&crc)
// crc.AddUint16(ufrm.Length())
// crc.AddUint16(ufrm.SourcePort())
// crc.AddUint16(ufrm.DestinationPort())
// crc.AddUint16(ufrm.Length()) // Length double tap.
// crc.Write(ufrm.Payload())
// return crc.Sum16()
// }
// func (ufrm Frame) CalculateIPv6Checksum(ifrm IPv6Frame) uint16 {
// var crc lneto2.CRC791
// ifrm.crcWritePseudo(&crc)
// crc.AddUint16(ufrm.SourcePort())
// crc.AddUint16(ufrm.DestinationPort())
// crc.AddUint16(ufrm.Length()) // Length double tap.
// crc.Write(ufrm.Payload())
// return crc.Sum16()
// }
// ClearHeader zeros out the header contents.
func (frm Frame) ClearHeader() {
for i := range frm.buf[:sizeHeader] {
frm.buf[i] = 0
}
}
//
// Validation API.
//
var (
errBadLen = errors.New("udp: bad UDP length")
errShort = errors.New("udp: short buffer")
)
// 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 (ufrm Frame) ValidateSize(v *lneto2.Validator) {
ul := ufrm.Length()
if ul < sizeHeader {
v.AddError(errBadLen)
}
if int(ul) > len(ufrm.RawData()) {
v.AddError(errShort)
}
}
-142
View File
@@ -1,142 +0,0 @@
package lneto
import "errors"
var (
errShortEth = errors.New("ethernet length exceeds frame")
errShortVLAN = errors.New("ethernet length too short for VLAN")
errShortUDP = errors.New("UDP length exceeds frame")
errBadUDPLen = errors.New("UDP length invalid")
errShortIPv4 = errors.New("IPv4 total length exceeds frame")
errBadIPv4TL = errors.New("IPv4 short total length")
errBadIPv4IHL = errors.New("IPv4 bad IHL (<5)")
errShortIPv6 = errors.New("IPv6 payload length exceeds frame")
errShortARP = errors.New("bad ARP size")
errShortTCP = errors.New("TCP offset exceeds frame")
errBadTCPOff = errors.New("TCP offset invalid")
errBadIPVersion = errors.New("bad IP version field")
errEvilPacket = errors.New("evil packet")
errZeroDstPort = errors.New("TCP zero destination port")
errZeroSrcPort = errors.New("TCP zero source port")
)
type Validator struct {
checkEvil bool
allowMultiErrs bool
accum []error
}
func (v *Validator) ResetErr() {
v.accum = v.accum[:0]
}
func (v *Validator) Err() error {
if len(v.accum) == 1 {
return v.accum[0]
} else if len(v.accum) == 0 {
return nil
}
return errors.Join(v.accum...)
}
func (v *Validator) gotErr(err error) {
if len(v.accum) != 0 && !v.allowMultiErrs {
return
}
v.accum = append(v.accum, err)
}
// 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 (efrm EthFrame) ValidateSize(v *Validator) {
sz := efrm.EtherTypeOrSize()
if sz.IsSize() && len(efrm.buf) < int(sz) {
v.gotErr(errShortEth)
}
if sz == EtherTypeVLAN && len(efrm.buf) < 18 {
v.gotErr(errShortVLAN)
}
}
// 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 ARPFrame) ValidateSize(v *Validator) {
_, hlen := afrm.Hardware()
_, ilen := afrm.Protocol()
minLen := 8 + 2*(hlen+ilen)
if len(afrm.buf) < int(minLen) {
v.gotErr(errShortARP)
}
}
// 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 (ifrm IPv4Frame) ValidateSize(v *Validator) {
ihl := ifrm.ihl()
tl := ifrm.TotalLength()
if tl < sizeHeaderIPv4 {
v.gotErr(errBadIPv4TL)
}
if int(tl) > len(ifrm.RawData()) {
v.gotErr(errShortIPv4)
}
if ihl < 5 {
v.gotErr(errBadIPv4IHL)
}
}
// ValidateExceptCRC checks for invalid frame values but does not check CRC.
func (ifrm IPv4Frame) ValidateExceptCRC(v *Validator) {
ifrm.ValidateSize(v)
flags := ifrm.Flags()
if ifrm.version() != 4 {
v.gotErr(errBadIPVersion)
}
if v.checkEvil && flags.IsEvil() {
v.gotErr(errEvilPacket)
}
}
// 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 (i6frm IPv6Frame) ValidateSize(v *Validator) {
tl := i6frm.PayloadLength()
if int(tl)+sizeHeaderIPv6 > len(i6frm.RawData()) {
v.gotErr(errShortIPv6)
}
}
// 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 (tfrm TCPFrame) ValidateSize(v *Validator) {
off := tfrm.HeaderLength()
if off < sizeHeaderTCP {
v.gotErr(errBadTCPOff)
}
if off > len(tfrm.RawData()) {
v.gotErr(errShortTCP)
}
}
func (tfrm TCPFrame) ValidateExceptCRC(v *Validator) {
tfrm.ValidateSize(v)
if tfrm.DestinationPort() == 0 {
v.gotErr(errZeroDstPort)
}
if tfrm.SourcePort() == 0 {
v.gotErr(errZeroSrcPort)
}
}
// 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 (ufrm UDPFrame) ValidateSize(v *Validator) {
ul := ufrm.Length()
if ul < sizeHeaderUDP {
v.gotErr(errBadUDPLen)
}
if int(ul) > len(ufrm.RawData()) {
v.gotErr(errShortUDP)
}
}