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 } }