net/packet: implement methods for rewriting v6 addresses

Implements the ability for the address-rewriting code to support rewriting IPv6 addresses.

Specifically, UpdateSrcAddr & UpdateDstAddr.

Signed-off-by: Tom DNetto <tom@tailscale.com>
Updates https://github.com/tailscale/corp/issues/11202
This commit is contained in:
Tom DNetto
2023-09-22 13:15:49 -07:00
committed by Tom
parent c26d91d6bd
commit 656a77ab4e
5 changed files with 213 additions and 22 deletions
+106 -1
View File
@@ -13,6 +13,9 @@ import (
"testing"
"unicode"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
"gvisor.dev/gvisor/pkg/tcpip/header"
"tailscale.com/tstest"
"tailscale.com/types/ipproto"
"tailscale.com/util/must"
@@ -45,7 +48,7 @@ func fullHeaderChecksumV4(b []byte) uint16 {
return ^uint16(s)
}
func TestHeaderChecksums(t *testing.T) {
func TestHeaderChecksumsV4(t *testing.T) {
// This is not a good enough test, because it doesn't
// check the various packet types or the many edge cases
// of the checksum algorithm. But it's a start.
@@ -109,6 +112,108 @@ func TestHeaderChecksums(t *testing.T) {
}
}
func TestNatChecksumsV6UDP(t *testing.T) {
a1, a2 := netip.MustParseAddr("a::1"), netip.MustParseAddr("b::1")
// Make a fake UDP packet with 32 bytes of zeros as the datagram payload.
b := header.IPv6(make([]byte, header.IPv6MinimumSize+header.UDPMinimumSize+32))
b.Encode(&header.IPv6Fields{
PayloadLength: header.UDPMinimumSize + 32,
TransportProtocol: header.UDPProtocolNumber,
HopLimit: 16,
SrcAddr: tcpip.AddrFrom16Slice(a1.AsSlice()),
DstAddr: tcpip.AddrFrom16Slice(a2.AsSlice()),
})
udp := header.UDP(b[header.IPv6MinimumSize:])
udp.Encode(&header.UDPFields{
SrcPort: 42,
DstPort: 43,
Length: header.UDPMinimumSize + 32,
})
xsum := header.PseudoHeaderChecksum(
header.UDPProtocolNumber,
tcpip.AddrFrom16Slice(a1.AsSlice()),
tcpip.AddrFrom16Slice(a2.AsSlice()),
uint16(header.UDPMinimumSize+32),
)
xsum = checksum.Checksum(b.Payload()[header.UDPMinimumSize:], xsum)
udp.SetChecksum(^udp.CalculateChecksum(xsum))
if !udp.IsChecksumValid(tcpip.AddrFrom16Slice(a1.AsSlice()), tcpip.AddrFrom16Slice(a2.AsSlice()), checksum.Checksum(b.Payload()[header.UDPMinimumSize:], 0)) {
t.Fatal("test broken; initial packet has incorrect checksum")
}
// Parse the packet.
var p Parsed
p.Decode(b)
t.Log(p.String())
// Update the source address of the packet to be the same as the dest.
p.UpdateSrcAddr(a2)
if !udp.IsChecksumValid(tcpip.AddrFrom16Slice(a2.AsSlice()), tcpip.AddrFrom16Slice(a2.AsSlice()), checksum.Checksum(b.Payload()[header.UDPMinimumSize:], 0)) {
t.Fatal("incorrect checksum after updating source address")
}
// Update the dest address of the packet to be the original source address.
p.UpdateDstAddr(a1)
if !udp.IsChecksumValid(tcpip.AddrFrom16Slice(a2.AsSlice()), tcpip.AddrFrom16Slice(a1.AsSlice()), checksum.Checksum(b.Payload()[header.UDPMinimumSize:], 0)) {
t.Fatal("incorrect checksum after updating destination address")
}
}
func TestNatChecksumsV6TCP(t *testing.T) {
a1, a2 := netip.MustParseAddr("a::1"), netip.MustParseAddr("b::1")
// Make a fake TCP packet with no payload.
b := header.IPv6(make([]byte, header.IPv6MinimumSize+header.TCPMinimumSize))
b.Encode(&header.IPv6Fields{
PayloadLength: header.TCPMinimumSize,
TransportProtocol: header.TCPProtocolNumber,
HopLimit: 16,
SrcAddr: tcpip.AddrFrom16Slice(a1.AsSlice()),
DstAddr: tcpip.AddrFrom16Slice(a2.AsSlice()),
})
tcp := header.TCP(b[header.IPv6MinimumSize:])
tcp.Encode(&header.TCPFields{
SrcPort: 42,
DstPort: 43,
SeqNum: 1,
AckNum: 2,
DataOffset: header.TCPMinimumSize,
Flags: 3,
WindowSize: 4,
Checksum: 0,
UrgentPointer: 5,
})
xsum := header.PseudoHeaderChecksum(
header.TCPProtocolNumber,
tcpip.AddrFrom16Slice(a1.AsSlice()),
tcpip.AddrFrom16Slice(a2.AsSlice()),
uint16(header.TCPMinimumSize),
)
tcp.SetChecksum(^tcp.CalculateChecksum(xsum))
if !tcp.IsChecksumValid(tcpip.AddrFrom16Slice(a1.AsSlice()), tcpip.AddrFrom16Slice(a2.AsSlice()), 0, 0) {
t.Fatal("test broken; initial packet has incorrect checksum")
}
// Parse the packet.
var p Parsed
p.Decode(b)
t.Log(p.String())
// Update the source address of the packet to be the same as the dest.
p.UpdateSrcAddr(a2)
if !tcp.IsChecksumValid(tcpip.AddrFrom16Slice(a2.AsSlice()), tcpip.AddrFrom16Slice(a2.AsSlice()), 0, 0) {
t.Fatal("incorrect checksum after updating source address")
}
// Update the dest address of the packet to be the original source address.
p.UpdateDstAddr(a1)
if !tcp.IsChecksumValid(tcpip.AddrFrom16Slice(a2.AsSlice()), tcpip.AddrFrom16Slice(a1.AsSlice()), 0, 0) {
t.Fatal("incorrect checksum after updating destination address")
}
}
func mustIPPort(s string) netip.AddrPort {
ipp, err := netip.ParseAddrPort(s)
if err != nil {