Skip to content

Commit

Permalink
Implement packet forwarding to enable NAT
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 213323501
  • Loading branch information
tamird authored and shentubot committed Sep 17, 2018
1 parent dd64f66 commit 98d516d
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 39 deletions.
110 changes: 73 additions & 37 deletions tcpip/stack/nic.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 19 additions & 2 deletions tcpip/stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 98d516d

Please sign in to comment.