summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGhanan Gowripalan <ghanan@google.com>2021-10-28 19:33:32 -0700
committergVisor bot <gvisor-bot@google.com>2021-10-28 19:36:02 -0700
commit1953d2ad28d405a3ab028feba7b6fca18339e9be (patch)
treee8a45f77a46b20bc4b6d847ff51ce1d91d338a10
parentca55c18a31789b8f2541d5d3b90e2af012d3e6ef (diff)
NAT ICMPv6 errors
...so a NAT-ed connection's socket can handle ICMP errors. Updates #5916. PiperOrigin-RevId: 406270868
-rw-r--r--pkg/tcpip/stack/conntrack.go156
-rw-r--r--pkg/tcpip/tests/integration/iptables_test.go155
2 files changed, 273 insertions, 38 deletions
diff --git a/pkg/tcpip/stack/conntrack.go b/pkg/tcpip/stack/conntrack.go
index f5e990f03..c489506bb 100644
--- a/pkg/tcpip/stack/conntrack.go
+++ b/pkg/tcpip/stack/conntrack.go
@@ -209,6 +209,22 @@ type bucket struct {
tuples tupleList
}
+func getEmbeddedNetAndTransHeaders(pkt *PacketBuffer, netHdrLength int, netHdrFunc func([]byte) header.Network) (header.Network, header.ChecksummableTransport, bool) {
+ switch pkt.tuple.id().transProto {
+ case header.TCPProtocolNumber:
+ if netAndTransHeader, ok := pkt.Data().PullUp(netHdrLength + header.TCPMinimumSize); ok {
+ netHeader := netHdrFunc(netAndTransHeader)
+ return netHeader, header.TCP(netHeader.Payload()), true
+ }
+ case header.UDPProtocolNumber:
+ if netAndTransHeader, ok := pkt.Data().PullUp(netHdrLength + header.UDPMinimumSize); ok {
+ netHeader := netHdrFunc(netAndTransHeader)
+ return netHeader, header.UDP(netHeader.Payload()), true
+ }
+ }
+ return nil, nil, false
+}
+
func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.ChecksummableTransport, isICMPError bool, ok bool) {
switch pkt.TransportProtocolNumber {
case header.TCPProtocolNumber:
@@ -225,29 +241,31 @@ func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Check
panic(fmt.Sprintf("should have a valid IPv4 packet; only have %d bytes, want at least %d bytes", pkt.Data().Size(), header.IPv4MinimumSize))
}
- ipv4 := header.IPv4(h)
- if ipv4.HeaderLength() > header.IPv4MinimumSize {
+ if header.IPv4(h).HeaderLength() > header.IPv4MinimumSize {
// TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
panic("should have dropped packets with IPv4 options")
}
- switch pkt.tuple.id().transProto {
- case header.TCPProtocolNumber:
- // TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
- netAndTransHeader, ok := pkt.Data().PullUp(header.IPv4MinimumSize + header.TCPMinimumSize)
- if !ok {
- return nil, nil, false, false
- }
- netHeader := header.IPv4(netAndTransHeader)
- return netHeader, header.TCP(netHeader.Payload()), true, true
- case header.UDPProtocolNumber:
- // TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
- netAndTransHeader, ok := pkt.Data().PullUp(header.IPv4MinimumSize + header.UDPMinimumSize)
- if !ok {
- return nil, nil, false, false
- }
- netHeader := header.IPv4(netAndTransHeader)
- return netHeader, header.UDP(netHeader.Payload()), true, true
+ if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, header.IPv4MinimumSize, func(b []byte) header.Network { return header.IPv4(b) }); ok {
+ return netHdr, transHdr, true, true
+ }
+ case header.ICMPv6ProtocolNumber:
+ h, ok := pkt.Data().PullUp(header.IPv6MinimumSize)
+ if !ok {
+ panic(fmt.Sprintf("should have a valid IPv6 packet; only have %d bytes, want at least %d bytes", pkt.Data().Size(), header.IPv6MinimumSize))
+ }
+
+ // We do not support extension headers in ICMP errors so the next header
+ // in the IPv6 packet should be a tracked protocol if we reach this point.
+ //
+ // TODO(https://gvisor.dev/issue/6789): Support extension headers.
+ transProto := pkt.tuple.id().transProto
+ if got := header.IPv6(h).TransportProtocol(); got != transProto {
+ panic(fmt.Sprintf("got TransportProtocol() = %d, want = %d", got, transProto))
+ }
+
+ if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, header.IPv6MinimumSize, func(b []byte) header.Network { return header.IPv6(b) }); ok {
+ return netHdr, transHdr, true, true
}
}
@@ -265,15 +283,37 @@ func getTupleIDForRegularPacket(netHdr header.Network, netProto tcpip.NetworkPro
}
}
-func getTupleIDForPacketInICMPError(netHdr header.Network, netProto tcpip.NetworkProtocolNumber, transHdr header.Transport, transProto tcpip.TransportProtocolNumber) tupleID {
- return tupleID{
- srcAddr: netHdr.DestinationAddress(),
- srcPort: transHdr.DestinationPort(),
- dstAddr: netHdr.SourceAddress(),
- dstPort: transHdr.SourcePort(),
- transProto: transProto,
- netProto: netProto,
+func getTupleIDForPacketInICMPError(pkt *PacketBuffer, netHdrFunc func([]byte) header.Network, netProto tcpip.NetworkProtocolNumber, netLen int, transProto tcpip.TransportProtocolNumber) (tupleID, bool) {
+ switch transProto {
+ case header.TCPProtocolNumber:
+ if netAndTransHeader, ok := pkt.Data().PullUp(netLen + header.TCPMinimumSize); ok {
+ netHdr := netHdrFunc(netAndTransHeader)
+ transHdr := header.TCP(netHdr.Payload())
+ return tupleID{
+ srcAddr: netHdr.DestinationAddress(),
+ srcPort: transHdr.DestinationPort(),
+ dstAddr: netHdr.SourceAddress(),
+ dstPort: transHdr.SourcePort(),
+ transProto: transProto,
+ netProto: netProto,
+ }, true
+ }
+ case header.UDPProtocolNumber:
+ if netAndTransHeader, ok := pkt.Data().PullUp(netLen + header.UDPMinimumSize); ok {
+ netHdr := netHdrFunc(netAndTransHeader)
+ transHdr := header.UDP(netHdr.Payload())
+ return tupleID{
+ srcAddr: netHdr.DestinationAddress(),
+ srcPort: transHdr.DestinationPort(),
+ dstAddr: netHdr.SourceAddress(),
+ dstPort: transHdr.SourcePort(),
+ transProto: transProto,
+ netProto: netProto,
+ }, true
+ }
}
+
+ return tupleID{}, false
}
func getTupleID(pkt *PacketBuffer) (tid tupleID, isICMPError bool, ok bool) {
@@ -308,17 +348,30 @@ func getTupleID(pkt *PacketBuffer) (tid tupleID, isICMPError bool, ok bool) {
// TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
return tupleID{}, false, false
}
- switch ipv4.TransportProtocol() {
- case header.TCPProtocolNumber:
- if netAndTransHeader, ok := pkt.Data().PullUp(header.IPv4MinimumSize + header.TCPMinimumSize); ok {
- netHdr := header.IPv4(netAndTransHeader)
- return getTupleIDForPacketInICMPError(netHdr, header.IPv4ProtocolNumber, header.TCP(netHdr.Payload()), header.TCPProtocolNumber), true, true
- }
- case header.UDPProtocolNumber:
- if netAndTransHeader, ok := pkt.Data().PullUp(header.IPv4MinimumSize + header.UDPMinimumSize); ok {
- netHdr := header.IPv4(netAndTransHeader)
- return getTupleIDForPacketInICMPError(netHdr, header.IPv4ProtocolNumber, header.UDP(netHdr.Payload()), header.UDPProtocolNumber), true, true
- }
+
+ if tid, ok := getTupleIDForPacketInICMPError(pkt, func(b []byte) header.Network { return header.IPv4(b) }, header.IPv4ProtocolNumber, header.IPv4MinimumSize, ipv4.TransportProtocol()); ok {
+ return tid, true, true
+ }
+ case header.ICMPv6ProtocolNumber:
+ icmp := header.ICMPv6(pkt.TransportHeader().View())
+ if len(icmp) < header.ICMPv6MinimumSize {
+ return tupleID{}, false, false
+ }
+
+ switch icmp.Type() {
+ case header.ICMPv6DstUnreachable, header.ICMPv6PacketTooBig, header.ICMPv6TimeExceeded, header.ICMPv6ParamProblem:
+ default:
+ return tupleID{}, false, false
+ }
+
+ h, ok := pkt.Data().PullUp(header.IPv6MinimumSize)
+ if !ok {
+ return tupleID{}, false, false
+ }
+
+ // TODO(https://gvisor.dev/issue/6789): Handle extension headers.
+ if tid, ok := getTupleIDForPacketInICMPError(pkt, func(b []byte) header.Network { return header.IPv6(b) }, header.IPv6ProtocolNumber, header.IPv6MinimumSize, header.IPv6(h).TransportProtocol()); ok {
+ return tid, true, true
}
}
@@ -624,6 +677,33 @@ func (cn *conn) handlePacket(pkt *PacketBuffer, hook Hook, rt *Route) bool {
} else {
network.SetSourceAddressWithChecksumUpdate(tid.dstAddr)
}
+ case header.ICMPv6ProtocolNumber:
+ network := header.IPv6(pkt.NetworkHeader().View())
+ srcAddr := network.SourceAddress()
+ dstAddr := network.DestinationAddress()
+ if dnat {
+ dstAddr = tid.srcAddr
+ } else {
+ srcAddr = tid.dstAddr
+ }
+
+ icmp := header.ICMPv6(pkt.TransportHeader().View())
+ // TODO(https://gvisor.dev/issue/6788): Incrementally update ICMP checksum.
+ icmp.SetChecksum(0)
+ payload := pkt.Data()
+ icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
+ Header: icmp,
+ Src: srcAddr,
+ Dst: dstAddr,
+ PayloadCsum: payload.AsRange().Checksum(),
+ PayloadLen: payload.Size(),
+ }))
+
+ if dnat {
+ network.SetDestinationAddress(dstAddr)
+ } else {
+ network.SetSourceAddress(srcAddr)
+ }
}
return true
diff --git a/pkg/tcpip/tests/integration/iptables_test.go b/pkg/tcpip/tests/integration/iptables_test.go
index 9e00a6350..7fe3b29d9 100644
--- a/pkg/tcpip/tests/integration/iptables_test.go
+++ b/pkg/tcpip/tests/integration/iptables_test.go
@@ -1809,6 +1809,17 @@ func TestNATICMPError(t *testing.T) {
ip.SetChecksum(^ip.CalculateChecksum())
}
+ ip6Hdr := func(v buffer.View, payloadLen int, transProto tcpip.TransportProtocolNumber, srcAddr, dstAddr tcpip.Address) {
+ ip := header.IPv6(v)
+ ip.Encode(&header.IPv6Fields{
+ PayloadLength: uint16(payloadLen),
+ TransportProtocol: transProto,
+ HopLimit: 64,
+ SrcAddr: srcAddr,
+ DstAddr: dstAddr,
+ })
+ }
+
tests := []struct {
name string
netProto tcpip.NetworkProtocolNumber
@@ -1960,6 +1971,150 @@ func TestNATICMPError(t *testing.T) {
},
},
},
+ {
+ name: "IPv6",
+ netProto: ipv6.ProtocolNumber,
+ host1Addr: utils.Host1IPv6Addr.AddressWithPrefix.Address,
+ icmpError: func(t *testing.T, original buffer.View, icmpType uint8) buffer.View {
+ payloadLen := header.ICMPv6MinimumSize + len(original)
+ hdr := buffer.NewPrependable(header.IPv6MinimumSize + payloadLen)
+ icmp := header.ICMPv6(hdr.Prepend(payloadLen))
+ icmp.SetType(header.ICMPv6Type(icmpType))
+ if n := copy(icmp.Payload(), original); n != len(original) {
+ t.Fatalf("got copy(...) = %d, want = %d", n, len(original))
+ }
+ icmp.SetChecksum(0)
+ icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
+ Header: icmp,
+ Src: utils.Host1IPv6Addr.AddressWithPrefix.Address,
+ Dst: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address,
+ }))
+ ip6Hdr(hdr.Prepend(header.IPv6MinimumSize),
+ payloadLen,
+ header.ICMPv6ProtocolNumber,
+ utils.Host1IPv6Addr.AddressWithPrefix.Address,
+ utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address,
+ )
+ return hdr.View()
+ },
+ decrementTTL: func(v buffer.View) {
+ ip := header.IPv6(v)
+ ip.SetHopLimit(ip.HopLimit() - 1)
+ },
+ checkNATedError: func(t *testing.T, v buffer.View, original buffer.View, icmpType uint8) {
+ checker.IPv6(t, v,
+ checker.SrcAddr(utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address),
+ checker.DstAddr(utils.Host2IPv6Addr.AddressWithPrefix.Address),
+ checker.ICMPv6(
+ checker.ICMPv6Type(header.ICMPv6Type(icmpType)),
+ checker.ICMPv6Payload(original),
+ ),
+ )
+ },
+ transportTypes: []transportTypeTest{
+ {
+ name: "UDP",
+ proto: header.UDPProtocolNumber,
+ buf: func() buffer.View {
+ hdr := buffer.NewPrependable(header.IPv6MinimumSize + header.UDPMinimumSize)
+ udp := header.UDP(hdr.Prepend(header.UDPMinimumSize))
+ udp.SetSourcePort(srcPort)
+ udp.SetDestinationPort(dstPort)
+ udp.SetChecksum(0)
+ udp.SetChecksum(^udp.CalculateChecksum(header.PseudoHeaderChecksum(
+ header.UDPProtocolNumber,
+ utils.Host2IPv6Addr.AddressWithPrefix.Address,
+ utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
+ uint16(len(udp)),
+ )))
+ ip6Hdr(hdr.Prepend(header.IPv6MinimumSize),
+ header.UDPMinimumSize,
+ header.UDPProtocolNumber,
+ utils.Host2IPv6Addr.AddressWithPrefix.Address,
+ utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
+ )
+ return hdr.View()
+ }(),
+ checkNATed: func(t *testing.T, v buffer.View) {
+ checker.IPv6(t, v,
+ checker.SrcAddr(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address),
+ checker.DstAddr(utils.Host1IPv6Addr.AddressWithPrefix.Address),
+ checker.UDP(
+ checker.SrcPort(srcPort),
+ checker.DstPort(dstPort),
+ ),
+ )
+ },
+ },
+ {
+ name: "TCP",
+ proto: header.TCPProtocolNumber,
+ buf: func() buffer.View {
+ hdr := buffer.NewPrependable(header.IPv6MinimumSize + header.TCPMinimumSize)
+ tcp := header.TCP(hdr.Prepend(header.TCPMinimumSize))
+ tcp.SetSourcePort(srcPort)
+ tcp.SetDestinationPort(dstPort)
+ tcp.SetDataOffset(header.TCPMinimumSize)
+ tcp.SetChecksum(0)
+ tcp.SetChecksum(^tcp.CalculateChecksum(header.PseudoHeaderChecksum(
+ header.TCPProtocolNumber,
+ utils.Host2IPv6Addr.AddressWithPrefix.Address,
+ utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
+ uint16(len(tcp)),
+ )))
+ ip6Hdr(hdr.Prepend(header.IPv6MinimumSize),
+ header.TCPMinimumSize,
+ header.TCPProtocolNumber,
+ utils.Host2IPv6Addr.AddressWithPrefix.Address,
+ utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
+ )
+ return hdr.View()
+ }(),
+ checkNATed: func(t *testing.T, v buffer.View) {
+ checker.IPv6(t, v,
+ checker.SrcAddr(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address),
+ checker.DstAddr(utils.Host1IPv6Addr.AddressWithPrefix.Address),
+ checker.TCP(
+ checker.SrcPort(srcPort),
+ checker.DstPort(dstPort),
+ ),
+ )
+ },
+ },
+ },
+ icmpTypes: []icmpTypeTest{
+ {
+ name: "Destination Unreachable",
+ val: uint8(header.ICMPv6DstUnreachable),
+ expectResponse: true,
+ },
+ {
+ name: "Packet Too Big",
+ val: uint8(header.ICMPv6PacketTooBig),
+ expectResponse: true,
+ },
+ {
+ name: "Time Exceeded",
+ val: uint8(header.ICMPv6TimeExceeded),
+ expectResponse: true,
+ },
+ {
+ name: "Parameter Problem",
+ val: uint8(header.ICMPv6ParamProblem),
+ expectResponse: true,
+ },
+ {
+ name: "Echo Request",
+ val: uint8(header.ICMPv6EchoRequest),
+ expectResponse: false,
+ },
+ {
+ name: "Echo Reply",
+ val: uint8(header.ICMPv6EchoReply),
+ expectResponse: false,
+ },
+ },
+ },
}
for _, test := range tests {