mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support NATing ICMPv4 Echo packets
Updates #5915. PiperOrigin-RevId: 413738470
This commit is contained in:
committed by
gVisor bot
parent
b2f8b495ad
commit
c0d0937bb0
@@ -185,6 +185,13 @@ func (b ICMPv4) SetIdent(ident uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv4IdentOffset:], ident)
|
||||
}
|
||||
|
||||
// SetIdentWithChecksumUpdate sets the Ident field and updates the checksum.
|
||||
func (b ICMPv4) SetIdentWithChecksumUpdate(new uint16) {
|
||||
old := b.Ident()
|
||||
b.SetIdent(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// Sequence retrieves the Sequence field from an ICMPv4 message.
|
||||
func (b ICMPv4) Sequence() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv4SequenceOffset:])
|
||||
|
||||
+120
-57
@@ -17,6 +17,7 @@ package stack
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -70,29 +71,38 @@ func (t *tuple) id() tupleID {
|
||||
return t.tupleID
|
||||
}
|
||||
|
||||
// tupleID uniquely identifies a connection in one direction. It currently
|
||||
// contains enough information to distinguish between any TCP or UDP
|
||||
// connection, and will need to be extended to support other protocols.
|
||||
// tupleID uniquely identifies a trackable connection in one direction.
|
||||
//
|
||||
// +stateify savable
|
||||
type tupleID struct {
|
||||
srcAddr tcpip.Address
|
||||
srcPort uint16
|
||||
dstAddr tcpip.Address
|
||||
dstPort uint16
|
||||
transProto tcpip.TransportProtocolNumber
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
srcAddr tcpip.Address
|
||||
// The source port of a packet in the original direction is overloaded with
|
||||
// the ident of an Echo Request packet.
|
||||
//
|
||||
// This also matches the behaviour of sending packets on Linux where the
|
||||
// socket's source port value is used for the source port of outgoing packets
|
||||
// for TCP/UDP and the ident field for outgoing Echo Requests on Ping sockets:
|
||||
//
|
||||
// IPv4: https://github.com/torvalds/linux/blob/c5c17547b778975b3d83a73c8d84e8fb5ecf3ba5/net/ipv4/ping.c#L810
|
||||
// IPv6: https://github.com/torvalds/linux/blob/c5c17547b778975b3d83a73c8d84e8fb5ecf3ba5/net/ipv6/ping.c#L133
|
||||
srcPortOrEchoRequestIdent uint16
|
||||
dstAddr tcpip.Address
|
||||
// The opposite of srcPortOrEchoRequestIdent; the destination port of a packet
|
||||
// in the reply direction is overloaded with the ident of an Echo Reply.
|
||||
dstPortOrEchoReplyIdent uint16
|
||||
transProto tcpip.TransportProtocolNumber
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// reply creates the reply tupleID.
|
||||
func (ti tupleID) reply() tupleID {
|
||||
return tupleID{
|
||||
srcAddr: ti.dstAddr,
|
||||
srcPort: ti.dstPort,
|
||||
dstAddr: ti.srcAddr,
|
||||
dstPort: ti.srcPort,
|
||||
transProto: ti.transProto,
|
||||
netProto: ti.netProto,
|
||||
srcAddr: ti.dstAddr,
|
||||
srcPortOrEchoRequestIdent: ti.dstPortOrEchoReplyIdent,
|
||||
dstAddr: ti.srcAddr,
|
||||
dstPortOrEchoReplyIdent: ti.srcPortOrEchoRequestIdent,
|
||||
transProto: ti.transProto,
|
||||
netProto: ti.netProto,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +294,7 @@ func getEmbeddedNetAndTransHeaders(pkt *PacketBuffer, netHdrLength int, getNetAn
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.ChecksummableTransport, isICMPError bool, ok bool) {
|
||||
func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Transport, isICMPError bool, ok bool) {
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.TCPProtocolNumber:
|
||||
if tcpHeader := header.TCP(pkt.TransportHeader().View()); len(tcpHeader) >= header.TCPMinimumSize {
|
||||
@@ -295,6 +305,19 @@ func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Check
|
||||
return pkt.Network(), udpHeader, false, true
|
||||
}
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
icmpHeader := header.ICMPv4(pkt.TransportHeader().View())
|
||||
if len(icmpHeader) < header.ICMPv4MinimumSize {
|
||||
break
|
||||
}
|
||||
|
||||
switch icmpType := icmpHeader.Type(); icmpType {
|
||||
case header.ICMPv4Echo, header.ICMPv4EchoReply:
|
||||
return pkt.Network(), icmpHeader, false, true
|
||||
case header.ICMPv4DstUnreachable, header.ICMPv4TimeExceeded, header.ICMPv4ParamProblem:
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv4 type = %d", icmpType))
|
||||
}
|
||||
|
||||
h, ok := pkt.Data().PullUp(header.IPv4MinimumSize)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("should have a valid IPv4 packet; only have %d bytes, want at least %d bytes", pkt.Data().Size(), header.IPv4MinimumSize))
|
||||
@@ -333,24 +356,24 @@ func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Check
|
||||
|
||||
func getTupleIDForRegularPacket(netHdr header.Network, netProto tcpip.NetworkProtocolNumber, transHdr header.Transport, transProto tcpip.TransportProtocolNumber) tupleID {
|
||||
return tupleID{
|
||||
srcAddr: netHdr.SourceAddress(),
|
||||
srcPort: transHdr.SourcePort(),
|
||||
dstAddr: netHdr.DestinationAddress(),
|
||||
dstPort: transHdr.DestinationPort(),
|
||||
transProto: transProto,
|
||||
netProto: netProto,
|
||||
srcAddr: netHdr.SourceAddress(),
|
||||
srcPortOrEchoRequestIdent: transHdr.SourcePort(),
|
||||
dstAddr: netHdr.DestinationAddress(),
|
||||
dstPortOrEchoReplyIdent: transHdr.DestinationPort(),
|
||||
transProto: transProto,
|
||||
netProto: netProto,
|
||||
}
|
||||
}
|
||||
|
||||
func getTupleIDForPacketInICMPError(pkt *PacketBuffer, getNetAndTransHdr netAndTransHeadersFunc, netProto tcpip.NetworkProtocolNumber, netLen int, transProto tcpip.TransportProtocolNumber) (tupleID, bool) {
|
||||
if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, netLen, getNetAndTransHdr, transProto); ok {
|
||||
return tupleID{
|
||||
srcAddr: netHdr.DestinationAddress(),
|
||||
srcPort: transHdr.DestinationPort(),
|
||||
dstAddr: netHdr.SourceAddress(),
|
||||
dstPort: transHdr.SourcePort(),
|
||||
transProto: transProto,
|
||||
netProto: netProto,
|
||||
srcAddr: netHdr.DestinationAddress(),
|
||||
srcPortOrEchoRequestIdent: transHdr.DestinationPort(),
|
||||
dstAddr: netHdr.SourceAddress(),
|
||||
dstPortOrEchoReplyIdent: transHdr.SourcePort(),
|
||||
transProto: transProto,
|
||||
netProto: netProto,
|
||||
}, true
|
||||
}
|
||||
|
||||
@@ -365,6 +388,24 @@ const (
|
||||
getTupleIDOKAndDontAllowNewConn
|
||||
)
|
||||
|
||||
func getTupleIDForEchoPacket(pkt *PacketBuffer, ident uint16, request bool) tupleID {
|
||||
netHdr := pkt.Network()
|
||||
tid := tupleID{
|
||||
srcAddr: netHdr.SourceAddress(),
|
||||
dstAddr: netHdr.DestinationAddress(),
|
||||
transProto: pkt.TransportProtocolNumber,
|
||||
netProto: pkt.NetworkProtocolNumber,
|
||||
}
|
||||
|
||||
if request {
|
||||
tid.srcPortOrEchoRequestIdent = ident
|
||||
} else {
|
||||
tid.dstPortOrEchoReplyIdent = ident
|
||||
}
|
||||
|
||||
return tid
|
||||
}
|
||||
|
||||
func getTupleID(pkt *PacketBuffer) (tupleID, getTupleIDDisposition) {
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.TCPProtocolNumber:
|
||||
@@ -382,8 +423,16 @@ func getTupleID(pkt *PacketBuffer) (tupleID, getTupleIDDisposition) {
|
||||
}
|
||||
|
||||
switch icmp.Type() {
|
||||
case header.ICMPv4Echo:
|
||||
return getTupleIDForEchoPacket(pkt, icmp.Ident(), true /* request */), getTupleIDOKAndAllowNewConn
|
||||
case header.ICMPv4EchoReply:
|
||||
// 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.ICMPv4DstUnreachable, header.ICMPv4TimeExceeded, header.ICMPv4ParamProblem:
|
||||
default:
|
||||
// Unsupported ICMP type for NAT-ing.
|
||||
return tupleID{}, getTupleIDNotOK
|
||||
}
|
||||
|
||||
@@ -614,9 +663,9 @@ func (cn *conn) maybePerformNoopNAT(dnat bool) {
|
||||
}
|
||||
}
|
||||
|
||||
type portRange struct {
|
||||
type portOrIdentRange struct {
|
||||
start uint16
|
||||
size uint16
|
||||
size uint32
|
||||
}
|
||||
|
||||
// performNAT setups up the connection for the specified NAT and rewrites the
|
||||
@@ -628,7 +677,15 @@ type portRange struct {
|
||||
//
|
||||
// Generally, only the first packet of a connection reaches this method; other
|
||||
// packets will be manipulated without needing to modify the connection.
|
||||
func (cn *conn) performNAT(pkt *PacketBuffer, hook Hook, r *Route, ports portRange, natAddress tcpip.Address, dnat bool) {
|
||||
func (cn *conn) performNAT(pkt *PacketBuffer, hook Hook, r *Route, portsOrIdents portOrIdentRange, natAddress tcpip.Address, dnat bool) {
|
||||
lastPortOrIdent := func() uint16 {
|
||||
lastPortOrIdent := uint32(portsOrIdents.start) + portsOrIdents.size - 1
|
||||
if lastPortOrIdent > math.MaxUint16 {
|
||||
panic(fmt.Sprintf("got lastPortOrIdent = %d, want <= MaxUint16(=%d); portsOrIdents=%#v", lastPortOrIdent, math.MaxUint16, portsOrIdents))
|
||||
}
|
||||
return uint16(lastPortOrIdent)
|
||||
}()
|
||||
|
||||
// Make sure the packet is re-written after performing NAT.
|
||||
defer func() {
|
||||
// handlePacket returns true if the packet may skip the NAT table as the
|
||||
@@ -645,15 +702,15 @@ func (cn *conn) performNAT(pkt *PacketBuffer, hook Hook, r *Route, ports portRan
|
||||
|
||||
var manip *manipType
|
||||
var address *tcpip.Address
|
||||
var port *uint16
|
||||
var portOrIdent *uint16
|
||||
if dnat {
|
||||
manip = &cn.destinationManip
|
||||
address = &cn.reply.tupleID.srcAddr
|
||||
port = &cn.reply.tupleID.srcPort
|
||||
portOrIdent = &cn.reply.tupleID.srcPortOrEchoRequestIdent
|
||||
} else {
|
||||
manip = &cn.sourceManip
|
||||
address = &cn.reply.tupleID.dstAddr
|
||||
port = &cn.reply.tupleID.dstPort
|
||||
portOrIdent = &cn.reply.tupleID.dstPortOrEchoReplyIdent
|
||||
}
|
||||
|
||||
if *manip != manipNotPerformed {
|
||||
@@ -662,8 +719,8 @@ func (cn *conn) performNAT(pkt *PacketBuffer, hook Hook, r *Route, ports portRan
|
||||
*manip = manipPerformed
|
||||
*address = natAddress
|
||||
|
||||
// Does the current port fit in the range?
|
||||
if end := ports.start + ports.size - 1; *port >= ports.start && *port <= end {
|
||||
// Does the current port/ident fit in the range?
|
||||
if portsOrIdents.start <= *portOrIdent && *portOrIdent <= lastPortOrIdent {
|
||||
// Yes, is the current reply tuple unique?
|
||||
if other := cn.ct.connForTID(cn.reply.tupleID); other == nil {
|
||||
// Yes! No need to change the port.
|
||||
@@ -671,27 +728,32 @@ func (cn *conn) performNAT(pkt *PacketBuffer, hook Hook, r *Route, ports portRan
|
||||
}
|
||||
}
|
||||
|
||||
// Try our best to find a port that results in a unique reply tuple.
|
||||
// Try our best to find a port/ident that results in a unique reply tuple.
|
||||
//
|
||||
// We limit the number of attempts to find a unique tuple to not waste a lot
|
||||
// of time looking for a unique tuple.
|
||||
//
|
||||
// Matches linux behaviour introduced in
|
||||
// https://github.com/torvalds/linux/commit/a504b703bb1da526a01593da0e4be2af9d9f5fa8.
|
||||
const maxAttemptsForInitialRound uint16 = 128
|
||||
const maxAttemptsForInitialRound uint32 = 128
|
||||
const minAttemptsToContinue = 16
|
||||
|
||||
allowedInitialAttempts := maxAttemptsForInitialRound
|
||||
if allowedInitialAttempts > ports.size {
|
||||
allowedInitialAttempts = ports.size
|
||||
if allowedInitialAttempts > portsOrIdents.size {
|
||||
allowedInitialAttempts = portsOrIdents.size
|
||||
}
|
||||
|
||||
for maxAttempts := allowedInitialAttempts; ; maxAttempts /= 2 {
|
||||
// Start reach round with a random initial port in the range.
|
||||
initial := ports.start + uint16(cn.ct.rand.Uint32())%ports.size
|
||||
// Start reach round with a random initial port/ident offset.
|
||||
randOffset := cn.ct.rand.Uint32()
|
||||
|
||||
for i := uint16(0); i < maxAttempts; i++ {
|
||||
*port = initial + i%ports.size
|
||||
for i := uint32(0); i < maxAttempts; i++ {
|
||||
newPortOrIdentU32 := uint32(portsOrIdents.start) + (randOffset+i)%portsOrIdents.size
|
||||
if newPortOrIdentU32 > math.MaxUint16 {
|
||||
panic(fmt.Sprintf("got newPortOrIdentU32 = %d, want <= MaxUint16(=%d); portsOrIdents=%#v, randOffset=%d", newPortOrIdentU32, math.MaxUint16, portsOrIdents, randOffset))
|
||||
}
|
||||
|
||||
*portOrIdent = uint16(newPortOrIdentU32)
|
||||
|
||||
if other := cn.ct.connForTID(cn.reply.tupleID); other == nil {
|
||||
// We found a unique tuple!
|
||||
@@ -699,8 +761,9 @@ func (cn *conn) performNAT(pkt *PacketBuffer, hook Hook, r *Route, ports portRan
|
||||
}
|
||||
}
|
||||
|
||||
if maxAttempts == ports.size {
|
||||
// We already tried all the ports in the range so no need to keep trying.
|
||||
if maxAttempts == portsOrIdents.size {
|
||||
// We already tried all the ports/idents in the range so no need to keep
|
||||
// trying.
|
||||
return
|
||||
}
|
||||
|
||||
@@ -795,10 +858,10 @@ func (cn *conn) handlePacket(pkt *PacketBuffer, hook Hook, rt *Route) bool {
|
||||
panic(fmt.Sprintf("unhandled manip = %d", manip))
|
||||
}
|
||||
|
||||
newPort := tid.dstPort
|
||||
newPort := tid.dstPortOrEchoReplyIdent
|
||||
newAddr := tid.dstAddr
|
||||
if dnat {
|
||||
newPort = tid.srcPort
|
||||
newPort = tid.srcPortOrEchoRequestIdent
|
||||
newAddr = tid.srcAddr
|
||||
}
|
||||
|
||||
@@ -871,9 +934,9 @@ func (ct *ConnTrack) bucket(id tupleID) int {
|
||||
h.Write([]byte(id.srcAddr))
|
||||
h.Write([]byte(id.dstAddr))
|
||||
shortBuf := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(shortBuf, id.srcPort)
|
||||
binary.LittleEndian.PutUint16(shortBuf, id.srcPortOrEchoRequestIdent)
|
||||
h.Write([]byte(shortBuf))
|
||||
binary.LittleEndian.PutUint16(shortBuf, id.dstPort)
|
||||
binary.LittleEndian.PutUint16(shortBuf, id.dstPortOrEchoReplyIdent)
|
||||
h.Write([]byte(shortBuf))
|
||||
binary.LittleEndian.PutUint16(shortBuf, uint16(id.transProto))
|
||||
h.Write([]byte(shortBuf))
|
||||
@@ -998,12 +1061,12 @@ func (ct *ConnTrack) originalDst(epID TransportEndpointID, netProto tcpip.Networ
|
||||
// Lookup the connection. The reply's original destination
|
||||
// describes the original address.
|
||||
tid := tupleID{
|
||||
srcAddr: epID.LocalAddress,
|
||||
srcPort: epID.LocalPort,
|
||||
dstAddr: epID.RemoteAddress,
|
||||
dstPort: epID.RemotePort,
|
||||
transProto: transProto,
|
||||
netProto: netProto,
|
||||
srcAddr: epID.LocalAddress,
|
||||
srcPortOrEchoRequestIdent: epID.LocalPort,
|
||||
dstAddr: epID.RemoteAddress,
|
||||
dstPortOrEchoReplyIdent: epID.RemotePort,
|
||||
transProto: transProto,
|
||||
netProto: netProto,
|
||||
}
|
||||
t := ct.connForTID(tid)
|
||||
if t == nil {
|
||||
@@ -1019,5 +1082,5 @@ func (ct *ConnTrack) originalDst(epID TransportEndpointID, netProto tcpip.Networ
|
||||
}
|
||||
|
||||
id := t.conn.original.id()
|
||||
return id.dstAddr, id.dstPort, nil
|
||||
return id.dstAddr, id.dstPortOrEchoReplyIdent, nil
|
||||
}
|
||||
|
||||
@@ -176,48 +176,57 @@ type SNATTarget struct {
|
||||
}
|
||||
|
||||
func dnatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcpip.Address) (RuleVerdict, int) {
|
||||
return natAction(pkt, hook, r, portRange{start: port, size: 1}, address, true /* dnat */)
|
||||
return natAction(pkt, hook, r, portOrIdentRange{start: port, size: 1}, address, true /* dnat */)
|
||||
}
|
||||
|
||||
func targetPortRangeForTCPAndUDP(originalSrcPort uint16) portOrIdentRange {
|
||||
// As per iptables(8),
|
||||
//
|
||||
// If no port range is specified, then source ports below 512 will be
|
||||
// mapped to other ports below 512: those between 512 and 1023 inclusive
|
||||
// will be mapped to ports below 1024, and other ports will be mapped to
|
||||
// 1024 or above.
|
||||
switch {
|
||||
case originalSrcPort < 512:
|
||||
return portOrIdentRange{start: 1, size: 511}
|
||||
case originalSrcPort < 1024:
|
||||
return portOrIdentRange{start: 1, size: 1023}
|
||||
default:
|
||||
return portOrIdentRange{start: 1024, size: math.MaxUint16 - 1023}
|
||||
}
|
||||
}
|
||||
|
||||
func snatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcpip.Address) (RuleVerdict, int) {
|
||||
ports := portRange{start: port, size: 1}
|
||||
if port == 0 {
|
||||
// As per iptables(8),
|
||||
//
|
||||
// If no port range is specified, then source ports below 512 will be
|
||||
// mapped to other ports below 512: those between 512 and 1023 inclusive
|
||||
// will be mapped to ports below 1024, and other ports will be mapped to
|
||||
// 1024 or above.
|
||||
switch protocol := pkt.TransportProtocolNumber; protocol {
|
||||
case header.UDPProtocolNumber:
|
||||
port = header.UDP(pkt.TransportHeader().View()).SourcePort()
|
||||
case header.TCPProtocolNumber:
|
||||
port = header.TCP(pkt.TransportHeader().View()).SourcePort()
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported transport protocol = %d", pkt.TransportProtocolNumber))
|
||||
}
|
||||
portsOrIdents := portOrIdentRange{start: port, size: 1}
|
||||
|
||||
switch {
|
||||
case port < 512:
|
||||
ports = portRange{start: 1, size: 511}
|
||||
case port < 1024:
|
||||
ports = portRange{start: 1, size: 1023}
|
||||
default:
|
||||
ports = portRange{start: 1024, size: math.MaxUint16 - 1023}
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.UDPProtocolNumber:
|
||||
if port == 0 {
|
||||
portsOrIdents = targetPortRangeForTCPAndUDP(header.UDP(pkt.TransportHeader().View()).SourcePort())
|
||||
}
|
||||
case header.TCPProtocolNumber:
|
||||
if port == 0 {
|
||||
portsOrIdents = targetPortRangeForTCPAndUDP(header.TCP(pkt.TransportHeader().View()).SourcePort())
|
||||
}
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
// Allow NAT-ing to any 16-bit value for ICMP's Ident field to match Linux
|
||||
// behaviour.
|
||||
//
|
||||
// https://github.com/torvalds/linux/blob/58e1100fdc5990b0cc0d4beaf2562a92e621ac7d/net/netfilter/nf_nat_core.c#L391
|
||||
portsOrIdents = portOrIdentRange{start: 0, size: math.MaxUint16 + 1}
|
||||
}
|
||||
|
||||
return natAction(pkt, hook, r, ports, address, false /* dnat */)
|
||||
return natAction(pkt, hook, r, portsOrIdents, address, false /* dnat */)
|
||||
}
|
||||
|
||||
func natAction(pkt *PacketBuffer, hook Hook, r *Route, ports portRange, address tcpip.Address, dnat bool) (RuleVerdict, int) {
|
||||
func natAction(pkt *PacketBuffer, hook Hook, r *Route, portsOrIdents portOrIdentRange, address tcpip.Address, dnat bool) (RuleVerdict, int) {
|
||||
// Drop the packet if network and transport header are not set.
|
||||
if pkt.NetworkHeader().View().IsEmpty() || pkt.TransportHeader().View().IsEmpty() {
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
if t := pkt.tuple; t != nil {
|
||||
t.conn.performNAT(pkt, hook, r, ports, address, dnat)
|
||||
t.conn.performNAT(pkt, hook, r, portsOrIdents, address, dnat)
|
||||
return RuleAccept, 0
|
||||
}
|
||||
|
||||
@@ -280,30 +289,48 @@ 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.ChecksummableTransport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPort uint16, newAddr tcpip.Address) {
|
||||
if updateSRCFields {
|
||||
if fullChecksum {
|
||||
t.SetSourcePortWithChecksumUpdate(newPort)
|
||||
} else {
|
||||
t.SetSourcePort(newPort)
|
||||
}
|
||||
} else {
|
||||
if fullChecksum {
|
||||
t.SetDestinationPortWithChecksumUpdate(newPort)
|
||||
} else {
|
||||
t.SetDestinationPort(newPort)
|
||||
}
|
||||
}
|
||||
|
||||
if updatePseudoHeader {
|
||||
var oldAddr tcpip.Address
|
||||
func rewritePacket(n header.Network, t header.Transport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPort uint16, newAddr tcpip.Address) {
|
||||
switch t := t.(type) {
|
||||
case header.ChecksummableTransport:
|
||||
if updateSRCFields {
|
||||
oldAddr = n.SourceAddress()
|
||||
if fullChecksum {
|
||||
t.SetSourcePortWithChecksumUpdate(newPort)
|
||||
} else {
|
||||
t.SetSourcePort(newPort)
|
||||
}
|
||||
} else {
|
||||
oldAddr = n.DestinationAddress()
|
||||
if fullChecksum {
|
||||
t.SetDestinationPortWithChecksumUpdate(newPort)
|
||||
} else {
|
||||
t.SetDestinationPort(newPort)
|
||||
}
|
||||
}
|
||||
|
||||
t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr, fullChecksum)
|
||||
if updatePseudoHeader {
|
||||
var oldAddr tcpip.Address
|
||||
if updateSRCFields {
|
||||
oldAddr = n.SourceAddress()
|
||||
} else {
|
||||
oldAddr = n.DestinationAddress()
|
||||
}
|
||||
|
||||
t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr, fullChecksum)
|
||||
}
|
||||
case header.ICMPv4:
|
||||
switch icmpType := t.Type(); icmpType {
|
||||
case header.ICMPv4Echo:
|
||||
if updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPort)
|
||||
}
|
||||
case header.ICMPv4EchoReply:
|
||||
if !updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPort)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv4 type = %d", icmpType))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled transport = %#v", t))
|
||||
}
|
||||
|
||||
if checksummableNetHeader, ok := n.(header.ChecksummableNetwork); ok {
|
||||
|
||||
@@ -40,6 +40,7 @@ go_test(
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/tests/utils",
|
||||
"//pkg/tcpip/testutil",
|
||||
"//pkg/tcpip/transport/icmp",
|
||||
"//pkg/tcpip/transport/tcp",
|
||||
"//pkg/tcpip/transport/udp",
|
||||
"//pkg/waiter",
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/tests/utils"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/testutil"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
@@ -1300,6 +1301,15 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
dnatTarget = natType{
|
||||
name: "DNAT",
|
||||
setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, _, dnatAddr tcpip.Address, dnatPort uint16) {
|
||||
t.Helper()
|
||||
|
||||
setupDNAT(t, s, netProto, transProto, &stack.DNATTarget{NetworkProtocol: netProto, Addr: dnatAddr, Port: dnatPort})
|
||||
},
|
||||
}
|
||||
|
||||
dnatTypes = []natType{
|
||||
{
|
||||
name: "Redirect",
|
||||
@@ -1309,14 +1319,7 @@ var (
|
||||
setupDNAT(t, s, netProto, transProto, &stack.RedirectTarget{NetworkProtocol: netProto, Port: dnatPort})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DNAT",
|
||||
setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, _, dnatAddr tcpip.Address, dnatPort uint16) {
|
||||
t.Helper()
|
||||
|
||||
setupDNAT(t, s, netProto, transProto, &stack.DNATTarget{NetworkProtocol: netProto, Addr: dnatAddr, Port: dnatPort})
|
||||
},
|
||||
},
|
||||
dnatTarget,
|
||||
}
|
||||
|
||||
twiceNATTypes = []natType{
|
||||
@@ -1339,6 +1342,140 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func TestNATEcho(t *testing.T) {
|
||||
const ident = 1
|
||||
|
||||
v4EchoPkt := func(srcAddr, dstAddr tcpip.Address, reply bool) buffer.View {
|
||||
icmpType := header.ICMPv4Echo
|
||||
if reply {
|
||||
icmpType = header.ICMPv4EchoReply
|
||||
}
|
||||
|
||||
return icmpv4Packet(srcAddr, dstAddr, icmpType, ident)
|
||||
}
|
||||
|
||||
checkV4EchoPkt := func(t *testing.T, v buffer.View, srcAddr, dstAddr tcpip.Address, reply bool) {
|
||||
t.Helper()
|
||||
|
||||
icmpType := header.ICMPv4Echo
|
||||
if reply {
|
||||
icmpType = header.ICMPv4EchoReply
|
||||
}
|
||||
|
||||
checker.IPv4(t, v,
|
||||
checker.SrcAddr(srcAddr),
|
||||
checker.DstAddr(dstAddr),
|
||||
checker.ICMPv4(
|
||||
checker.ICMPv4Type(icmpType),
|
||||
checker.ICMPv4Checksum(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
type natTypeTest struct {
|
||||
name string
|
||||
natTypes []natType
|
||||
requestSrc, requestDst tcpip.Address
|
||||
expectedRequestSrc, expectedRequestDst tcpip.Address
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
transProto tcpip.TransportProtocolNumber
|
||||
echoPkt func(srcAddr, dstAddr tcpip.Address, reply bool) buffer.View
|
||||
checkEchoPkt func(t *testing.T, v buffer.View, srcAddr, dstAddr tcpip.Address, reply bool)
|
||||
|
||||
natTypes []natTypeTest
|
||||
}{
|
||||
{
|
||||
name: "IPv4",
|
||||
netProto: header.IPv4ProtocolNumber,
|
||||
transProto: header.ICMPv4ProtocolNumber,
|
||||
echoPkt: v4EchoPkt,
|
||||
checkEchoPkt: checkV4EchoPkt,
|
||||
|
||||
natTypes: []natTypeTest{
|
||||
{
|
||||
name: "SNAT",
|
||||
natTypes: snatTypes,
|
||||
requestSrc: utils.Host2IPv4Addr.AddressWithPrefix.Address,
|
||||
requestDst: utils.Host1IPv4Addr.AddressWithPrefix.Address,
|
||||
expectedRequestSrc: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address,
|
||||
expectedRequestDst: utils.Host1IPv4Addr.AddressWithPrefix.Address,
|
||||
},
|
||||
{
|
||||
name: "DNAT",
|
||||
natTypes: []natType{dnatTarget},
|
||||
requestSrc: utils.Host2IPv4Addr.AddressWithPrefix.Address,
|
||||
requestDst: utils.RouterNIC2IPv4Addr.AddressWithPrefix.Address,
|
||||
expectedRequestSrc: utils.Host2IPv4Addr.AddressWithPrefix.Address,
|
||||
expectedRequestDst: utils.Host1IPv4Addr.AddressWithPrefix.Address,
|
||||
},
|
||||
{
|
||||
name: "Twice-NAT",
|
||||
natTypes: twiceNATTypes,
|
||||
requestSrc: utils.Host2IPv4Addr.AddressWithPrefix.Address,
|
||||
requestDst: utils.RouterNIC2IPv4Addr.AddressWithPrefix.Address,
|
||||
expectedRequestSrc: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address,
|
||||
expectedRequestDst: utils.Host1IPv4Addr.AddressWithPrefix.Address,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, natTypeTest := range test.natTypes {
|
||||
t.Run(natTypeTest.name, func(t *testing.T) {
|
||||
for _, natType := range natTypeTest.natTypes {
|
||||
t.Run(natType.name, func(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol4, icmp.NewProtocol6},
|
||||
})
|
||||
|
||||
ep1 := channel.New(1, header.IPv6MinimumMTU, "")
|
||||
ep2 := channel.New(1, header.IPv6MinimumMTU, "")
|
||||
utils.SetupRouterStack(t, s, ep1, ep2)
|
||||
|
||||
natType.setupNAT(t, s, test.netProto, test.transProto, natTypeTest.expectedRequestSrc, natTypeTest.expectedRequestDst, 0 /* dnatPort */)
|
||||
|
||||
// Send and check the Echo Request.
|
||||
{
|
||||
ep2.InjectInbound(test.netProto, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: test.echoPkt(natTypeTest.requestSrc, natTypeTest.requestDst, false /* reply */).ToVectorisedView(),
|
||||
}))
|
||||
pkt, ok := ep1.Read()
|
||||
if !ok {
|
||||
t.Fatal("expected to read a packet on ep1")
|
||||
}
|
||||
test.checkEchoPkt(t, stack.PayloadSince(pkt.Pkt.NetworkHeader()), natTypeTest.expectedRequestSrc, natTypeTest.expectedRequestDst, false /* reply */)
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// Send and check the Echo Reply.
|
||||
{
|
||||
ep1.InjectInbound(test.netProto, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: test.echoPkt(natTypeTest.expectedRequestDst, natTypeTest.expectedRequestSrc, true /* reply */).ToVectorisedView(),
|
||||
}))
|
||||
pkt, ok := ep2.Read()
|
||||
if !ok {
|
||||
t.Fatal("expected to read a packet on ep2")
|
||||
}
|
||||
test.checkEchoPkt(t, stack.PayloadSince(pkt.Pkt.NetworkHeader()), natTypeTest.requestDst, natTypeTest.requestSrc, true /* reply */)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNAT(t *testing.T) {
|
||||
const listenPort uint16 = 8080
|
||||
|
||||
@@ -1856,6 +1993,23 @@ func tcpv4Packet(srcAddr, dstAddr tcpip.Address, srcPort, dstPort uint16, dataSi
|
||||
return hdr.View()
|
||||
}
|
||||
|
||||
func icmpv4Packet(srcAddr, dstAddr tcpip.Address, icmpType header.ICMPv4Type, ident uint16) buffer.View {
|
||||
hdr := buffer.NewPrependable(header.IPv4MinimumSize + header.ICMPv4MinimumSize)
|
||||
icmp := header.ICMPv4(hdr.Prepend(header.ICMPv4MinimumSize))
|
||||
icmp.SetType(icmpType)
|
||||
icmp.SetIdent(ident)
|
||||
icmp.SetChecksum(0)
|
||||
icmp.SetChecksum(^header.Checksum(icmp, 0))
|
||||
encodeIPv4Header(
|
||||
hdr.Prepend(header.IPv4MinimumSize),
|
||||
hdr.UsedLength(),
|
||||
header.ICMPv4ProtocolNumber,
|
||||
srcAddr,
|
||||
dstAddr,
|
||||
)
|
||||
return hdr.View()
|
||||
}
|
||||
|
||||
func udpv6Packet(srcAddr, dstAddr tcpip.Address, srcPort, dstPort uint16, dataSize int) buffer.View {
|
||||
udpSize := header.UDPMinimumSize + dataSize
|
||||
hdr := buffer.NewPrependable(header.IPv6MinimumSize + udpSize)
|
||||
@@ -2292,33 +2446,77 @@ func TestNATICMPError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
func TestSNATHandlePortOrIdentConflicts(t *testing.T) {
|
||||
const dstPort = 5432
|
||||
|
||||
type portRange struct {
|
||||
type portOrIdentRange struct {
|
||||
first uint16
|
||||
last uint16
|
||||
}
|
||||
|
||||
type transportTypeTest struct {
|
||||
name string
|
||||
proto tcpip.TransportProtocolNumber
|
||||
buf func(tcpip.Address, uint16) buffer.View
|
||||
checkNATed func(*testing.T, buffer.View, uint16, bool, portRange)
|
||||
type srcPortOrIdentRangeTest struct {
|
||||
name string
|
||||
originalRange portOrIdentRange
|
||||
targetRange portOrIdentRange
|
||||
}
|
||||
|
||||
compareSrcPort := func(t *testing.T, gotPort uint16, originalSrcPort uint16, firstPacket bool, expectedRange portRange) {
|
||||
srcPortRanges := []srcPortOrIdentRangeTest{
|
||||
{
|
||||
name: "Less than 512",
|
||||
originalRange: portOrIdentRange{first: 1, last: 511},
|
||||
targetRange: portOrIdentRange{first: 1, last: 511},
|
||||
},
|
||||
{
|
||||
name: "Greater than or equal to 512 but less than 1024",
|
||||
originalRange: portOrIdentRange{first: 512, last: 1023},
|
||||
targetRange: portOrIdentRange{first: 1, last: 1023},
|
||||
},
|
||||
{
|
||||
name: "Greater than or equal to 1024",
|
||||
originalRange: portOrIdentRange{first: 1024, last: math.MaxUint16},
|
||||
targetRange: portOrIdentRange{first: 1024, last: math.MaxUint16},
|
||||
},
|
||||
}
|
||||
|
||||
// Unlike TCP/UDP, the Ident may be mapped to any 16-bit value.
|
||||
identRanges := []srcPortOrIdentRangeTest{
|
||||
{
|
||||
name: "Less than 512",
|
||||
originalRange: portOrIdentRange{first: 0, last: 511},
|
||||
targetRange: portOrIdentRange{first: 0, last: math.MaxUint16},
|
||||
},
|
||||
{
|
||||
name: "Greater than or equal to 512 but less than 1024",
|
||||
originalRange: portOrIdentRange{first: 512, last: 1023},
|
||||
targetRange: portOrIdentRange{first: 0, last: math.MaxUint16},
|
||||
},
|
||||
{
|
||||
name: "Greater than or equal to 1024",
|
||||
originalRange: portOrIdentRange{first: 1024, last: math.MaxUint16},
|
||||
targetRange: portOrIdentRange{first: 0, last: math.MaxUint16},
|
||||
},
|
||||
}
|
||||
|
||||
type transportTypeTest struct {
|
||||
name string
|
||||
proto tcpip.TransportProtocolNumber
|
||||
buf func(tcpip.Address, uint16) buffer.View
|
||||
checkNATed func(*testing.T, buffer.View, uint16, bool, portOrIdentRange)
|
||||
srcPortOrIdentRanges []srcPortOrIdentRangeTest
|
||||
}
|
||||
|
||||
compareSrcPortOrIdent := func(t *testing.T, gotPort uint16, originalSrcPort uint16, firstPacket bool, expectedRange portOrIdentRange) {
|
||||
t.Helper()
|
||||
|
||||
if firstPacket {
|
||||
if gotPort != originalSrcPort {
|
||||
t.Errorf("got port = %d, want = %d", gotPort, originalSrcPort)
|
||||
t.Errorf("got port/ident = %d, want = %d", gotPort, originalSrcPort)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if gotPort < expectedRange.first || gotPort > expectedRange.last {
|
||||
t.Errorf("got port = %d, want in range [%d, %d]", gotPort, expectedRange.first, expectedRange.last)
|
||||
t.Errorf("got port/ident = %d, want in range [%d, %d]", gotPort, expectedRange.first, expectedRange.last)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2345,7 +2543,7 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
buf: func(srcAddr tcpip.Address, srcPort uint16) buffer.View {
|
||||
return udpv4Packet(srcAddr, utils.Host1IPv4Addr.AddressWithPrefix.Address, srcPort, dstPort, 0 /* dataSize */)
|
||||
},
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalSrcPort uint16, firstPacket bool, expectedRange portRange) {
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalSrcPort uint16, firstPacket bool, expectedRange portOrIdentRange) {
|
||||
checker.IPv4(t, v,
|
||||
checker.SrcAddr(utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address),
|
||||
checker.DstAddr(utils.Host1IPv4Addr.AddressWithPrefix.Address),
|
||||
@@ -2355,9 +2553,10 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
)
|
||||
|
||||
if !t.Failed() {
|
||||
compareSrcPort(t, header.UDP(header.IPv4(v).Payload()).SourcePort(), originalSrcPort, firstPacket, expectedRange)
|
||||
compareSrcPortOrIdent(t, header.UDP(header.IPv4(v).Payload()).SourcePort(), originalSrcPort, firstPacket, expectedRange)
|
||||
}
|
||||
},
|
||||
srcPortOrIdentRanges: srcPortRanges,
|
||||
},
|
||||
{
|
||||
name: "TCP",
|
||||
@@ -2365,7 +2564,7 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
buf: func(srcAddr tcpip.Address, srcPort uint16) buffer.View {
|
||||
return tcpv4Packet(srcAddr, utils.Host1IPv4Addr.AddressWithPrefix.Address, srcPort, dstPort, 0 /* dataSize */)
|
||||
},
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalSrcPort uint16, firstPacket bool, expectedRange portRange) {
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalSrcPort uint16, firstPacket bool, expectedRange portOrIdentRange) {
|
||||
checker.IPv4(t, v,
|
||||
checker.SrcAddr(utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address),
|
||||
checker.DstAddr(utils.Host1IPv4Addr.AddressWithPrefix.Address),
|
||||
@@ -2375,9 +2574,32 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
)
|
||||
|
||||
if !t.Failed() {
|
||||
compareSrcPort(t, header.TCP(header.IPv4(v).Payload()).SourcePort(), originalSrcPort, firstPacket, expectedRange)
|
||||
compareSrcPortOrIdent(t, header.TCP(header.IPv4(v).Payload()).SourcePort(), originalSrcPort, firstPacket, expectedRange)
|
||||
}
|
||||
},
|
||||
srcPortOrIdentRanges: srcPortRanges,
|
||||
},
|
||||
{
|
||||
name: "ICMP Echo",
|
||||
proto: header.ICMPv4ProtocolNumber,
|
||||
buf: func(srcAddr tcpip.Address, ident uint16) buffer.View {
|
||||
return icmpv4Packet(srcAddr, utils.Host1IPv4Addr.AddressWithPrefix.Address, header.ICMPv4Echo, ident)
|
||||
},
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalIdent uint16, firstPacket bool, expectedRange portOrIdentRange) {
|
||||
checker.IPv4(t, v,
|
||||
checker.SrcAddr(utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address),
|
||||
checker.DstAddr(utils.Host1IPv4Addr.AddressWithPrefix.Address),
|
||||
checker.ICMPv4(
|
||||
checker.ICMPv4Type(header.ICMPv4Echo),
|
||||
checker.ICMPv4Checksum(),
|
||||
),
|
||||
)
|
||||
|
||||
if !t.Failed() {
|
||||
compareSrcPortOrIdent(t, header.ICMPv4(header.IPv4(v).Payload()).Ident(), originalIdent, firstPacket, expectedRange)
|
||||
}
|
||||
},
|
||||
srcPortOrIdentRanges: identRanges,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2397,7 +2619,7 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
buf: func(srcAddr tcpip.Address, srcPort uint16) buffer.View {
|
||||
return udpv6Packet(srcAddr, utils.Host1IPv6Addr.AddressWithPrefix.Address, srcPort, dstPort, 0 /* dataSize */)
|
||||
},
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalSrcPort uint16, firstPacket bool, expectedRange portRange) {
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalSrcPort uint16, firstPacket bool, expectedRange portOrIdentRange) {
|
||||
checker.IPv6(t, v,
|
||||
checker.SrcAddr(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address),
|
||||
checker.DstAddr(utils.Host1IPv6Addr.AddressWithPrefix.Address),
|
||||
@@ -2407,9 +2629,10 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
)
|
||||
|
||||
if !t.Failed() {
|
||||
compareSrcPort(t, header.UDP(header.IPv6(v).Payload()).SourcePort(), originalSrcPort, firstPacket, expectedRange)
|
||||
compareSrcPortOrIdent(t, header.UDP(header.IPv6(v).Payload()).SourcePort(), originalSrcPort, firstPacket, expectedRange)
|
||||
}
|
||||
},
|
||||
srcPortOrIdentRanges: srcPortRanges,
|
||||
},
|
||||
{
|
||||
name: "TCP",
|
||||
@@ -2417,7 +2640,7 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
buf: func(srcAddr tcpip.Address, srcPort uint16) buffer.View {
|
||||
return tcpv6Packet(srcAddr, utils.Host1IPv6Addr.AddressWithPrefix.Address, srcPort, dstPort, 0 /* dataSize */)
|
||||
},
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalSrcPort uint16, firstPacket bool, expectedRange portRange) {
|
||||
checkNATed: func(t *testing.T, v buffer.View, originalSrcPort uint16, firstPacket bool, expectedRange portOrIdentRange) {
|
||||
checker.IPv6(t, v,
|
||||
checker.SrcAddr(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address),
|
||||
checker.DstAddr(utils.Host1IPv6Addr.AddressWithPrefix.Address),
|
||||
@@ -2427,9 +2650,10 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
)
|
||||
|
||||
if !t.Failed() {
|
||||
compareSrcPort(t, header.TCP(header.IPv6(v).Payload()).SourcePort(), originalSrcPort, firstPacket, expectedRange)
|
||||
compareSrcPortOrIdent(t, header.TCP(header.IPv6(v).Payload()).SourcePort(), originalSrcPort, firstPacket, expectedRange)
|
||||
}
|
||||
},
|
||||
srcPortOrIdentRanges: srcPortRanges,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2453,38 +2677,16 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
srcPortRanges := []struct {
|
||||
name string
|
||||
originalRange portRange
|
||||
targetRange portRange
|
||||
}{
|
||||
{
|
||||
name: "Less than 512",
|
||||
originalRange: portRange{first: 1, last: 511},
|
||||
targetRange: portRange{first: 1, last: 511},
|
||||
},
|
||||
{
|
||||
name: "Greater than or equal to 512 but less than 1024",
|
||||
originalRange: portRange{first: 512, last: 1023},
|
||||
targetRange: portRange{first: 1, last: 1023},
|
||||
},
|
||||
{
|
||||
name: "Greater than or equal to 1024",
|
||||
originalRange: portRange{first: 1024, last: math.MaxUint16},
|
||||
targetRange: portRange{first: 1024, last: math.MaxUint16},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, transportType := range test.transportTypes {
|
||||
t.Run(transportType.name, func(t *testing.T) {
|
||||
for _, natType := range natTypes {
|
||||
t.Run(natType.name, func(t *testing.T) {
|
||||
for _, srcPortRange := range srcPortRanges {
|
||||
t.Run(srcPortRange.name, func(t *testing.T) {
|
||||
for _, srcPort := range [2]uint16{srcPortRange.originalRange.first, srcPortRange.originalRange.last} {
|
||||
t.Run(fmt.Sprintf("OriginalSrcPort=%d", srcPort), func(t *testing.T) {
|
||||
for _, srcPortOrIdentRange := range transportType.srcPortOrIdentRanges {
|
||||
t.Run(srcPortOrIdentRange.name, func(t *testing.T) {
|
||||
for _, srcPortOrIdent := range [2]uint16{srcPortOrIdentRange.originalRange.first, srcPortOrIdentRange.originalRange.last} {
|
||||
t.Run(fmt.Sprintf("OriginalSrcPortOrIdent=%d", srcPortOrIdent), func(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol, tcp.NewProtocol},
|
||||
@@ -2548,7 +2750,7 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
for i, srcAddr := range test.srcAddrs {
|
||||
t.Run(fmt.Sprintf("Packet#%d", i), func(t *testing.T) {
|
||||
ep2.InjectInbound(test.netProto, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: transportType.buf(srcAddr, srcPort).ToVectorisedView(),
|
||||
Data: transportType.buf(srcAddr, srcPortOrIdent).ToVectorisedView(),
|
||||
}))
|
||||
|
||||
pkt, ok := ep1.Read()
|
||||
@@ -2556,7 +2758,7 @@ func TestSNATHandlePortConflicts(t *testing.T) {
|
||||
t.Fatal("expected to read a packet on ep1")
|
||||
}
|
||||
pktView := stack.PayloadSince(pkt.Pkt.NetworkHeader())
|
||||
transportType.checkNATed(t, pktView, srcPort, i == 0, srcPortRange.targetRange)
|
||||
transportType.checkNATed(t, pktView, srcPortOrIdent, i == 0, srcPortOrIdentRange.targetRange)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user