diff --git a/net/packet/packet.go b/net/packet/packet.go index 780349ddf..e3784a571 100644 --- a/net/packet/packet.go +++ b/net/packet/packet.go @@ -558,19 +558,42 @@ func withPort(ap netip.AddrPort, port uint16) netip.AddrPort { // Currently (2023-03-01) only TCP/UDP/ICMP over IPv4 is supported. // p is modified in place. // If p.IPProto is unknown, only the IP header checksum is updated. -// TODO(maisem): more protocols (sctp, gre, dccp) func updateV4PacketChecksums(p *Parsed, old, new netip.Addr) { + if len(p.Buffer()) < 12 { + // Not enough space for an IPv4 header. + return + } o4, n4 := old.As4(), new.As4() - updateV4Checksum(p.Buffer()[10:12], o4[:], n4[:]) // header + + // First update the checksum in the IP header. + updateV4Checksum(p.Buffer()[10:12], o4[:], n4[:]) + + // Now update the transport layer checksums, where applicable. + tr := p.Transport() switch p.IPProto { - case ipproto.UDP: - updateV4Checksum(p.Transport()[6:8], o4[:], n4[:]) + case ipproto.UDP, ipproto.DCCP: + if len(tr) < 8 { + // Not enough space for a UDP header. + return + } + updateV4Checksum(tr[6:8], o4[:], n4[:]) case ipproto.TCP: - updateV4Checksum(p.Transport()[16:18], o4[:], n4[:]) - case ipproto.ICMPv4: - // Nothing to do. + if len(tr) < 18 { + // Not enough space for a TCP header. + return + } + updateV4Checksum(tr[16:18], o4[:], n4[:]) + case ipproto.GRE: + if len(tr) < 6 { + // Not enough space for a GRE header. + return + } + if tr[0] == 1 { // checksum present + updateV4Checksum(tr[4:6], o4[:], n4[:]) + } + case ipproto.SCTP, ipproto.ICMPv4: + // No transport layer update required. } - // TODO(maisem): more protocols (sctp, gre, dccp) } // updateV4Checksum calculates and updates the checksum in the packet buffer for diff --git a/net/packet/packet_test.go b/net/packet/packet_test.go index acf878296..4bc2be77a 100644 --- a/net/packet/packet_test.go +++ b/net/packet/packet_test.go @@ -72,6 +72,19 @@ func TestHeaderChecksums(t *testing.T) { 0x45, 0x00, 0x00, 0x74, 0xe2, 0x85, 0x00, 0x00, 0x40, 0x11, 0x96, 0xb5, 0x64, 0x64, 0x64, 0x64, 0x64, 0x42, 0xd4, 0x33, 0x00, 0x35, 0xec, 0x55, 0x00, 0x60, 0xd9, 0x19, 0xed, 0xfd, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x34, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x0c, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x01, 0x6c, 0xc0, 0x15, 0xc0, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x04, 0x8e, 0xfa, 0xbd, 0xce, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, + { + name: "DCCP", + packet: []byte{ + 0x45, 0x00, 0x00, 0x28, 0x15, 0x06, 0x40, 0x00, 0x40, 0x21, 0x5f, 0x2f, 0xc0, 0xa8, 0x01, 0x1f, 0xc9, 0x0b, 0x3b, 0xad, 0x80, 0x04, 0x13, 0x89, 0x05, 0x00, 0x08, 0xdb, 0x01, 0x00, 0x00, 0x04, 0x29, 0x01, 0x6d, 0xdc, 0x00, 0x00, 0x00, 0x00, + }, + }, + { + name: "SCTP", + packet: []byte{ + 0x45, 0x00, 0x00, 0x30, 0x09, 0xd9, 0x40, 0x00, 0xff, 0x84, 0x50, 0xe2, 0x0a, 0x1c, 0x06, 0x2c, 0x0a, 0x1c, 0x06, 0x2b, 0x0b, 0x80, 0x40, 0x00, 0x21, 0x44, 0x15, 0x23, 0x2b, 0xf2, 0x02, 0x4e, 0x03, 0x00, 0x00, 0x10, 0x28, 0x02, 0x43, 0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, + // TODO(maisem): add test for GRE. } var p Parsed for _, tt := range tests { diff --git a/types/ipproto/ipproto.go b/types/ipproto/ipproto.go index 66258f1ab..a6b1e0c48 100644 --- a/types/ipproto/ipproto.go +++ b/types/ipproto/ipproto.go @@ -25,6 +25,8 @@ const ( ICMPv6 Proto = 0x3a TCP Proto = 0x06 UDP Proto = 0x11 + DCCP Proto = 0x21 + GRE Proto = 0x2f SCTP Proto = 0x84 // TSMP is the Tailscale Message Protocol (our ICMP-ish @@ -67,6 +69,10 @@ func (p Proto) String() string { return "SCTP" case TSMP: return "TSMP" + case GRE: + return "GRE" + case DCCP: + return "DCCP" default: return fmt.Sprintf("IPProto-%d", int(p)) }