From 23258ca284b3f086cc83b69d9a1f8f382036a69e Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 17 Sep 2018 13:04:38 -0700 Subject: Implement packet forwarding to enable NAT PiperOrigin-RevId: 213323501 Change-Id: I0996ddbdcf097588745efe35481085d42dbaf446 --- pkg/tcpip/stack/nic.go | 110 +++++++++++++++++++++++++++++++---------------- pkg/tcpip/stack/stack.go | 21 ++++++++- 2 files changed, 92 insertions(+), 39 deletions(-) (limited to 'pkg') diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go index 4c027e91a..29c9ddec4 100644 --- a/pkg/tcpip/stack/nic.go +++ b/pkg/tcpip/stack/nic.go @@ -408,53 +408,89 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remoteLinkAddr tcpip.Lin } src, dst := netProto.ParseAddresses(vv.First()) - id := NetworkEndpointID{dst} - n.mu.RLock() - ref, ok := n.endpoints[id] - if ok && !ref.tryIncRef() { - ref = nil + if ref := n.getRef(protocol, dst); ref != nil { + r := makeRoute(protocol, dst, src, linkEP.LinkAddress(), ref) + r.RemoteLinkAddress = remoteLinkAddr + ref.ep.HandlePacket(&r, vv) + ref.decRef() + return } - if ref != nil { + + // This NIC doesn't care about the packet. Find a NIC that cares about the + // packet and forward it to the NIC. + // + // TODO: Should we be forwarding the packet even if promiscuous? + if n.stack.Forwarding() { + r, err := n.stack.FindRoute(0, "", dst, protocol) + if err != nil { + n.stack.stats.IP.InvalidAddressesReceived.Increment() + return + } + defer r.Release() + + r.LocalLinkAddress = n.linkEP.LinkAddress() + r.RemoteLinkAddress = remoteLinkAddr + + // Found a NIC. + n := r.ref.nic + n.mu.RLock() + ref, ok := n.endpoints[NetworkEndpointID{dst}] n.mu.RUnlock() - } else { - promiscuous := n.promiscuous - // Check if the packet is for a subnet this NIC cares about. - if !promiscuous { - for _, sn := range n.subnets { - if sn.Contains(dst) { - promiscuous = true - break - } - } + if ok && ref.tryIncRef() { + ref.ep.HandlePacket(&r, vv) + ref.decRef() + } else { + // n doesn't have a destination endpoint. + // Send the packet out of n. + hdr := buffer.NewPrependableFromView(vv.First()) + vv.RemoveFirst() + n.linkEP.WritePacket(&r, hdr, vv, protocol) } + return + } + + n.stack.stats.IP.InvalidAddressesReceived.Increment() +} + +func (n *NIC) getRef(protocol tcpip.NetworkProtocolNumber, dst tcpip.Address) *referencedNetworkEndpoint { + id := NetworkEndpointID{dst} + + n.mu.RLock() + if ref, ok := n.endpoints[id]; ok && ref.tryIncRef() { n.mu.RUnlock() - if promiscuous { - // Try again with the lock in exclusive mode. If we still can't - // get the endpoint, create a new "temporary" one. It will only - // exist while there's a route through it. - n.mu.Lock() - ref, ok = n.endpoints[id] - if !ok || !ref.tryIncRef() { - var err *tcpip.Error - ref, err = n.addAddressLocked(protocol, dst, CanBePrimaryEndpoint, true) - if err == nil { - ref.holdsInsertRef = false - } + return ref + } + + promiscuous := n.promiscuous + // Check if the packet is for a subnet this NIC cares about. + if !promiscuous { + for _, sn := range n.subnets { + if sn.Contains(dst) { + promiscuous = true + break } - n.mu.Unlock() } } - - if ref == nil { - n.stack.stats.IP.InvalidAddressesReceived.Increment() - return + n.mu.RUnlock() + if promiscuous { + // Try again with the lock in exclusive mode. If we still can't + // get the endpoint, create a new "temporary" one. It will only + // exist while there's a route through it. + n.mu.Lock() + if ref, ok := n.endpoints[id]; ok && ref.tryIncRef() { + n.mu.Unlock() + return ref + } + ref, err := n.addAddressLocked(protocol, dst, CanBePrimaryEndpoint, true) + n.mu.Unlock() + if err == nil { + ref.holdsInsertRef = false + return ref + } } - r := makeRoute(protocol, dst, src, linkEP.LinkAddress(), ref) - r.RemoteLinkAddress = remoteLinkAddr - ref.ep.HandlePacket(&r, vv) - ref.decRef() + return nil } // DeliverTransportPacket delivers the packets to the appropriate transport diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index 2d313cc27..699519be1 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -283,8 +283,9 @@ type Stack struct { linkAddrCache *linkAddrCache - mu sync.RWMutex - nics map[tcpip.NICID]*NIC + mu sync.RWMutex + nics map[tcpip.NICID]*NIC + forwarding bool // route is the route table passed in by the user via SetRouteTable(), // it is used by FindRoute() to build a route for a specific @@ -448,6 +449,22 @@ func (s *Stack) Stats() tcpip.Stats { return s.stats } +// SetForwarding enables or disables the packet forwarding between NICs. +func (s *Stack) SetForwarding(enable bool) { + // TODO: Expose via /proc/sys/net/ipv4/ip_forward. + s.mu.Lock() + s.forwarding = enable + s.mu.Unlock() +} + +// Forwarding returns if the packet forwarding between NICs is enabled. +func (s *Stack) Forwarding() bool { + // TODO: Expose via /proc/sys/net/ipv4/ip_forward. + s.mu.RLock() + defer s.mu.RUnlock() + return s.forwarding +} + // SetRouteTable assigns the route table to be used by this stack. It // specifies which NIC to use for given destination address ranges. func (s *Stack) SetRouteTable(table []tcpip.Route) { -- cgit v1.2.3