mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support NATing ICMPv6 Echo packets
Updates #5915. PiperOrigin-RevId: 415367088
This commit is contained in:
committed by
gVisor bot
parent
46d3404d8e
commit
7f4b03068f
@@ -240,6 +240,13 @@ func (b ICMPv6) SetIdent(ident uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv6IdentOffset:], ident)
|
||||
}
|
||||
|
||||
// SetIdentWithChecksumUpdate sets the Ident field and updates the checksum.
|
||||
func (b ICMPv6) SetIdentWithChecksumUpdate(new uint16) {
|
||||
old := b.Ident()
|
||||
b.SetIdent(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// Sequence retrieves the Sequence field from an ICMPv6 message.
|
||||
func (b ICMPv6) Sequence() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv6SequenceOffset:])
|
||||
@@ -284,3 +291,9 @@ func ICMPv6Checksum(params ICMPv6ChecksumParams) uint16 {
|
||||
|
||||
return ^xsum
|
||||
}
|
||||
|
||||
// UpdateChecksumPseudoHeaderAddress updates the checksum to reflect an
|
||||
// updated address in the pseudo header.
|
||||
func (b ICMPv6) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address) {
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedAddress(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
@@ -300,14 +300,16 @@ func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Trans
|
||||
if tcpHeader := header.TCP(pkt.TransportHeader().View()); len(tcpHeader) >= header.TCPMinimumSize {
|
||||
return pkt.Network(), tcpHeader, false, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.UDPProtocolNumber:
|
||||
if udpHeader := header.UDP(pkt.TransportHeader().View()); len(udpHeader) >= header.UDPMinimumSize {
|
||||
return pkt.Network(), udpHeader, false, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
icmpHeader := header.ICMPv4(pkt.TransportHeader().View())
|
||||
if len(icmpHeader) < header.ICMPv4MinimumSize {
|
||||
break
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
switch icmpType := icmpHeader.Type(); icmpType {
|
||||
@@ -331,7 +333,21 @@ func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Trans
|
||||
if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, header.IPv4MinimumSize, v4NetAndTransHdr, pkt.tuple.id().transProto); ok {
|
||||
return netHdr, transHdr, true, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
icmpHeader := header.ICMPv6(pkt.TransportHeader().View())
|
||||
if len(icmpHeader) < header.ICMPv6MinimumSize {
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
switch icmpType := icmpHeader.Type(); icmpType {
|
||||
case header.ICMPv6EchoRequest, header.ICMPv6EchoReply:
|
||||
return pkt.Network(), icmpHeader, false, true
|
||||
case header.ICMPv6DstUnreachable, header.ICMPv6PacketTooBig, header.ICMPv6TimeExceeded, header.ICMPv6ParamProblem:
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv6 type = %d", icmpType))
|
||||
}
|
||||
|
||||
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))
|
||||
@@ -349,9 +365,10 @@ func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Trans
|
||||
if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, header.IPv6MinimumSize, v6NetAndTransHdr, transProto); ok {
|
||||
return netHdr, transHdr, true, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected transport protocol = %d", pkt.TransportProtocolNumber))
|
||||
}
|
||||
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
func getTupleIDForRegularPacket(netHdr header.Network, netProto tcpip.NetworkProtocolNumber, transHdr header.Transport, transProto tcpip.TransportProtocolNumber) tupleID {
|
||||
@@ -458,6 +475,13 @@ func getTupleID(pkt *PacketBuffer) (tupleID, getTupleIDDisposition) {
|
||||
}
|
||||
|
||||
switch icmp.Type() {
|
||||
case header.ICMPv6EchoRequest:
|
||||
return getTupleIDForEchoPacket(pkt, icmp.Ident(), true /* request */), getTupleIDOKAndAllowNewConn
|
||||
case header.ICMPv6EchoReply:
|
||||
// Do not create a new connection in response to a reply packet as only
|
||||
// the first packet of a connection should create a conntrack entry but
|
||||
// a reply is never the first packet sent for a connection.
|
||||
return getTupleIDForEchoPacket(pkt, icmp.Ident(), false /* request */), getTupleIDOKAndDontAllowNewConn
|
||||
case header.ICMPv6DstUnreachable, header.ICMPv6PacketTooBig, header.ICMPv6TimeExceeded, header.ICMPv6ParamProblem:
|
||||
default:
|
||||
return tupleID{}, getTupleIDNotOK
|
||||
|
||||
@@ -208,7 +208,7 @@ func snatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcp
|
||||
if port == 0 {
|
||||
portsOrIdents = targetPortRangeForTCPAndUDP(header.TCP(pkt.TransportHeader().View()).SourcePort())
|
||||
}
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
case header.ICMPv4ProtocolNumber, header.ICMPv6ProtocolNumber:
|
||||
// Allow NAT-ing to any 16-bit value for ICMP's Ident field to match Linux
|
||||
// behaviour.
|
||||
//
|
||||
@@ -289,20 +289,20 @@ func (mt *MasqueradeTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, addre
|
||||
return snatAction(pkt, hook, r, 0 /* port */, address)
|
||||
}
|
||||
|
||||
func rewritePacket(n header.Network, t header.Transport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPort uint16, newAddr tcpip.Address) {
|
||||
func rewritePacket(n header.Network, t header.Transport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPortOrIdent uint16, newAddr tcpip.Address) {
|
||||
switch t := t.(type) {
|
||||
case header.ChecksummableTransport:
|
||||
if updateSRCFields {
|
||||
if fullChecksum {
|
||||
t.SetSourcePortWithChecksumUpdate(newPort)
|
||||
t.SetSourcePortWithChecksumUpdate(newPortOrIdent)
|
||||
} else {
|
||||
t.SetSourcePort(newPort)
|
||||
t.SetSourcePort(newPortOrIdent)
|
||||
}
|
||||
} else {
|
||||
if fullChecksum {
|
||||
t.SetDestinationPortWithChecksumUpdate(newPort)
|
||||
t.SetDestinationPortWithChecksumUpdate(newPortOrIdent)
|
||||
} else {
|
||||
t.SetDestinationPort(newPort)
|
||||
t.SetDestinationPort(newPortOrIdent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,15 +320,37 @@ func rewritePacket(n header.Network, t header.Transport, updateSRCFields, fullCh
|
||||
switch icmpType := t.Type(); icmpType {
|
||||
case header.ICMPv4Echo:
|
||||
if updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPort)
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
case header.ICMPv4EchoReply:
|
||||
if !updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPort)
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv4 type = %d", icmpType))
|
||||
}
|
||||
case header.ICMPv6:
|
||||
switch icmpType := t.Type(); icmpType {
|
||||
case header.ICMPv6EchoRequest:
|
||||
if updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
case header.ICMPv6EchoReply:
|
||||
if !updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv4 type = %d", icmpType))
|
||||
}
|
||||
|
||||
var oldAddr tcpip.Address
|
||||
if updateSRCFields {
|
||||
oldAddr = n.SourceAddress()
|
||||
} else {
|
||||
oldAddr = n.DestinationAddress()
|
||||
}
|
||||
|
||||
t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled transport = %#v", t))
|
||||
}
|
||||
|
||||
@@ -1372,6 +1372,32 @@ func TestNATEcho(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
v6EchoPkt := func(srcAddr, dstAddr tcpip.Address, reply bool) buffer.View {
|
||||
icmpType := header.ICMPv6EchoRequest
|
||||
if reply {
|
||||
icmpType = header.ICMPv6EchoReply
|
||||
}
|
||||
|
||||
return icmpv6Packet(srcAddr, dstAddr, icmpType, ident)
|
||||
}
|
||||
|
||||
checkV6EchoPkt := func(t *testing.T, v buffer.View, srcAddr, dstAddr tcpip.Address, reply bool) {
|
||||
t.Helper()
|
||||
|
||||
icmpType := header.ICMPv6EchoRequest
|
||||
if reply {
|
||||
icmpType = header.ICMPv6EchoReply
|
||||
}
|
||||
|
||||
checker.IPv6(t, v,
|
||||
checker.SrcAddr(srcAddr),
|
||||
checker.DstAddr(dstAddr),
|
||||
checker.ICMPv6(
|
||||
checker.ICMPv6Type(icmpType),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
type natTypeTest struct {
|
||||
name string
|
||||
natTypes []natType
|
||||
@@ -1422,6 +1448,40 @@ func TestNATEcho(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv6",
|
||||
netProto: header.IPv6ProtocolNumber,
|
||||
transProto: header.ICMPv6ProtocolNumber,
|
||||
echoPkt: v6EchoPkt,
|
||||
checkEchoPkt: checkV6EchoPkt,
|
||||
|
||||
natTypes: []natTypeTest{
|
||||
{
|
||||
name: "SNAT",
|
||||
natTypes: snatTypes,
|
||||
requestSrc: utils.Host2IPv6Addr.AddressWithPrefix.Address,
|
||||
requestDst: utils.Host1IPv6Addr.AddressWithPrefix.Address,
|
||||
expectedRequestSrc: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address,
|
||||
expectedRequestDst: utils.Host1IPv6Addr.AddressWithPrefix.Address,
|
||||
},
|
||||
{
|
||||
name: "DNAT",
|
||||
natTypes: []natType{dnatTarget},
|
||||
requestSrc: utils.Host2IPv6Addr.AddressWithPrefix.Address,
|
||||
requestDst: utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
|
||||
expectedRequestSrc: utils.Host2IPv6Addr.AddressWithPrefix.Address,
|
||||
expectedRequestDst: utils.Host1IPv6Addr.AddressWithPrefix.Address,
|
||||
},
|
||||
{
|
||||
name: "Twice-NAT",
|
||||
natTypes: twiceNATTypes,
|
||||
requestSrc: utils.Host2IPv6Addr.AddressWithPrefix.Address,
|
||||
requestDst: utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address,
|
||||
expectedRequestSrc: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address,
|
||||
expectedRequestDst: utils.Host1IPv6Addr.AddressWithPrefix.Address,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -2057,6 +2117,27 @@ func tcpv6Packet(srcAddr, dstAddr tcpip.Address, srcPort, dstPort uint16, dataSi
|
||||
return hdr.View()
|
||||
}
|
||||
|
||||
func icmpv6Packet(srcAddr, dstAddr tcpip.Address, icmpType header.ICMPv6Type, ident uint16) buffer.View {
|
||||
hdr := buffer.NewPrependable(header.IPv6MinimumSize + header.ICMPv6MinimumSize)
|
||||
icmp := header.ICMPv6(hdr.Prepend(header.ICMPv6MinimumSize))
|
||||
icmp.SetType(icmpType)
|
||||
icmp.SetIdent(ident)
|
||||
icmp.SetChecksum(0)
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: srcAddr,
|
||||
Dst: dstAddr,
|
||||
}))
|
||||
encodeIPv6Header(
|
||||
hdr.Prepend(header.IPv6MinimumSize),
|
||||
len(icmp),
|
||||
header.ICMPv6ProtocolNumber,
|
||||
srcAddr,
|
||||
dstAddr,
|
||||
)
|
||||
return hdr.View()
|
||||
}
|
||||
|
||||
func TestNATICMPError(t *testing.T) {
|
||||
const (
|
||||
srcPort = 1234
|
||||
@@ -2655,6 +2736,27 @@ func TestSNATHandlePortOrIdentConflicts(t *testing.T) {
|
||||
},
|
||||
srcPortOrIdentRanges: srcPortRanges,
|
||||
},
|
||||
{
|
||||
name: "ICMP Echo",
|
||||
proto: header.ICMPv6ProtocolNumber,
|
||||
buf: func(srcAddr tcpip.Address, ident uint16) buffer.View {
|
||||
return icmpv6Packet(srcAddr, utils.Host1IPv6Addr.AddressWithPrefix.Address, header.ICMPv6EchoRequest, ident)
|
||||
},
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalIdent uint16, firstPacket bool, expectedRange portOrIdentRange) {
|
||||
checker.IPv6(t, v,
|
||||
checker.SrcAddr(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address),
|
||||
checker.DstAddr(utils.Host1IPv6Addr.AddressWithPrefix.Address),
|
||||
checker.ICMPv6(
|
||||
checker.ICMPv6Type(header.ICMPv6EchoRequest),
|
||||
),
|
||||
)
|
||||
|
||||
if !t.Failed() {
|
||||
compareSrcPortOrIdent(t, header.ICMPv6(header.IPv6(v).Payload()).Ident(), originalIdent, firstPacket, expectedRange)
|
||||
}
|
||||
},
|
||||
srcPortOrIdentRanges: identRanges,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user