diff options
author | Ghanan Gowripalan <ghanan@google.com> | 2021-10-11 21:34:07 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-10-11 21:36:35 -0700 |
commit | ab1ef0baba1d15c4d4e7717d23cf8b32fd6b5feb (patch) | |
tree | d5e57be6e49190e8c2e3ca8812055fc25844271a | |
parent | b9176535cea01e35f819edf312d0f5e7da3e8ffd (diff) |
Support DNAT target
PiperOrigin-RevId: 402468096
-rw-r--r-- | pkg/tcpip/stack/iptables_targets.go | 74 | ||||
-rw-r--r-- | pkg/tcpip/tests/integration/iptables_test.go | 21 |
2 files changed, 65 insertions, 30 deletions
diff --git a/pkg/tcpip/stack/iptables_targets.go b/pkg/tcpip/stack/iptables_targets.go index 7e5a1672a..85490e2d4 100644 --- a/pkg/tcpip/stack/iptables_targets.go +++ b/pkg/tcpip/stack/iptables_targets.go @@ -83,6 +83,45 @@ func (*ReturnTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (R return RuleReturn, 0 } +// DNATTarget modifies the destination port/IP of packets. +type DNATTarget struct { + // The new destination address for packets. + // + // Immutable. + Addr tcpip.Address + + // The new destination port for packets. + // + // Immutable. + Port uint16 + + // NetworkProtocol is the network protocol the target is used with. + // + // Immutable. + NetworkProtocol tcpip.NetworkProtocolNumber +} + +// Action implements Target.Action. +func (rt *DNATTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, addressEP AddressableEndpoint) (RuleVerdict, int) { + // Sanity check. + if rt.NetworkProtocol != pkt.NetworkProtocolNumber { + panic(fmt.Sprintf( + "DNATTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d", + rt.NetworkProtocol, pkt.NetworkProtocolNumber)) + } + + switch hook { + case Prerouting, Output: + case Input, Forward, Postrouting: + panic(fmt.Sprintf("%s not supported for DNAT", hook)) + default: + panic(fmt.Sprintf("%s unrecognized", hook)) + } + + return natAction(pkt, hook, r, rt.Port, rt.Addr, true /* dnat */) + +} + // RedirectTarget redirects the packet to this machine by modifying the // destination port/IP. Outgoing packets are redirected to the loopback device, // and incoming packets are redirected to the incoming interface (rather than @@ -105,16 +144,6 @@ func (rt *RedirectTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, address rt.NetworkProtocol, pkt.NetworkProtocolNumber)) } - // Packet is already manipulated. - if pkt.NatDone { - return RuleAccept, 0 - } - - // Drop the packet if network and transport header are not set. - if pkt.NetworkHeader().View().IsEmpty() || pkt.TransportHeader().View().IsEmpty() { - return RuleDrop, 0 - } - // Change the address to loopback (127.0.0.1 or ::1) in Output and to // the primary address of the incoming interface in Prerouting. var address tcpip.Address @@ -132,12 +161,7 @@ func (rt *RedirectTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, address panic("redirect target is supported only on output and prerouting hooks") } - if t := pkt.tuple; t != nil { - t.conn.performNAT(pkt, hook, r, rt.Port, address, true /* dnat */) - return RuleAccept, 0 - } - - return RuleDrop, 0 + return natAction(pkt, hook, r, rt.Port, address, true /* dnat */) } // SNATTarget modifies the source port/IP in the outgoing packets. @@ -150,7 +174,7 @@ type SNATTarget struct { NetworkProtocol tcpip.NetworkProtocolNumber } -func snatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcpip.Address) (RuleVerdict, int) { +func natAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcpip.Address, dnat bool) (RuleVerdict, int) { // Packet is already manipulated. if pkt.NatDone { return RuleAccept, 0 @@ -161,6 +185,11 @@ func snatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcp return RuleDrop, 0 } + t := pkt.tuple + if t == nil { + return RuleDrop, 0 + } + // TODO(https://gvisor.dev/issue/5773): If the port is in use, pick a // different port. if port == 0 { @@ -169,13 +198,12 @@ func snatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcp 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)) } } - if t := pkt.tuple; t != nil { - t.conn.performNAT(pkt, hook, r, port, address, false /* dnat */) - } - + t.conn.performNAT(pkt, hook, r, port, address, dnat) return RuleAccept, 0 } @@ -196,7 +224,7 @@ func (st *SNATTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, _ Addressab panic(fmt.Sprintf("%s unrecognized", hook)) } - return snatAction(pkt, hook, r, st.Port, st.Addr) + return natAction(pkt, hook, r, st.Port, st.Addr, false /* dnat */) } // MasqueradeTarget modifies the source port/IP in the outgoing packets. @@ -232,7 +260,7 @@ func (mt *MasqueradeTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, addre address := ep.AddressWithPrefix().Address ep.DecRef() - return snatAction(pkt, hook, r, 0 /* port */, address) + return natAction(pkt, hook, r, 0 /* port */, address, false /* dnat */) } func rewritePacket(n header.Network, t header.ChecksummableTransport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPort uint16, newAddr tcpip.Address) { diff --git a/pkg/tcpip/tests/integration/iptables_test.go b/pkg/tcpip/tests/integration/iptables_test.go index 203655f05..7f872c271 100644 --- a/pkg/tcpip/tests/integration/iptables_test.go +++ b/pkg/tcpip/tests/integration/iptables_test.go @@ -1245,21 +1245,21 @@ func TestNAT(t *testing.T) { type natType struct { name string - setupNAT func(*testing.T, *stack.Stack, tcpip.NetworkProtocolNumber, tcpip.TransportProtocolNumber, tcpip.Address) + setupNAT func(_ *testing.T, _ *stack.Stack, _ tcpip.NetworkProtocolNumber, _ tcpip.TransportProtocolNumber, snatAddr, dnatAddr tcpip.Address) } snatTypes := []natType{ { name: "SNAT", - setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, natToAddr tcpip.Address) { + setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, snatAddr, _ tcpip.Address) { t.Helper() - setupSNAT(t, s, netProto, transProto, &stack.SNATTarget{NetworkProtocol: netProto, Addr: natToAddr}) + setupSNAT(t, s, netProto, transProto, &stack.SNATTarget{NetworkProtocol: netProto, Addr: snatAddr}) }, }, { name: "Masquerade", - setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, natToAddr tcpip.Address) { + setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, _, _ tcpip.Address) { t.Helper() setupSNAT(t, s, netProto, transProto, &stack.MasqueradeTarget{NetworkProtocol: netProto}) @@ -1269,12 +1269,20 @@ func TestNAT(t *testing.T) { dnatTypes := []natType{ { name: "Redirect", - setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, natToAddr tcpip.Address) { + setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, _, _ tcpip.Address) { t.Helper() setupDNAT(t, s, netProto, transProto, &stack.RedirectTarget{NetworkProtocol: netProto, Port: listenPort}) }, }, + { + name: "DNAT", + setupNAT: func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, _, dnatAddr tcpip.Address) { + t.Helper() + + setupDNAT(t, s, netProto, transProto, &stack.DNATTarget{NetworkProtocol: netProto, Addr: dnatAddr, Port: listenPort}) + }, + }, } tests := []struct { @@ -1509,8 +1517,7 @@ func TestNAT(t *testing.T) { utils.SetupRoutedStacks(t, host1Stack, routerStack, host2Stack) epsAndAddrs := test.epAndAddrs(t, host1Stack, routerStack, host2Stack, subTest.proto) - - natType.setupNAT(t, routerStack, test.netProto, subTest.proto, epsAndAddrs.serverConnectAddr) + natType.setupNAT(t, routerStack, test.netProto, subTest.proto, epsAndAddrs.serverConnectAddr, epsAndAddrs.serverAddr.Addr) if err := epsAndAddrs.serverEP.Bind(epsAndAddrs.serverAddr); err != nil { t.Fatalf("epsAndAddrs.serverEP.Bind(%#v): %s", epsAndAddrs.serverAddr, err) |