From a1848bb584fe37ecf3266d5bc251a9af26ef1b7a Mon Sep 17 00:00:00 2001 From: Aaron Turner Date: Fri, 28 Apr 2023 18:29:52 -0700 Subject: [PATCH] Add SerializeOptions.IPLengthHostByteOrder In order to use gopacket/layers to generate packets to be used with raw sockets at Layer 3, we need to be able to write the IP.Length in host byte order for certain (mostly *BSD) operating systems or the kernel will silently drop the packet when sending. --- layers/endian.go | 23 +++++++++++++++++++++++ layers/ip4.go | 8 +++++++- layers/ip6.go | 8 +++++++- writer.go | 21 +++++++++++++-------- 4 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 layers/endian.go diff --git a/layers/endian.go b/layers/endian.go new file mode 100644 index 000000000..76c4a6b90 --- /dev/null +++ b/layers/endian.go @@ -0,0 +1,23 @@ +package layers + +import ( + "encoding/binary" + "unsafe" +) + +var endian binary.ByteOrder = _endian() + +// _endian returns the binary.ByteOrder as defined for the host byte order +func _endian() binary.ByteOrder { + buf := [2]byte{} + *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) + + switch buf { + case [2]byte{0xCD, 0xAB}: + return binary.LittleEndian + case [2]byte{0xAB, 0xCD}: + return binary.BigEndian + default: + panic("Could not determine native endianness.") + } +} diff --git a/layers/ip4.go b/layers/ip4.go index 2b3c0c6bf..fda23581f 100644 --- a/layers/ip4.go +++ b/layers/ip4.go @@ -112,7 +112,13 @@ func (ip *IPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeO } bytes[0] = (ip.Version << 4) | ip.IHL bytes[1] = ip.TOS - binary.BigEndian.PutUint16(bytes[2:], ip.Length) + + if opts.IPLengthHostByteOrder { + endian.PutUint16(bytes[2:], ip.Length) + } else { + binary.BigEndian.PutUint16(bytes[2:], ip.Length) + } + binary.BigEndian.PutUint16(bytes[4:], ip.Id) binary.BigEndian.PutUint16(bytes[6:], ip.flagsfrags()) bytes[8] = ip.TTL diff --git a/layers/ip6.go b/layers/ip6.go index 87b9d33d5..302dad3bb 100644 --- a/layers/ip6.go +++ b/layers/ip6.go @@ -206,7 +206,13 @@ func (ipv6 *IPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.Serializ ipv6.Length = uint16(pLen) } } - binary.BigEndian.PutUint16(bytes[4:], ipv6.Length) + + if opts.IPLengthHostByteOrder { + endian.PutUint16(bytes[4:], ipv6.Length) + } else { + binary.BigEndian.PutUint16(bytes[4:], ipv6.Length) + } + bytes[6] = byte(ipv6.NextHeader) bytes[7] = byte(ipv6.HopLimit) if err := ipv6.AddressTo16(); err != nil { diff --git a/writer.go b/writer.go index 5d303dc4a..a57199ec3 100644 --- a/writer.go +++ b/writer.go @@ -46,6 +46,10 @@ type SerializeOptions struct { // FixLengths determines whether, during serialization, layers should fix // the values for any length field that depends on the payload. FixLengths bool + // IPLengthHostByteOrder determines whether, during serialization, the network + // layer containing the IPv4/v6 header length field should be serialized in + // host byte order. + IPLengthHostByteOrder bool // ComputeChecksums determines whether, during serialization, layers // should recompute checksums based on their payloads. ComputeChecksums bool @@ -69,9 +73,9 @@ type SerializeOptions struct { // byte slices returned by any previous Bytes() call (the same buffer is // reused). // -// 1) Reusing a write buffer is generally much faster than creating a new one, +// 1. Reusing a write buffer is generally much faster than creating a new one, // and with the default implementation it avoids additional memory allocations. -// 2) If a byte slice from a previous Bytes() call will continue to be used, +// 2. If a byte slice from a previous Bytes() call will continue to be used, // it's better to create a new SerializeBuffer. // // The Clear method is specifically designed to minimize memory allocations for @@ -197,12 +201,13 @@ func (w *serializeBuffer) PushLayer(l LayerType) { // invalidates all slices previously returned by w.Bytes() // // Example: -// buf := gopacket.NewSerializeBuffer() -// opts := gopacket.SerializeOptions{} -// gopacket.SerializeLayers(buf, opts, a, b, c) -// firstPayload := buf.Bytes() // contains byte representation of a(b(c)) -// gopacket.SerializeLayers(buf, opts, d, e, f) -// secondPayload := buf.Bytes() // contains byte representation of d(e(f)). firstPayload is now invalidated, since the SerializeLayers call Clears buf. +// +// buf := gopacket.NewSerializeBuffer() +// opts := gopacket.SerializeOptions{} +// gopacket.SerializeLayers(buf, opts, a, b, c) +// firstPayload := buf.Bytes() // contains byte representation of a(b(c)) +// gopacket.SerializeLayers(buf, opts, d, e, f) +// secondPayload := buf.Bytes() // contains byte representation of d(e(f)). firstPayload is now invalidated, since the SerializeLayers call Clears buf. func SerializeLayers(w SerializeBuffer, opts SerializeOptions, layers ...SerializableLayer) error { w.Clear() for i := len(layers) - 1; i >= 0; i-- {