Tcp rst handling (#40)

* claude suggests a way forward

* add timing to capture printer

* add pcap.Flags

* fix ICMP CRC calculation and add test

* bugfix: still send data on half-close state(close-wait)

* fix pcap test
This commit is contained in:
Pat Whittingslow
2026-02-23 13:50:13 +01:00
committed by GitHub
parent 08423d0dba
commit 7d323aae19
10 changed files with 386 additions and 46 deletions
+21 -14
View File
@@ -603,10 +603,21 @@ type FrameField struct {
FrameBitOffset int
BitLength int
SubFields []FrameField
RightAligned bool
Legacy bool
Flags Flags
}
// Flags stores frame field interpretation bits.
type Flags uint32
const (
FlagRightAligned Flags = 1 << iota
FlagLegacy
)
func (ff Flags) IsLegacy() bool { return ff&FlagLegacy != 0 }
func (ff Flags) IsRightAligned() bool { return ff&FlagRightAligned != 0 }
type Frame struct {
Protocol any
Fields []FrameField
@@ -653,7 +664,7 @@ func (frm Frame) FieldAsUint(fieldIdx int, pkt []byte) (uint64, error) {
return badUint64, errors.New("invalid field index")
}
field := frm.Fields[fieldIdx]
return fieldAsUint(pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.RightAligned)
return fieldAsUint(pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.Flags.IsRightAligned())
}
// AppendField appends the binary on-the-wire representation of the field and aligns the field so it starts at the first bit of appended data.
@@ -662,7 +673,7 @@ func (frm Frame) AppendField(dst []byte, fieldIdx int, pkt []byte) ([]byte, erro
return dst, errors.New("invalid field index")
}
field := frm.Fields[fieldIdx]
return appendField(dst, pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.RightAligned)
return appendField(dst, pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.Flags.IsRightAligned())
}
func fieldAsUint(pkt []byte, fieldBitStart, bitlen int, rightAligned bool) (uint64, error) {
@@ -860,14 +871,14 @@ var baseIPv6Fields = [...]FrameField{
Class: FieldClassFlags,
FrameBitOffset: 4,
BitLength: 1 * octet,
RightAligned: true,
Flags: FlagRightAligned,
},
{
Name: "Flow Label",
Class: FieldClassID,
FrameBitOffset: 12,
BitLength: 20,
RightAligned: true,
Flags: FlagRightAligned,
},
{
Name: "Total Length",
@@ -928,11 +939,6 @@ var baseIPv4Fields = [...]FrameField{
FrameBitOffset: 4 * octet,
BitLength: 2 * octet,
},
{
Class: FieldClassID,
FrameBitOffset: 4 * octet,
BitLength: 2 * octet,
},
{
Class: FieldClassFlags,
FrameBitOffset: 6 * octet,
@@ -1000,7 +1006,7 @@ var baseTCPFields = [...]FrameField{
Class: FieldClassFlags,
FrameBitOffset: 12*octet + 4,
BitLength: 12,
RightAligned: true,
Flags: FlagRightAligned,
},
{
Name: "Window",
@@ -1018,6 +1024,7 @@ var baseTCPFields = [...]FrameField{
Class: 0,
FrameBitOffset: 18 * octet,
BitLength: 2 * octet,
Flags: FlagLegacy,
},
}
@@ -1199,14 +1206,14 @@ var baseDHCPv4Fields = [...]FrameField{
Class: FieldClassBinaryText,
FrameBitOffset: (28 + 6) * octet, // Part of Client Hardware Address(16 bytes) but unused.
BitLength: 10 * octet,
Legacy: true,
Flags: FlagLegacy,
},
{
Name: "BOOTP",
Class: FieldClassBinaryText,
FrameBitOffset: (28 + 16) * octet,
BitLength: (dhcpv4.OptionsOffset - (28 + 16)) * octet,
Legacy: true,
Flags: FlagLegacy,
},
}
+7 -7
View File
@@ -474,15 +474,15 @@ func ExampleFormatter_dhcp() {
fmt.Println(string(out))
// Output:
// Ethernet len=14; destination=ff:ff:ff:ff:ff:ff; source=de:ad:be:ef:ca:fe; protocol=0x0800
// IPv4 len=20; version=0x04; (Header Length)=5; (Type of Service)=0x00; (Total Length)=312; identification=0x6043; identification=0x6043; flags=0x4000; (Time to live)=0x40; protocol=0x11; checksum=0xd972; source=0.0.0.0; destination=255.255.255.255
// IPv4 len=20; version=0x04; (Header Length)=5; (Type of Service)=0x00; (Total Length)=312; identification=0x6043; flags=0x4000; (Time to live)=0x40; protocol=0x11; checksum=0xd972; source=0.0.0.0; destination=255.255.255.255
// UDP [RFC768] len=8; (Source port)=68; (Destination port)=67; size=292; checksum=0x0000
// DHCPv4 len=240; op=1; (Hardware Address Type)=0x01; (Hardware Address Length)=6; Hops=0x00; (Transaction ID)=0xdeadbeef; (Start Time)=0x0001; Flags=0x0000; (Client Address)=0.0.0.0; (Offered Address)=0.0.0.0; (Server Next Address)=255.255.255.255; (Relay Agent Address)=0.0.0.0; (Client Hardware Address)=de:ad:be:ef:ca:fe; options
// (DHCP message type.)=1
// (Parameter request list)=0x0102031a1c060f2a
// (DHCP maximum message size)=558
// (Requested IP address)=192.168.1.100
// (Client identifier)="lneto-test"
// (Hostname string)="myhost"
// (DHCP message type.)=1
// (Parameter request list)=0x0102031a1c060f2a
// (DHCP maximum message size)=558
// (Requested IP address)=192.168.1.100
// (Client identifier)="lneto-test"
// (Hostname string)="myhost"
}
func writeOpt(dst []byte, opt dhcpv4.OptNum, data ...byte) int {
dst[0] = byte(opt)
+4 -4
View File
@@ -68,7 +68,7 @@ func (f *Formatter) FormatFrame(dst []byte, frm Frame, pkt []byte) (_ []byte, er
if field.Class == FieldClassFlags && frm.Protocol == lneto.IPProtoTCP {
// TCP flags pretty print special case.
dst = append(dst, "flags="...)
v, err := fieldAsUint(pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.RightAligned)
v, err := fieldAsUint(pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.Flags.IsRightAligned())
if err != nil {
return dst, err
}
@@ -95,7 +95,7 @@ func (f *Formatter) FormatFrame(dst []byte, frm Frame, pkt []byte) (_ []byte, er
func (f *Formatter) filterField(field FrameField) bool {
return f.FilterClasses != nil && !slices.Contains(f.FilterClasses, field.Class) ||
(field.Legacy && !f.DisableLegacyFilter)
(field.Flags.IsLegacy() && !f.DisableLegacyFilter)
}
func (f *Formatter) FormatField(dst []byte, pktStartOff int, field FrameField, pkt []byte) (_ []byte, err error) {
@@ -133,7 +133,7 @@ func (f *Formatter) formatField(dst []byte, pktStartOff int, field FrameField, p
dst = append(dst, '=')
f.mubuf.Lock()
defer f.mubuf.Unlock()
f.buf, err = appendField(f.buf[:0], pkt, field.FrameBitOffset+pktStartOff, field.BitLength, field.RightAligned)
f.buf, err = appendField(f.buf[:0], pkt, field.FrameBitOffset+pktStartOff, field.BitLength, field.Flags.IsRightAligned())
if err != nil {
return dst, err
}
@@ -158,7 +158,7 @@ func (f *Formatter) formatField(dst []byte, pktStartOff int, field FrameField, p
case FieldClassDst, FieldClassSrc, FieldClassSize, FieldClassAddress, FieldClassOperation:
// IP, MAC addresses and ports.
if field.BitLength <= 16 {
v, err := fieldAsUint(pkt, fieldBitStart, field.BitLength, field.RightAligned)
v, err := fieldAsUint(pkt, fieldBitStart, field.BitLength, field.Flags.IsRightAligned())
if err != nil {
return dst, err
}
+3 -4
View File
@@ -98,7 +98,7 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
totalLen := ifrm.TotalLength()
proto := ifrm.Protocol()
if proto == lneto.IPProtoICMP {
return sb.recvicmp(ifrm.RawData(), ifrm.HeaderLength())
return sb.recvicmp(ifrm.Payload())
}
node := sb.handlers.nodeByProto(uint16(proto))
// nodeIdx := getNodeByProto(sb.handlers, uint16(proto))
@@ -205,10 +205,9 @@ func (sb *StackIP) Register(h StackNode) error {
return sb.handlers.registerByPortProto(nodeFromStackNode(h, h.LocalPort(), proto, nil))
}
func (sb *StackIP) recvicmp(carrierData []byte, offset int) error {
frameData := carrierData[offset:]
func (sb *StackIP) recvicmp(icmpData []byte) error {
var crc lneto.CRC791
if crc.PayloadSum16(frameData) != 0 {
if crc.PayloadSum16(icmpData) != 0 {
return errors.New("ICMP CRC mismatch")
}
return nil