mirror of
https://github.com/wader/fq.git
synced 2024-12-23 05:13:30 +03:00
119 lines
3.4 KiB
Go
119 lines
3.4 KiB
Go
package inet
|
|
|
|
// https://en.wikipedia.org/wiki/Transmission_Control_Protocol
|
|
|
|
import (
|
|
"github.com/wader/fq/format"
|
|
"github.com/wader/fq/pkg/decode"
|
|
"github.com/wader/fq/pkg/interp"
|
|
"github.com/wader/fq/pkg/scalar"
|
|
)
|
|
|
|
func init() {
|
|
interp.RegisterFormat(decode.Format{
|
|
Name: format.TCP_SEGMENT,
|
|
Description: "Transmission control protocol segment",
|
|
Groups: []string{format.IP_PACKET},
|
|
DecodeFn: decodeTCP,
|
|
})
|
|
}
|
|
|
|
const (
|
|
tcpOptionEnd = 0
|
|
tcpOptionNop = 1
|
|
tcpOptionMSS = 2
|
|
tcpOptionWinscale = 3
|
|
tcpOptionSackPermitted = 4
|
|
tcpOptionSack = 5
|
|
tcpOptionTimestamp = 8
|
|
)
|
|
|
|
var tcpOptionsMap = scalar.UintMap{
|
|
tcpOptionEnd: {Sym: "end", Description: "End of options list"},
|
|
tcpOptionNop: {Sym: "nop", Description: "No operation"},
|
|
tcpOptionMSS: {Sym: "mss", Description: "Maximum segment size"},
|
|
tcpOptionWinscale: {Sym: "winscale", Description: "Window scale"},
|
|
tcpOptionSackPermitted: {Sym: "sack_permitted", Description: "Selective Acknowledgement permitted"},
|
|
tcpOptionSack: {Sym: "sack", Description: "Selective Acknowledgement"},
|
|
tcpOptionTimestamp: {Sym: "timestamp", Description: "Timestamp and echo of previous timestamp"},
|
|
}
|
|
|
|
func decodeTCP(d *decode.D, in any) any {
|
|
if ipi, ok := in.(format.IPPacketIn); ok && ipi.Protocol != format.IPv4ProtocolTCP {
|
|
d.Fatalf("incorrect protocol %d", ipi.Protocol)
|
|
}
|
|
|
|
d.FieldU16("source_port", format.TCPPortMap)
|
|
d.FieldU16("destination_port", format.TCPPortMap)
|
|
d.FieldU32("sequence_number")
|
|
d.FieldU32("acknowledgment_number")
|
|
dataOffset := d.FieldU4("data_offset")
|
|
d.FieldU3("reserved")
|
|
d.FieldBool("ns")
|
|
d.FieldBool("cwr")
|
|
d.FieldBool("ece")
|
|
d.FieldBool("urg")
|
|
d.FieldBool("ack")
|
|
d.FieldBool("psh")
|
|
d.FieldBool("rst")
|
|
d.FieldBool("syn")
|
|
d.FieldBool("fin")
|
|
d.FieldU16("window_size")
|
|
// checksumStart := d.Pos()
|
|
d.FieldU16("checksum", scalar.UintHex)
|
|
// checksumEnd := d.Pos()
|
|
d.FieldU16("urgent_pointer")
|
|
optionsLen := (int64(dataOffset) - 5) * 8 * 4
|
|
if optionsLen > 0 {
|
|
d.FramedFn(optionsLen, func(d *decode.D) {
|
|
d.FieldArray("options", func(d *decode.D) {
|
|
for !d.End() {
|
|
d.FieldStruct("option", func(d *decode.D) {
|
|
kind := d.FieldU8("kind", tcpOptionsMap)
|
|
switch kind {
|
|
case tcpOptionEnd, tcpOptionNop:
|
|
// has no length or data
|
|
default:
|
|
l := d.FieldU8("length")
|
|
switch kind {
|
|
case tcpOptionMSS:
|
|
d.FieldU16("size")
|
|
case tcpOptionWinscale:
|
|
d.FieldU8("shift")
|
|
case tcpOptionSackPermitted:
|
|
// none
|
|
case tcpOptionSack:
|
|
d.FramedFn((int64(l-2))*8, func(d *decode.D) {
|
|
d.FieldArray("blocks", func(d *decode.D) {
|
|
for !d.End() {
|
|
d.FieldStruct("block", func(d *decode.D) {
|
|
d.FieldU32("left_edge")
|
|
d.FieldU32("right_edge")
|
|
})
|
|
}
|
|
})
|
|
})
|
|
case tcpOptionTimestamp:
|
|
d.FieldU32("value")
|
|
d.FieldU32("echo_reply")
|
|
default:
|
|
d.FieldRawLen("data", (int64(l-2))*8)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// TODO: need to pass ipv4 pseudo header somehow
|
|
// tcpChecksum := &checksum.IPv4{}
|
|
// d.MustCopy(tcpChecksum, d.BitBufRange(0, checksumStart))
|
|
// d.MustCopy(tcpChecksum, d.BitBufRange(checksumEnd, d.Len()-checksumEnd))
|
|
// _ = d.FieldMustGet("checksum").TryScalarFn(d.UintValidateBytes(tcpChecksum.Sum(nil)), scalar.Hex)
|
|
|
|
d.FieldRawLen("payload", d.BitsLeft())
|
|
|
|
return nil
|
|
}
|