Documentation
¶
Overview ¶
Package packet builds the Ethernet, 802.1Q, IPv4, UDP and TCP headers Wireblast transmits.
The design goal is a hot path that allocates nothing and recomputes nothing it does not have to. A Template is built once per queue; changing a flow rewrites only the fields that differ and repairs the checksums incrementally (RFC 1624), never by rescanning the header.
Everything here is pure: no sockets, no system calls, no globals. The layout is computed from an offset rather than hardcoded constants, so an IPv6 builder can be added later without disturbing the mutators.
Index ¶
- Constants
- func Checksum(b []byte) uint16
- func PseudoHeaderSum(srcIP, dstIP [4]byte, proto uint8, l4Len int) uint32
- func PseudoHeaderSum6(srcIP, dstIP [16]byte, nextHdr uint8, l4Len int) uint32
- func ReplaceU16(csum, old, new uint16) uint16
- func ReplaceU32(csum uint16, old, new [4]byte) uint16
- type Layout
- type Spec
- type Template
- func (t *Template) Bytes() []byte
- func (t *Template) Layout() Layout
- func (t *Template) Len() int
- func (t *Template) SetDstIP(ip netip.Addr)
- func (t *Template) SetDstPort(p uint16)
- func (t *Template) SetFrameLen(n int) error
- func (t *Template) SetSrcIP(ip netip.Addr)
- func (t *Template) SetSrcPort(p uint16)
- func (t *Template) WriteTo(dst []byte) int
Constants ¶
const ( ProtoTCP = 6 ProtoUDP = 17 )
IP protocol numbers Wireblast generates.
const ( EtherTypeIPv4 = 0x0800 EtherTypeIPv6 = 0x86DD EtherTypeVLAN = 0x8100 )
EtherTypes used by the builders.
const ( EthHeaderLen = 14 VLANTagLen = 4 IPv4HeaderLen = 20 IPv6HeaderLen = 40 UDPHeaderLen = 8 TCPHeaderLen = 20 )
Header sizes, in bytes.
Variables ¶
This section is empty.
Functions ¶
func Checksum ¶
Checksum computes the 16-bit one's-complement checksum of b, as used by IPv4, UDP and TCP. An odd-length buffer is padded with a zero byte.
func PseudoHeaderSum ¶
PseudoHeaderSum returns the unfolded partial sum of the IPv4 pseudo-header used by UDP and TCP: source address, destination address, protocol number and L4 length.
func PseudoHeaderSum6 ¶ added in v0.2.0
PseudoHeaderSum6 returns the unfolded partial sum of the IPv6 pseudo-header used by UDP and TCP (RFC 8200 section 8.1): the 128-bit source and destination addresses, a 32-bit upper-layer packet length, and the next-header value (the three bytes before it are zero and contribute nothing).
func ReplaceU16 ¶
ReplaceU16 returns the checksum csum updated for a 16-bit header field whose value changed from old to new, without rescanning the packet.
This is RFC 1624 equation 3, HC' = ~(~HC + ~m + m'), which is correct even when the intermediate sum would otherwise produce the negative-zero result that the naive RFC 1141 formula gets wrong.
func ReplaceU32 ¶
ReplaceU32 returns csum updated for a 32-bit field (an IPv4 address, say) that changed from old to new. It is two 16-bit replacements.
Types ¶
type Layout ¶
type Layout struct {
HasVLAN bool
HasIP bool
Is6 bool // IPv6 rather than IPv4
L3Off int // start of the IP header (or of the raw payload)
L4Off int // start of the UDP/TCP header
SrcIPOff int
DstIPOff int
IPCksumOff int
IPLenOff int
SrcPortOff int
DstPortOff int
L4LenOff int // UDP length field; -1 for TCP
L4CksumOff int // -1 when there is no L4 checksum to maintain
HeaderLen int // total header bytes before the payload
Proto uint8 // 0 for raw Ethernet
}
Layout records where each mutable field sits in a built frame. Offsets shift by VLANTagLen when a tag is present, which is why nothing downstream hardcodes them.
type Spec ¶
type Spec struct {
SrcMAC, DstMAC [6]byte
VLAN uint16 // 0 means untagged
PCP uint8 // 802.1p priority, 0-7
SrcIP, DstIP netip.Addr // IPv4; ignored when EtherType is set
Proto uint8 // ProtoUDP or ProtoTCP
SrcPort, DstPort uint16
TTL uint8
// EtherType, when non-zero, builds a raw Ethernet frame with this type
// and no IP headers at all.
EtherType uint16
FrameLen int // bytes written for each packet
Cap int // buffer capacity; >= FrameLen, for later SetFrameLen calls
PayloadByte byte
}
Spec describes the frame to build. FrameLen and Cap are in bytes actually written to the wire, i.e. excluding the FCS the NIC appends.
type Template ¶
type Template struct {
// contains filtered or unexported fields
}
Template is a prebuilt frame plus the bookkeeping needed to change a flow field and repair the checksums without rescanning the packet.
A Template is owned by exactly one transmit goroutine. It is not safe for concurrent use, and does not need to be: each queue builds its own.
func (*Template) Bytes ¶
Bytes returns the current frame. The slice aliases the Template's buffer and is only valid until the next mutation; do not retain it.
func (*Template) SetDstPort ¶
SetDstPort changes the L4 destination port.
func (*Template) SetFrameLen ¶
SetFrameLen changes how many bytes the next packet occupies, updating the IPv4 total length and the UDP length. This is how IMIX varies frame size without rebuilding anything.
For UDP the update is incremental and cheap. For TCP the length is part of the checksummed pseudo-header and changes which payload bytes are covered, so the L4 checksum is recomputed in full — TCP modes are fixed-size, so that never happens on a hot path.
func (*Template) SetSrcIP ¶
SetSrcIP changes the source address, repairing the IPv4 header checksum (IPv6 has none) and, wherever an L4 checksum is maintained, its pseudo-header part. It is a no-op for a non-IP template. The address family must match the one the template was built for.
func (*Template) SetSrcPort ¶
SetSrcPort changes the L4 source port. The IPv4 header checksum does not cover ports, so only the L4 checksum needs repairing — and for UDP, whose checksum is left at zero, not even that.