Skip to content

Commit 1953d2a

Browse files
ghananigansgvisor-bot
authored andcommitted
NAT ICMPv6 errors
...so a NAT-ed connection's socket can handle ICMP errors. Updates google#5916. PiperOrigin-RevId: 406270868
1 parent ca55c18 commit 1953d2a

File tree

2 files changed

+273
-38
lines changed

2 files changed

+273
-38
lines changed

pkg/tcpip/stack/conntrack.go

Lines changed: 118 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,22 @@ type bucket struct {
209209
tuples tupleList
210210
}
211211

212+
func getEmbeddedNetAndTransHeaders(pkt *PacketBuffer, netHdrLength int, netHdrFunc func([]byte) header.Network) (header.Network, header.ChecksummableTransport, bool) {
213+
switch pkt.tuple.id().transProto {
214+
case header.TCPProtocolNumber:
215+
if netAndTransHeader, ok := pkt.Data().PullUp(netHdrLength + header.TCPMinimumSize); ok {
216+
netHeader := netHdrFunc(netAndTransHeader)
217+
return netHeader, header.TCP(netHeader.Payload()), true
218+
}
219+
case header.UDPProtocolNumber:
220+
if netAndTransHeader, ok := pkt.Data().PullUp(netHdrLength + header.UDPMinimumSize); ok {
221+
netHeader := netHdrFunc(netAndTransHeader)
222+
return netHeader, header.UDP(netHeader.Payload()), true
223+
}
224+
}
225+
return nil, nil, false
226+
}
227+
212228
func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.ChecksummableTransport, isICMPError bool, ok bool) {
213229
switch pkt.TransportProtocolNumber {
214230
case header.TCPProtocolNumber:
@@ -225,29 +241,31 @@ func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Check
225241
panic(fmt.Sprintf("should have a valid IPv4 packet; only have %d bytes, want at least %d bytes", pkt.Data().Size(), header.IPv4MinimumSize))
226242
}
227243

228-
ipv4 := header.IPv4(h)
229-
if ipv4.HeaderLength() > header.IPv4MinimumSize {
244+
if header.IPv4(h).HeaderLength() > header.IPv4MinimumSize {
230245
// TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
231246
panic("should have dropped packets with IPv4 options")
232247
}
233248

234-
switch pkt.tuple.id().transProto {
235-
case header.TCPProtocolNumber:
236-
// TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
237-
netAndTransHeader, ok := pkt.Data().PullUp(header.IPv4MinimumSize + header.TCPMinimumSize)
238-
if !ok {
239-
return nil, nil, false, false
240-
}
241-
netHeader := header.IPv4(netAndTransHeader)
242-
return netHeader, header.TCP(netHeader.Payload()), true, true
243-
case header.UDPProtocolNumber:
244-
// TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
245-
netAndTransHeader, ok := pkt.Data().PullUp(header.IPv4MinimumSize + header.UDPMinimumSize)
246-
if !ok {
247-
return nil, nil, false, false
248-
}
249-
netHeader := header.IPv4(netAndTransHeader)
250-
return netHeader, header.UDP(netHeader.Payload()), true, true
249+
if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, header.IPv4MinimumSize, func(b []byte) header.Network { return header.IPv4(b) }); ok {
250+
return netHdr, transHdr, true, true
251+
}
252+
case header.ICMPv6ProtocolNumber:
253+
h, ok := pkt.Data().PullUp(header.IPv6MinimumSize)
254+
if !ok {
255+
panic(fmt.Sprintf("should have a valid IPv6 packet; only have %d bytes, want at least %d bytes", pkt.Data().Size(), header.IPv6MinimumSize))
256+
}
257+
258+
// We do not support extension headers in ICMP errors so the next header
259+
// in the IPv6 packet should be a tracked protocol if we reach this point.
260+
//
261+
// TODO(https://gvisor.dev/issue/6789): Support extension headers.
262+
transProto := pkt.tuple.id().transProto
263+
if got := header.IPv6(h).TransportProtocol(); got != transProto {
264+
panic(fmt.Sprintf("got TransportProtocol() = %d, want = %d", got, transProto))
265+
}
266+
267+
if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, header.IPv6MinimumSize, func(b []byte) header.Network { return header.IPv6(b) }); ok {
268+
return netHdr, transHdr, true, true
251269
}
252270
}
253271

@@ -265,15 +283,37 @@ func getTupleIDForRegularPacket(netHdr header.Network, netProto tcpip.NetworkPro
265283
}
266284
}
267285

268-
func getTupleIDForPacketInICMPError(netHdr header.Network, netProto tcpip.NetworkProtocolNumber, transHdr header.Transport, transProto tcpip.TransportProtocolNumber) tupleID {
269-
return tupleID{
270-
srcAddr: netHdr.DestinationAddress(),
271-
srcPort: transHdr.DestinationPort(),
272-
dstAddr: netHdr.SourceAddress(),
273-
dstPort: transHdr.SourcePort(),
274-
transProto: transProto,
275-
netProto: netProto,
286+
func getTupleIDForPacketInICMPError(pkt *PacketBuffer, netHdrFunc func([]byte) header.Network, netProto tcpip.NetworkProtocolNumber, netLen int, transProto tcpip.TransportProtocolNumber) (tupleID, bool) {
287+
switch transProto {
288+
case header.TCPProtocolNumber:
289+
if netAndTransHeader, ok := pkt.Data().PullUp(netLen + header.TCPMinimumSize); ok {
290+
netHdr := netHdrFunc(netAndTransHeader)
291+
transHdr := header.TCP(netHdr.Payload())
292+
return tupleID{
293+
srcAddr: netHdr.DestinationAddress(),
294+
srcPort: transHdr.DestinationPort(),
295+
dstAddr: netHdr.SourceAddress(),
296+
dstPort: transHdr.SourcePort(),
297+
transProto: transProto,
298+
netProto: netProto,
299+
}, true
300+
}
301+
case header.UDPProtocolNumber:
302+
if netAndTransHeader, ok := pkt.Data().PullUp(netLen + header.UDPMinimumSize); ok {
303+
netHdr := netHdrFunc(netAndTransHeader)
304+
transHdr := header.UDP(netHdr.Payload())
305+
return tupleID{
306+
srcAddr: netHdr.DestinationAddress(),
307+
srcPort: transHdr.DestinationPort(),
308+
dstAddr: netHdr.SourceAddress(),
309+
dstPort: transHdr.SourcePort(),
310+
transProto: transProto,
311+
netProto: netProto,
312+
}, true
313+
}
276314
}
315+
316+
return tupleID{}, false
277317
}
278318

279319
func getTupleID(pkt *PacketBuffer) (tid tupleID, isICMPError bool, ok bool) {
@@ -308,17 +348,30 @@ func getTupleID(pkt *PacketBuffer) (tid tupleID, isICMPError bool, ok bool) {
308348
// TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
309349
return tupleID{}, false, false
310350
}
311-
switch ipv4.TransportProtocol() {
312-
case header.TCPProtocolNumber:
313-
if netAndTransHeader, ok := pkt.Data().PullUp(header.IPv4MinimumSize + header.TCPMinimumSize); ok {
314-
netHdr := header.IPv4(netAndTransHeader)
315-
return getTupleIDForPacketInICMPError(netHdr, header.IPv4ProtocolNumber, header.TCP(netHdr.Payload()), header.TCPProtocolNumber), true, true
316-
}
317-
case header.UDPProtocolNumber:
318-
if netAndTransHeader, ok := pkt.Data().PullUp(header.IPv4MinimumSize + header.UDPMinimumSize); ok {
319-
netHdr := header.IPv4(netAndTransHeader)
320-
return getTupleIDForPacketInICMPError(netHdr, header.IPv4ProtocolNumber, header.UDP(netHdr.Payload()), header.UDPProtocolNumber), true, true
321-
}
351+
352+
if tid, ok := getTupleIDForPacketInICMPError(pkt, func(b []byte) header.Network { return header.IPv4(b) }, header.IPv4ProtocolNumber, header.IPv4MinimumSize, ipv4.TransportProtocol()); ok {
353+
return tid, true, true
354+
}
355+
case header.ICMPv6ProtocolNumber:
356+
icmp := header.ICMPv6(pkt.TransportHeader().View())
357+
if len(icmp) < header.ICMPv6MinimumSize {
358+
return tupleID{}, false, false
359+
}
360+
361+
switch icmp.Type() {
362+
case header.ICMPv6DstUnreachable, header.ICMPv6PacketTooBig, header.ICMPv6TimeExceeded, header.ICMPv6ParamProblem:
363+
default:
364+
return tupleID{}, false, false
365+
}
366+
367+
h, ok := pkt.Data().PullUp(header.IPv6MinimumSize)
368+
if !ok {
369+
return tupleID{}, false, false
370+
}
371+
372+
// TODO(https://gvisor.dev/issue/6789): Handle extension headers.
373+
if tid, ok := getTupleIDForPacketInICMPError(pkt, func(b []byte) header.Network { return header.IPv6(b) }, header.IPv6ProtocolNumber, header.IPv6MinimumSize, header.IPv6(h).TransportProtocol()); ok {
374+
return tid, true, true
322375
}
323376
}
324377

@@ -624,6 +677,33 @@ func (cn *conn) handlePacket(pkt *PacketBuffer, hook Hook, rt *Route) bool {
624677
} else {
625678
network.SetSourceAddressWithChecksumUpdate(tid.dstAddr)
626679
}
680+
case header.ICMPv6ProtocolNumber:
681+
network := header.IPv6(pkt.NetworkHeader().View())
682+
srcAddr := network.SourceAddress()
683+
dstAddr := network.DestinationAddress()
684+
if dnat {
685+
dstAddr = tid.srcAddr
686+
} else {
687+
srcAddr = tid.dstAddr
688+
}
689+
690+
icmp := header.ICMPv6(pkt.TransportHeader().View())
691+
// TODO(https://gvisor.dev/issue/6788): Incrementally update ICMP checksum.
692+
icmp.SetChecksum(0)
693+
payload := pkt.Data()
694+
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
695+
Header: icmp,
696+
Src: srcAddr,
697+
Dst: dstAddr,
698+
PayloadCsum: payload.AsRange().Checksum(),
699+
PayloadLen: payload.Size(),
700+
}))
701+
702+
if dnat {
703+
network.SetDestinationAddress(dstAddr)
704+
} else {
705+
network.SetSourceAddress(srcAddr)
706+
}
627707
}
628708

629709
return true

pkg/tcpip/tests/integration/iptables_test.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,6 +1809,17 @@ func TestNATICMPError(t *testing.T) {
18091809
ip.SetChecksum(^ip.CalculateChecksum())
18101810
}
18111811

1812+
ip6Hdr := func(v buffer.View, payloadLen int, transProto tcpip.TransportProtocolNumber, srcAddr, dstAddr tcpip.Address) {
1813+
ip := header.IPv6(v)
1814+
ip.Encode(&header.IPv6Fields{
1815+
PayloadLength: uint16(payloadLen),
1816+
TransportProtocol: transProto,
1817+
HopLimit: 64,
1818+
SrcAddr: srcAddr,
1819+
DstAddr: dstAddr,
1820+
})
1821+
}
1822+
18121823
tests := []struct {
18131824
name string
18141825
netProto tcpip.NetworkProtocolNumber
@@ -1960,6 +1971,150 @@ func TestNATICMPError(t *testing.T) {
19601971
},
19611972
},
19621973
},
1974+
{
1975+
name: "IPv6",
1976+
netProto: ipv6.ProtocolNumber,
1977+
host1Addr: utils.Host1IPv6Addr.AddressWithPrefix.Address,
1978+
icmpError: func(t *testing.T, original buffer.View, icmpType uint8) buffer.View {
1979+
payloadLen := header.ICMPv6MinimumSize + len(original)
1980+
hdr := buffer.NewPrependable(header.IPv6MinimumSize + payloadLen)
1981+
icmp := header.ICMPv6(hdr.Prepend(payloadLen))
1982+
icmp.SetType(header.ICMPv6Type(icmpType))
1983+
if n := copy(icmp.Payload(), original); n != len(original) {
1984+
t.Fatalf("got copy(...) = %d, want = %d", n, len(original))
1985+
}
1986+
icmp.SetChecksum(0)
1987+
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
1988+
Header: icmp,
1989+
Src: utils.Host1IPv6Addr.AddressWithPrefix.Address,
1990+
Dst: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address,
1991+
}))
1992+
ip6Hdr(hdr.Prepend(header.IPv6MinimumSize),
1993+
payloadLen,
1994+
header.ICMPv6ProtocolNumber,
1995+
utils.Host1IPv6Addr.AddressWithPrefix.Address,
1996+
utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address,
1997+
)
1998+
return hdr.View()
1999+
},
2000+
decrementTTL: func(v buffer.View) {
2001+
ip := header.IPv6(v)
2002+
ip.SetHopLimit(ip.HopLimit() - 1)
2003+
},
2004+
checkNATedError: func(t *testing.T, v buffer.View, original buffer.View, icmpType uint8) {
2005+
checker.IPv6(t, v,
2006+
checker.SrcAddr(utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address),
2007+
checker.DstAddr(utils.Host2IPv6Addr.AddressWithPrefix.Address),
2008+
checker.ICMPv6(
2009+
checker.ICMPv6Type(header.ICMPv6Type(icmpType)),
2010+
checker.ICMPv6Payload(original),
2011+
),
2012+
)
2013+
},
2014+
transportTypes: []transportTypeTest{
2015+
{
2016+
name: "UDP",
2017+
proto: header.UDPProtocolNumber,
2018+
buf: func() buffer.View {
2019+
hdr := buffer.NewPrependable(header.IPv6MinimumSize + header.UDPMinimumSize)
2020+
udp := header.UDP(hdr.Prepend(header.UDPMinimumSize))
2021+
udp.SetSourcePort(srcPort)
2022+
udp.SetDestinationPort(dstPort)
2023+
udp.SetChecksum(0)
2024+
udp.SetChecksum(^udp.CalculateChecksum(header.PseudoHeaderChecksum(
2025+
header.UDPProtocolNumber,
2026+
utils.Host2IPv6Addr.AddressWithPrefix.Address,
2027+
utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
2028+
uint16(len(udp)),
2029+
)))
2030+
ip6Hdr(hdr.Prepend(header.IPv6MinimumSize),
2031+
header.UDPMinimumSize,
2032+
header.UDPProtocolNumber,
2033+
utils.Host2IPv6Addr.AddressWithPrefix.Address,
2034+
utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
2035+
)
2036+
return hdr.View()
2037+
}(),
2038+
checkNATed: func(t *testing.T, v buffer.View) {
2039+
checker.IPv6(t, v,
2040+
checker.SrcAddr(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address),
2041+
checker.DstAddr(utils.Host1IPv6Addr.AddressWithPrefix.Address),
2042+
checker.UDP(
2043+
checker.SrcPort(srcPort),
2044+
checker.DstPort(dstPort),
2045+
),
2046+
)
2047+
},
2048+
},
2049+
{
2050+
name: "TCP",
2051+
proto: header.TCPProtocolNumber,
2052+
buf: func() buffer.View {
2053+
hdr := buffer.NewPrependable(header.IPv6MinimumSize + header.TCPMinimumSize)
2054+
tcp := header.TCP(hdr.Prepend(header.TCPMinimumSize))
2055+
tcp.SetSourcePort(srcPort)
2056+
tcp.SetDestinationPort(dstPort)
2057+
tcp.SetDataOffset(header.TCPMinimumSize)
2058+
tcp.SetChecksum(0)
2059+
tcp.SetChecksum(^tcp.CalculateChecksum(header.PseudoHeaderChecksum(
2060+
header.TCPProtocolNumber,
2061+
utils.Host2IPv6Addr.AddressWithPrefix.Address,
2062+
utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
2063+
uint16(len(tcp)),
2064+
)))
2065+
ip6Hdr(hdr.Prepend(header.IPv6MinimumSize),
2066+
header.TCPMinimumSize,
2067+
header.TCPProtocolNumber,
2068+
utils.Host2IPv6Addr.AddressWithPrefix.Address,
2069+
utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
2070+
)
2071+
return hdr.View()
2072+
}(),
2073+
checkNATed: func(t *testing.T, v buffer.View) {
2074+
checker.IPv6(t, v,
2075+
checker.SrcAddr(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address),
2076+
checker.DstAddr(utils.Host1IPv6Addr.AddressWithPrefix.Address),
2077+
checker.TCP(
2078+
checker.SrcPort(srcPort),
2079+
checker.DstPort(dstPort),
2080+
),
2081+
)
2082+
},
2083+
},
2084+
},
2085+
icmpTypes: []icmpTypeTest{
2086+
{
2087+
name: "Destination Unreachable",
2088+
val: uint8(header.ICMPv6DstUnreachable),
2089+
expectResponse: true,
2090+
},
2091+
{
2092+
name: "Packet Too Big",
2093+
val: uint8(header.ICMPv6PacketTooBig),
2094+
expectResponse: true,
2095+
},
2096+
{
2097+
name: "Time Exceeded",
2098+
val: uint8(header.ICMPv6TimeExceeded),
2099+
expectResponse: true,
2100+
},
2101+
{
2102+
name: "Parameter Problem",
2103+
val: uint8(header.ICMPv6ParamProblem),
2104+
expectResponse: true,
2105+
},
2106+
{
2107+
name: "Echo Request",
2108+
val: uint8(header.ICMPv6EchoRequest),
2109+
expectResponse: false,
2110+
},
2111+
{
2112+
name: "Echo Reply",
2113+
val: uint8(header.ICMPv6EchoReply),
2114+
expectResponse: false,
2115+
},
2116+
},
2117+
},
19632118
}
19642119

19652120
for _, test := range tests {

0 commit comments

Comments
 (0)