mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support naive Masquerade NAT target
* Does not accept a port range (Issue #5772). * Does not support checking for tuple conflits (Issue #5773). PiperOrigin-RevId: 399524088
This commit is contained in:
committed by
gVisor bot
parent
e251f6cc5c
commit
ed083bac40
@@ -466,7 +466,7 @@ func (e *endpoint) writePacket(r *stack.Route, pkt *stack.PacketBuffer, headerIn
|
||||
// Postrouting NAT can only change the source address, and does not alter the
|
||||
// route or outgoing interface of the packet.
|
||||
outNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckPostrouting(pkt, r, outNicName); !ok {
|
||||
if ok := e.protocol.stack.IPTables().CheckPostrouting(pkt, r, e, outNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
e.stats.ip.IPTablesPostroutingDropped.Increment()
|
||||
return nil
|
||||
@@ -576,7 +576,7 @@ func (e *endpoint) WritePackets(r *stack.Route, pkts stack.PacketBufferList, par
|
||||
// We ignore the list of NAT-ed packets here because Postrouting NAT can only
|
||||
// change the source address, and does not alter the route or outgoing
|
||||
// interface of the packet.
|
||||
postroutingDropped, _ := e.protocol.stack.IPTables().CheckPostroutingPackets(pkts, r, outNicName)
|
||||
postroutingDropped, _ := e.protocol.stack.IPTables().CheckPostroutingPackets(pkts, r, e, outNicName)
|
||||
stats.IPTablesPostroutingDropped.IncrementBy(uint64(len(postroutingDropped)))
|
||||
for pkt := range postroutingDropped {
|
||||
pkts.Remove(pkt)
|
||||
|
||||
@@ -788,7 +788,7 @@ func (e *endpoint) writePacket(r *stack.Route, pkt *stack.PacketBuffer, protocol
|
||||
// Postrouting NAT can only change the source address, and does not alter the
|
||||
// route or outgoing interface of the packet.
|
||||
outNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckPostrouting(pkt, r, outNicName); !ok {
|
||||
if ok := e.protocol.stack.IPTables().CheckPostrouting(pkt, r, e, outNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
e.stats.ip.IPTablesPostroutingDropped.Increment()
|
||||
return nil
|
||||
@@ -897,7 +897,7 @@ func (e *endpoint) WritePackets(r *stack.Route, pkts stack.PacketBufferList, par
|
||||
// We ignore the list of NAT-ed packets here because Postrouting NAT can only
|
||||
// change the source address, and does not alter the route or outgoing
|
||||
// interface of the packet.
|
||||
postroutingDropped, _ := e.protocol.stack.IPTables().CheckPostroutingPackets(pkts, r, outNicName)
|
||||
postroutingDropped, _ := e.protocol.stack.IPTables().CheckPostroutingPackets(pkts, r, e, outNicName)
|
||||
stats.IPTablesPostroutingDropped.IncrementBy(uint64(len(postroutingDropped)))
|
||||
for pkt := range postroutingDropped {
|
||||
pkts.Remove(pkt)
|
||||
|
||||
@@ -310,8 +310,8 @@ func (it *IPTables) CheckOutput(pkt *PacketBuffer, r *Route, outNicName string)
|
||||
// must be dropped if false is returned.
|
||||
//
|
||||
// Precondition: The packet's network and transport header must be set.
|
||||
func (it *IPTables) CheckPostrouting(pkt *PacketBuffer, r *Route, outNicName string) bool {
|
||||
return it.check(Postrouting, pkt, r, nil /* addressEP */, "" /* inNicName */, outNicName)
|
||||
func (it *IPTables) CheckPostrouting(pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, outNicName string) bool {
|
||||
return it.check(Postrouting, pkt, r, addressEP, "" /* inNicName */, outNicName)
|
||||
}
|
||||
|
||||
// check runs pkt through the rules for hook. It returns true when the packet
|
||||
@@ -431,7 +431,7 @@ func (it *IPTables) startReaper(interval time.Duration) {
|
||||
//
|
||||
// Precondition: The packets' network and transport header must be set.
|
||||
func (it *IPTables) CheckOutputPackets(pkts PacketBufferList, r *Route, outNicName string) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
return it.checkPackets(Output, pkts, r, outNicName)
|
||||
return it.checkPackets(Output, pkts, r, nil /* addressEP */, outNicName)
|
||||
}
|
||||
|
||||
// CheckPostroutingPackets performs the postrouting hook on the packets.
|
||||
@@ -439,8 +439,8 @@ func (it *IPTables) CheckOutputPackets(pkts PacketBufferList, r *Route, outNicNa
|
||||
// Returns a map of packets that must be dropped.
|
||||
//
|
||||
// Precondition: The packets' network and transport header must be set.
|
||||
func (it *IPTables) CheckPostroutingPackets(pkts PacketBufferList, r *Route, outNicName string) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
return it.checkPackets(Postrouting, pkts, r, outNicName)
|
||||
func (it *IPTables) CheckPostroutingPackets(pkts PacketBufferList, r *Route, addressEP AddressableEndpoint, outNicName string) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
return it.checkPackets(Postrouting, pkts, r, addressEP, outNicName)
|
||||
}
|
||||
|
||||
// checkPackets runs pkts through the rules for hook and returns a map of
|
||||
@@ -450,10 +450,10 @@ func (it *IPTables) CheckPostroutingPackets(pkts PacketBufferList, r *Route, out
|
||||
// dropped.
|
||||
//
|
||||
// Precondition: The packets' network and transport header must be set.
|
||||
func (it *IPTables) checkPackets(hook Hook, pkts PacketBufferList, r *Route, outNicName string) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
func (it *IPTables) checkPackets(hook Hook, pkts PacketBufferList, r *Route, addressEP AddressableEndpoint, outNicName string) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
|
||||
if !pkt.NatDone {
|
||||
if ok := it.check(hook, pkt, r, nil /* addressEP */, "" /* inNicName */, outNicName); !ok {
|
||||
if ok := it.check(hook, pkt, r, addressEP, "" /* inNicName */, outNicName); !ok {
|
||||
if drop == nil {
|
||||
drop = make(map[*PacketBuffer]struct{})
|
||||
}
|
||||
|
||||
@@ -181,15 +181,7 @@ type SNATTarget struct {
|
||||
NetworkProtocol tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (st *SNATTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Route, _ AddressableEndpoint) (RuleVerdict, int) {
|
||||
// Sanity check.
|
||||
if st.NetworkProtocol != pkt.NetworkProtocolNumber {
|
||||
panic(fmt.Sprintf(
|
||||
"SNATTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d",
|
||||
st.NetworkProtocol, pkt.NetworkProtocolNumber))
|
||||
}
|
||||
|
||||
func snatAction(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Route, port uint16, address tcpip.Address) (RuleVerdict, int) {
|
||||
// Packet is already manipulated.
|
||||
if pkt.NatDone {
|
||||
return RuleAccept, 0
|
||||
@@ -200,16 +192,8 @@ func (st *SNATTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Rou
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
switch hook {
|
||||
case Postrouting, Input:
|
||||
case Prerouting, Output, Forward:
|
||||
panic(fmt.Sprintf("%s not supported", hook))
|
||||
default:
|
||||
panic(fmt.Sprintf("%s unrecognized", hook))
|
||||
}
|
||||
|
||||
port := st.Port
|
||||
|
||||
// TODO(https://gvisor.dev/issue/5773): If the port is in use, pick a
|
||||
// different port.
|
||||
if port == 0 {
|
||||
switch protocol := pkt.TransportProtocolNumber; protocol {
|
||||
case header.UDPProtocolNumber:
|
||||
@@ -228,13 +212,69 @@ func (st *SNATTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Rou
|
||||
// tracking.
|
||||
//
|
||||
// Does nothing if the protocol does not support connection tracking.
|
||||
if conn := ct.insertSNATConn(pkt, hook, port, st.Addr); conn != nil {
|
||||
if conn := ct.insertSNATConn(pkt, hook, port, address); conn != nil {
|
||||
ct.handlePacket(pkt, hook, r)
|
||||
}
|
||||
|
||||
return RuleAccept, 0
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (st *SNATTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Route, _ AddressableEndpoint) (RuleVerdict, int) {
|
||||
// Sanity check.
|
||||
if st.NetworkProtocol != pkt.NetworkProtocolNumber {
|
||||
panic(fmt.Sprintf(
|
||||
"SNATTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d",
|
||||
st.NetworkProtocol, pkt.NetworkProtocolNumber))
|
||||
}
|
||||
|
||||
switch hook {
|
||||
case Postrouting, Input:
|
||||
case Prerouting, Output, Forward:
|
||||
panic(fmt.Sprintf("%s not supported", hook))
|
||||
default:
|
||||
panic(fmt.Sprintf("%s unrecognized", hook))
|
||||
}
|
||||
|
||||
return snatAction(pkt, ct, hook, r, st.Port, st.Addr)
|
||||
}
|
||||
|
||||
// MasqueradeTarget modifies the source port/IP in the outgoing packets.
|
||||
type MasqueradeTarget struct {
|
||||
// NetworkProtocol is the network protocol the target is used with. It
|
||||
// is immutable.
|
||||
NetworkProtocol tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (mt *MasqueradeTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Route, addressEP AddressableEndpoint) (RuleVerdict, int) {
|
||||
// Sanity check.
|
||||
if mt.NetworkProtocol != pkt.NetworkProtocolNumber {
|
||||
panic(fmt.Sprintf(
|
||||
"MasqueradeTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d",
|
||||
mt.NetworkProtocol, pkt.NetworkProtocolNumber))
|
||||
}
|
||||
|
||||
switch hook {
|
||||
case Postrouting:
|
||||
case Prerouting, Input, Forward, Output:
|
||||
panic(fmt.Sprintf("masquerade target is supported only on postrouting hook; hook = %d", hook))
|
||||
default:
|
||||
panic(fmt.Sprintf("%s unrecognized", hook))
|
||||
}
|
||||
|
||||
// addressEP is expected to be set for the postrouting hook.
|
||||
ep := addressEP.AcquireOutgoingPrimaryAddress(pkt.Network().DestinationAddress(), false /* allowExpired */)
|
||||
if ep == nil {
|
||||
// No address exists that we can use as a source address.
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
address := ep.AddressWithPrefix().Address
|
||||
ep.DecRef()
|
||||
return snatAction(pkt, ct, 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 {
|
||||
|
||||
@@ -1197,24 +1197,15 @@ func TestSNAT(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
epAndAddrs func(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack, proto tcpip.TransportProtocolNumber) endpointAndAddresses
|
||||
}{
|
||||
{
|
||||
name: "IPv4 host1 server with host2 client",
|
||||
name: "IPv4 host1 server with host2 client",
|
||||
netProto: ipv4.ProtocolNumber,
|
||||
epAndAddrs: func(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack, proto tcpip.TransportProtocolNumber) endpointAndAddresses {
|
||||
t.Helper()
|
||||
|
||||
ipt := routerStack.IPTables()
|
||||
filter := ipt.GetTable(stack.NATID, false /* ipv6 */)
|
||||
ruleIdx := filter.BuiltinChains[stack.Postrouting]
|
||||
filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{OutputInterface: utils.RouterNIC1Name}
|
||||
filter.Rules[ruleIdx].Target = &stack.SNATTarget{NetworkProtocol: ipv4.ProtocolNumber, Addr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address}
|
||||
// Make sure the packet is not dropped by the next rule.
|
||||
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
|
||||
if err := ipt.ReplaceTable(stack.NATID, filter, false /* ipv6 */); err != nil {
|
||||
t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.NATID, false, err)
|
||||
}
|
||||
|
||||
ep1, ep1WECH := newEP(t, host1Stack, proto, ipv4.ProtocolNumber)
|
||||
ep2, ep2WECH := newEP(t, host2Stack, proto, ipv4.ProtocolNumber)
|
||||
return endpointAndAddresses{
|
||||
@@ -1231,21 +1222,11 @@ func TestSNAT(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv6 host1 server with host2 client",
|
||||
name: "IPv6 host1 server with host2 client",
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
epAndAddrs: func(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack, proto tcpip.TransportProtocolNumber) endpointAndAddresses {
|
||||
t.Helper()
|
||||
|
||||
ipt := routerStack.IPTables()
|
||||
filter := ipt.GetTable(stack.NATID, true /* ipv6 */)
|
||||
ruleIdx := filter.BuiltinChains[stack.Postrouting]
|
||||
filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{OutputInterface: utils.RouterNIC1Name}
|
||||
filter.Rules[ruleIdx].Target = &stack.SNATTarget{NetworkProtocol: ipv6.ProtocolNumber, Addr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address}
|
||||
// Make sure the packet is not dropped by the next rule.
|
||||
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
|
||||
if err := ipt.ReplaceTable(stack.NATID, filter, true /* ipv6 */); err != nil {
|
||||
t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.NATID, true, err)
|
||||
}
|
||||
|
||||
ep1, ep1WECH := newEP(t, host1Stack, proto, ipv6.ProtocolNumber)
|
||||
ep2, ep2WECH := newEP(t, host2Stack, proto, ipv6.ProtocolNumber)
|
||||
return endpointAndAddresses{
|
||||
@@ -1324,120 +1305,165 @@ func TestSNAT(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
setupNAT := func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, target stack.Target) {
|
||||
t.Helper()
|
||||
|
||||
ipv6 := netProto == ipv6.ProtocolNumber
|
||||
ipt := s.IPTables()
|
||||
filter := ipt.GetTable(stack.NATID, ipv6)
|
||||
ruleIdx := filter.BuiltinChains[stack.Postrouting]
|
||||
filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{OutputInterface: utils.RouterNIC1Name}
|
||||
filter.Rules[ruleIdx].Target = target
|
||||
// Make sure the packet is not dropped by the next rule.
|
||||
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
|
||||
if err := ipt.ReplaceTable(stack.NATID, filter, ipv6); err != nil {
|
||||
t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.NATID, ipv6, err)
|
||||
}
|
||||
}
|
||||
|
||||
natTypes := []struct {
|
||||
name string
|
||||
setupNAT func(*testing.T, *stack.Stack, tcpip.NetworkProtocolNumber, tcpip.Address)
|
||||
}{
|
||||
{
|
||||
name: "SNAT",
|
||||
setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, natToAddr tcpip.Address) {
|
||||
t.Helper()
|
||||
|
||||
setupNAT(t, s, netProto, &stack.SNATTarget{NetworkProtocol: netProto, Addr: natToAddr})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Masquerade",
|
||||
setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, natToAddr tcpip.Address) {
|
||||
t.Helper()
|
||||
|
||||
setupNAT(t, s, netProto, &stack.MasqueradeTarget{NetworkProtocol: netProto})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, subTest := range subTests {
|
||||
t.Run(subTest.name, func(t *testing.T) {
|
||||
stackOpts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol, tcp.NewProtocol},
|
||||
}
|
||||
|
||||
host1Stack := stack.New(stackOpts)
|
||||
routerStack := stack.New(stackOpts)
|
||||
host2Stack := stack.New(stackOpts)
|
||||
utils.SetupRoutedStacks(t, host1Stack, routerStack, host2Stack)
|
||||
|
||||
epsAndAddrs := test.epAndAddrs(t, host1Stack, routerStack, host2Stack, subTest.proto)
|
||||
serverAddr := tcpip.FullAddress{Addr: epsAndAddrs.serverAddr, Port: listenPort}
|
||||
if err := epsAndAddrs.serverEP.Bind(serverAddr); err != nil {
|
||||
t.Fatalf("epsAndAddrs.serverEP.Bind(%#v): %s", serverAddr, err)
|
||||
}
|
||||
clientAddr := tcpip.FullAddress{Addr: epsAndAddrs.clientAddr}
|
||||
if err := epsAndAddrs.clientEP.Bind(clientAddr); err != nil {
|
||||
t.Fatalf("epsAndAddrs.clientEP.Bind(%#v): %s", clientAddr, err)
|
||||
}
|
||||
|
||||
if subTest.setupServer != nil {
|
||||
subTest.setupServer(t, epsAndAddrs.serverEP)
|
||||
}
|
||||
{
|
||||
err := epsAndAddrs.clientEP.Connect(serverAddr)
|
||||
if diff := cmp.Diff(subTest.expectedConnectErr, err); diff != "" {
|
||||
t.Fatalf("unexpected error from epsAndAddrs.clientEP.Connect(%#v), (-want, +got):\n%s", serverAddr, diff)
|
||||
}
|
||||
}
|
||||
nattedClientAddr := tcpip.FullAddress{Addr: epsAndAddrs.nattedClientAddr}
|
||||
if addr, err := epsAndAddrs.clientEP.GetLocalAddress(); err != nil {
|
||||
t.Fatalf("epsAndAddrs.clientEP.GetLocalAddress(): %s", err)
|
||||
} else {
|
||||
nattedClientAddr.Port = addr.Port
|
||||
}
|
||||
|
||||
serverEP := epsAndAddrs.serverEP
|
||||
serverCH := epsAndAddrs.serverReadableCH
|
||||
if ep, ch := subTest.setupServerConn(t, serverEP, serverCH, nattedClientAddr); ep != nil {
|
||||
defer ep.Close()
|
||||
serverEP = ep
|
||||
serverCH = ch
|
||||
}
|
||||
|
||||
write := func(ep tcpip.Endpoint, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
var r bytes.Reader
|
||||
r.Reset(data)
|
||||
var wOpts tcpip.WriteOptions
|
||||
n, err := ep.Write(&r, wOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("ep.Write(_, %#v): %s", wOpts, err)
|
||||
}
|
||||
if want := int64(len(data)); n != want {
|
||||
t.Fatalf("got ep.Write(_, %#v) = (%d, _), want = (%d, _)", wOpts, n, want)
|
||||
}
|
||||
}
|
||||
|
||||
read := func(ch chan struct{}, ep tcpip.Endpoint, data []byte, expectedFrom tcpip.FullAddress) {
|
||||
t.Helper()
|
||||
|
||||
var buf bytes.Buffer
|
||||
var res tcpip.ReadResult
|
||||
for {
|
||||
var err tcpip.Error
|
||||
opts := tcpip.ReadOptions{NeedRemoteAddr: subTest.needRemoteAddr}
|
||||
res, err = ep.Read(&buf, opts)
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
||||
<-ch
|
||||
continue
|
||||
for _, natType := range natTypes {
|
||||
t.Run(natType.name, func(t *testing.T) {
|
||||
stackOpts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol, tcp.NewProtocol},
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("ep.Read(_, %d, %#v): %s", len(data), opts, err)
|
||||
|
||||
host1Stack := stack.New(stackOpts)
|
||||
routerStack := stack.New(stackOpts)
|
||||
host2Stack := stack.New(stackOpts)
|
||||
utils.SetupRoutedStacks(t, host1Stack, routerStack, host2Stack)
|
||||
|
||||
epsAndAddrs := test.epAndAddrs(t, host1Stack, routerStack, host2Stack, subTest.proto)
|
||||
|
||||
natType.setupNAT(t, routerStack, test.netProto, epsAndAddrs.nattedClientAddr)
|
||||
|
||||
serverAddr := tcpip.FullAddress{Addr: epsAndAddrs.serverAddr, Port: listenPort}
|
||||
if err := epsAndAddrs.serverEP.Bind(serverAddr); err != nil {
|
||||
t.Fatalf("epsAndAddrs.serverEP.Bind(%#v): %s", serverAddr, err)
|
||||
}
|
||||
clientAddr := tcpip.FullAddress{Addr: epsAndAddrs.clientAddr}
|
||||
if err := epsAndAddrs.clientEP.Bind(clientAddr); err != nil {
|
||||
t.Fatalf("epsAndAddrs.clientEP.Bind(%#v): %s", clientAddr, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
readResult := tcpip.ReadResult{
|
||||
Count: len(data),
|
||||
Total: len(data),
|
||||
}
|
||||
if subTest.needRemoteAddr {
|
||||
readResult.RemoteAddr = expectedFrom
|
||||
}
|
||||
if diff := cmp.Diff(readResult, res, checker.IgnoreCmpPath(
|
||||
"ControlMessages",
|
||||
"RemoteAddr.NIC",
|
||||
)); diff != "" {
|
||||
t.Errorf("ep.Read: unexpected result (-want +got):\n%s", diff)
|
||||
}
|
||||
if diff := cmp.Diff(buf.Bytes(), data); diff != "" {
|
||||
t.Errorf("received data mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
if subTest.setupServer != nil {
|
||||
subTest.setupServer(t, epsAndAddrs.serverEP)
|
||||
}
|
||||
{
|
||||
err := epsAndAddrs.clientEP.Connect(serverAddr)
|
||||
if diff := cmp.Diff(subTest.expectedConnectErr, err); diff != "" {
|
||||
t.Fatalf("unexpected error from epsAndAddrs.clientEP.Connect(%#v), (-want, +got):\n%s", serverAddr, diff)
|
||||
}
|
||||
}
|
||||
nattedClientAddr := tcpip.FullAddress{Addr: epsAndAddrs.nattedClientAddr}
|
||||
if addr, err := epsAndAddrs.clientEP.GetLocalAddress(); err != nil {
|
||||
t.Fatalf("epsAndAddrs.clientEP.GetLocalAddress(): %s", err)
|
||||
} else {
|
||||
nattedClientAddr.Port = addr.Port
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
serverEP := epsAndAddrs.serverEP
|
||||
serverCH := epsAndAddrs.serverReadableCH
|
||||
if ep, ch := subTest.setupServerConn(t, serverEP, serverCH, nattedClientAddr); ep != nil {
|
||||
defer ep.Close()
|
||||
serverEP = ep
|
||||
serverCH = ch
|
||||
}
|
||||
|
||||
{
|
||||
data := []byte{1, 2, 3, 4}
|
||||
write(epsAndAddrs.clientEP, data)
|
||||
read(serverCH, serverEP, data, nattedClientAddr)
|
||||
}
|
||||
write := func(ep tcpip.Endpoint, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
{
|
||||
data := []byte{5, 6, 7, 8, 9, 10, 11, 12}
|
||||
write(serverEP, data)
|
||||
read(epsAndAddrs.clientReadableCH, epsAndAddrs.clientEP, data, serverAddr)
|
||||
var r bytes.Reader
|
||||
r.Reset(data)
|
||||
var wOpts tcpip.WriteOptions
|
||||
n, err := ep.Write(&r, wOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("ep.Write(_, %#v): %s", wOpts, err)
|
||||
}
|
||||
if want := int64(len(data)); n != want {
|
||||
t.Fatalf("got ep.Write(_, %#v) = (%d, _), want = (%d, _)", wOpts, n, want)
|
||||
}
|
||||
}
|
||||
|
||||
read := func(ch chan struct{}, ep tcpip.Endpoint, data []byte, expectedFrom tcpip.FullAddress) {
|
||||
t.Helper()
|
||||
|
||||
var buf bytes.Buffer
|
||||
var res tcpip.ReadResult
|
||||
for {
|
||||
var err tcpip.Error
|
||||
opts := tcpip.ReadOptions{NeedRemoteAddr: subTest.needRemoteAddr}
|
||||
res, err = ep.Read(&buf, opts)
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
||||
<-ch
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("ep.Read(_, %d, %#v): %s", len(data), opts, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
readResult := tcpip.ReadResult{
|
||||
Count: len(data),
|
||||
Total: len(data),
|
||||
}
|
||||
if subTest.needRemoteAddr {
|
||||
readResult.RemoteAddr = expectedFrom
|
||||
}
|
||||
if diff := cmp.Diff(readResult, res, checker.IgnoreCmpPath(
|
||||
"ControlMessages",
|
||||
"RemoteAddr.NIC",
|
||||
)); diff != "" {
|
||||
t.Errorf("ep.Read: unexpected result (-want +got):\n%s", diff)
|
||||
}
|
||||
if diff := cmp.Diff(buf.Bytes(), data); diff != "" {
|
||||
t.Errorf("received data mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
data := []byte{1, 2, 3, 4}
|
||||
write(epsAndAddrs.clientEP, data)
|
||||
read(serverCH, serverEP, data, nattedClientAddr)
|
||||
}
|
||||
|
||||
{
|
||||
data := []byte{5, 6, 7, 8, 9, 10, 11, 12}
|
||||
write(serverEP, data)
|
||||
read(epsAndAddrs.clientReadableCH, epsAndAddrs.clientEP, data, serverAddr)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user