mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
NAT ICMPv6 errors
...so a NAT-ed connection's socket can handle ICMP errors. Updates #5916. PiperOrigin-RevId: 406270868
This commit is contained in:
committed by
gVisor bot
parent
ca55c18a31
commit
1953d2ad28
+118
-38
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user