summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/stack/nic.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/stack/nic.go')
-rw-r--r--pkg/tcpip/stack/nic.go173
1 files changed, 107 insertions, 66 deletions
diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go
index 0f545f255..e56a624fe 100644
--- a/pkg/tcpip/stack/nic.go
+++ b/pkg/tcpip/stack/nic.go
@@ -53,6 +53,8 @@ type NIC struct {
// complete.
linkResQueue packetsPendingLinkResolution
+ linkAddrCache *linkAddrCache
+
mu struct {
sync.RWMutex
spoofing bool
@@ -138,7 +140,8 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, ctx NICC
stats: makeNICStats(),
networkEndpoints: make(map[tcpip.NetworkProtocolNumber]NetworkEndpoint),
}
- nic.linkResQueue.init()
+ nic.linkResQueue.init(nic)
+ nic.linkAddrCache = newLinkAddrCache(nic, ageLimit, resolutionTimeout, resolutionAttempts)
nic.mu.packetEPs = make(map[tcpip.NetworkProtocolNumber]*packetEndpointList)
// Check for Neighbor Unreachability Detection support.
@@ -167,7 +170,7 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, ctx NICC
for _, netProto := range stack.networkProtocols {
netNum := netProto.Number()
nic.mu.packetEPs[netNum] = new(packetEndpointList)
- nic.networkEndpoints[netNum] = netProto.NewEndpoint(nic, stack, nud, nic)
+ nic.networkEndpoints[netNum] = netProto.NewEndpoint(nic, nic.linkAddrCache, nud, nic)
}
nic.LinkEndpoint.Attach(nic)
@@ -228,7 +231,9 @@ func (n *NIC) disableLocked() {
//
// This matches linux's behaviour at the time of writing:
// https://github.com/torvalds/linux/blob/71c061d2443814de15e177489d5cc00a4a253ef3/net/core/neighbour.c#L371
- if err := n.clearNeighbors(); err != nil && err != tcpip.ErrNotSupported {
+ switch err := n.clearNeighbors(); err.(type) {
+ case nil, *tcpip.ErrNotSupported:
+ default:
panic(fmt.Sprintf("n.clearNeighbors(): %s", err))
}
@@ -243,7 +248,7 @@ func (n *NIC) disableLocked() {
// address (ff02::1), start DAD for permanent addresses, and start soliciting
// routers if the stack is not operating as a router. If the stack is also
// configured to auto-generate a link-local address, one will be generated.
-func (n *NIC) enable() *tcpip.Error {
+func (n *NIC) enable() tcpip.Error {
n.mu.Lock()
defer n.mu.Unlock()
@@ -263,7 +268,7 @@ func (n *NIC) enable() *tcpip.Error {
// remove detaches NIC from the link endpoint and releases network endpoint
// resources. This guarantees no packets between this NIC and the network
// stack.
-func (n *NIC) remove() *tcpip.Error {
+func (n *NIC) remove() tcpip.Error {
n.mu.Lock()
defer n.mu.Unlock()
@@ -299,48 +304,69 @@ func (n *NIC) IsLoopback() bool {
}
// WritePacket implements NetworkLinkEndpoint.
-func (n *NIC) WritePacket(r *Route, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) *tcpip.Error {
- // As per relevant RFCs, we should queue packets while we wait for link
- // resolution to complete.
- //
- // RFC 1122 section 2.3.2.2 (for IPv4):
- // The link layer SHOULD save (rather than discard) at least
- // one (the latest) packet of each set of packets destined to
- // the same unresolved IP address, and transmit the saved
- // packet when the address has been resolved.
- //
- // RFC 4861 section 7.2.2 (for IPv6):
- // While waiting for address resolution to complete, the sender MUST, for
- // each neighbor, retain a small queue of packets waiting for address
- // resolution to complete. The queue MUST hold at least one packet, and MAY
- // contain more. However, the number of queued packets per neighbor SHOULD
- // be limited to some small value. When a queue overflows, the new arrival
- // SHOULD replace the oldest entry. Once address resolution completes, the
- // node transmits any queued packets.
- if ch, err := r.Resolve(nil); err != nil {
- if err == tcpip.ErrWouldBlock {
- r.Acquire()
- n.linkResQueue.enqueue(ch, r, protocol, pkt)
- return nil
+func (n *NIC) WritePacket(r *Route, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) tcpip.Error {
+ _, err := n.enqueuePacketBuffer(r, gso, protocol, pkt)
+ return err
+}
+
+func (n *NIC) writePacketBuffer(r RouteInfo, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt pendingPacketBuffer) (int, tcpip.Error) {
+ switch pkt := pkt.(type) {
+ case *PacketBuffer:
+ if err := n.writePacket(r, gso, protocol, pkt); err != nil {
+ return 0, err
}
- return err
+ return 1, nil
+ case *PacketBufferList:
+ return n.writePackets(r, gso, protocol, *pkt)
+ default:
+ panic(fmt.Sprintf("unrecognized pending packet buffer type = %T", pkt))
}
+}
- return n.writePacket(r.Fields(), gso, protocol, pkt)
+func (n *NIC) enqueuePacketBuffer(r *Route, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt pendingPacketBuffer) (int, tcpip.Error) {
+ routeInfo, _, err := r.resolvedFields(nil)
+ switch err.(type) {
+ case nil:
+ return n.writePacketBuffer(routeInfo, gso, protocol, pkt)
+ case *tcpip.ErrWouldBlock:
+ // As per relevant RFCs, we should queue packets while we wait for link
+ // resolution to complete.
+ //
+ // RFC 1122 section 2.3.2.2 (for IPv4):
+ // The link layer SHOULD save (rather than discard) at least
+ // one (the latest) packet of each set of packets destined to
+ // the same unresolved IP address, and transmit the saved
+ // packet when the address has been resolved.
+ //
+ // RFC 4861 section 7.2.2 (for IPv6):
+ // While waiting for address resolution to complete, the sender MUST, for
+ // each neighbor, retain a small queue of packets waiting for address
+ // resolution to complete. The queue MUST hold at least one packet, and
+ // MAY contain more. However, the number of queued packets per neighbor
+ // SHOULD be limited to some small value. When a queue overflows, the new
+ // arrival SHOULD replace the oldest entry. Once address resolution
+ // completes, the node transmits any queued packets.
+ return n.linkResQueue.enqueue(r, gso, protocol, pkt)
+ default:
+ return 0, err
+ }
}
// WritePacketToRemote implements NetworkInterface.
-func (n *NIC) WritePacketToRemote(remoteLinkAddr tcpip.LinkAddress, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) *tcpip.Error {
+func (n *NIC) WritePacketToRemote(remoteLinkAddr tcpip.LinkAddress, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) tcpip.Error {
var r RouteInfo
r.NetProto = protocol
r.RemoteLinkAddress = remoteLinkAddr
return n.writePacket(r, gso, protocol, pkt)
}
-func (n *NIC) writePacket(r RouteInfo, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) *tcpip.Error {
+func (n *NIC) writePacket(r RouteInfo, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) tcpip.Error {
// WritePacket takes ownership of pkt, calculate numBytes first.
numBytes := pkt.Size()
+ pkt.EgressRoute = r
+ pkt.GSOOptions = gso
+ pkt.NetworkProtocolNumber = protocol
if err := n.LinkEndpoint.WritePacket(r, gso, protocol, pkt); err != nil {
return err
}
@@ -351,10 +377,18 @@ func (n *NIC) writePacket(r RouteInfo, gso *GSO, protocol tcpip.NetworkProtocolN
}
// WritePackets implements NetworkLinkEndpoint.
-func (n *NIC) WritePackets(r *Route, gso *GSO, pkts PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
- // TODO(gvisor.dev/issue/4458): Queue packets whie link address resolution
- // is being peformed like WritePacket.
- writtenPackets, err := n.LinkEndpoint.WritePackets(r.Fields(), gso, pkts, protocol)
+func (n *NIC) WritePackets(r *Route, gso *GSO, pkts PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
+ return n.enqueuePacketBuffer(r, gso, protocol, &pkts)
+}
+
+func (n *NIC) writePackets(r RouteInfo, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkts PacketBufferList) (int, tcpip.Error) {
+ for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
+ pkt.EgressRoute = r
+ pkt.GSOOptions = gso
+ pkt.NetworkProtocolNumber = protocol
+ }
+
+ writtenPackets, err := n.LinkEndpoint.WritePackets(r, gso, pkts, protocol)
n.stats.Tx.Packets.IncrementBy(uint64(writtenPackets))
writtenBytes := 0
for i, pb := 0, pkts.Front(); i < writtenPackets && pb != nil; i, pb = i+1, pb.Next() {
@@ -463,15 +497,15 @@ func (n *NIC) getAddressOrCreateTempInner(protocol tcpip.NetworkProtocolNumber,
// addAddress adds a new address to n, so that it starts accepting packets
// targeted at the given address (and network protocol).
-func (n *NIC) addAddress(protocolAddress tcpip.ProtocolAddress, peb PrimaryEndpointBehavior) *tcpip.Error {
+func (n *NIC) addAddress(protocolAddress tcpip.ProtocolAddress, peb PrimaryEndpointBehavior) tcpip.Error {
ep, ok := n.networkEndpoints[protocolAddress.Protocol]
if !ok {
- return tcpip.ErrUnknownProtocol
+ return &tcpip.ErrUnknownProtocol{}
}
addressableEndpoint, ok := ep.(AddressableEndpoint)
if !ok {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
addressEndpoint, err := addressableEndpoint.AddAndAcquirePermanentAddress(protocolAddress.AddressWithPrefix, peb, AddressConfigStatic, false /* deprecated */)
@@ -535,63 +569,70 @@ func (n *NIC) primaryAddress(proto tcpip.NetworkProtocolNumber) tcpip.AddressWit
}
// removeAddress removes an address from n.
-func (n *NIC) removeAddress(addr tcpip.Address) *tcpip.Error {
+func (n *NIC) removeAddress(addr tcpip.Address) tcpip.Error {
for _, ep := range n.networkEndpoints {
addressableEndpoint, ok := ep.(AddressableEndpoint)
if !ok {
continue
}
- if err := addressableEndpoint.RemovePermanentAddress(addr); err == tcpip.ErrBadLocalAddress {
+ switch err := addressableEndpoint.RemovePermanentAddress(addr); err.(type) {
+ case *tcpip.ErrBadLocalAddress:
continue
- } else {
+ default:
return err
}
}
- return tcpip.ErrBadLocalAddress
+ return &tcpip.ErrBadLocalAddress{}
+}
+
+func (n *NIC) confirmReachable(addr tcpip.Address) {
+ if n := n.neigh; n != nil {
+ n.handleUpperLevelConfirmation(addr)
+ }
}
-func (n *NIC) getNeighborLinkAddress(addr, localAddr tcpip.Address, linkRes LinkAddressResolver, onResolve func(tcpip.LinkAddress, bool)) (tcpip.LinkAddress, <-chan struct{}, *tcpip.Error) {
+func (n *NIC) getNeighborLinkAddress(addr, localAddr tcpip.Address, linkRes LinkAddressResolver, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
if n.neigh != nil {
entry, ch, err := n.neigh.entry(addr, localAddr, linkRes, onResolve)
return entry.LinkAddr, ch, err
}
- return n.stack.linkAddrCache.get(tcpip.FullAddress{NIC: n.ID(), Addr: addr}, linkRes, localAddr, n, onResolve)
+ return n.linkAddrCache.get(addr, linkRes, localAddr, n, onResolve)
}
-func (n *NIC) neighbors() ([]NeighborEntry, *tcpip.Error) {
+func (n *NIC) neighbors() ([]NeighborEntry, tcpip.Error) {
if n.neigh == nil {
- return nil, tcpip.ErrNotSupported
+ return nil, &tcpip.ErrNotSupported{}
}
return n.neigh.entries(), nil
}
-func (n *NIC) addStaticNeighbor(addr tcpip.Address, linkAddress tcpip.LinkAddress) *tcpip.Error {
+func (n *NIC) addStaticNeighbor(addr tcpip.Address, linkAddress tcpip.LinkAddress) tcpip.Error {
if n.neigh == nil {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
n.neigh.addStaticEntry(addr, linkAddress)
return nil
}
-func (n *NIC) removeNeighbor(addr tcpip.Address) *tcpip.Error {
+func (n *NIC) removeNeighbor(addr tcpip.Address) tcpip.Error {
if n.neigh == nil {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
if !n.neigh.removeEntry(addr) {
- return tcpip.ErrBadAddress
+ return &tcpip.ErrBadAddress{}
}
return nil
}
-func (n *NIC) clearNeighbors() *tcpip.Error {
+func (n *NIC) clearNeighbors() tcpip.Error {
if n.neigh == nil {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
n.neigh.clear()
@@ -600,7 +641,7 @@ func (n *NIC) clearNeighbors() *tcpip.Error {
// joinGroup adds a new endpoint for the given multicast address, if none
// exists yet. Otherwise it just increments its count.
-func (n *NIC) joinGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) *tcpip.Error {
+func (n *NIC) joinGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) tcpip.Error {
// TODO(b/143102137): When implementing MLD, make sure MLD packets are
// not sent unless a valid link-local address is available for use on n
// as an MLD packet's source address must be a link-local address as
@@ -608,12 +649,12 @@ func (n *NIC) joinGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address
ep, ok := n.networkEndpoints[protocol]
if !ok {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
gep, ok := ep.(GroupAddressableEndpoint)
if !ok {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
return gep.JoinGroup(addr)
@@ -621,15 +662,15 @@ func (n *NIC) joinGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address
// leaveGroup decrements the count for the given multicast address, and when it
// reaches zero removes the endpoint for this address.
-func (n *NIC) leaveGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) *tcpip.Error {
+func (n *NIC) leaveGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) tcpip.Error {
ep, ok := n.networkEndpoints[protocol]
if !ok {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
gep, ok := ep.(GroupAddressableEndpoint)
if !ok {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
return gep.LeaveGroup(addr)
@@ -879,9 +920,9 @@ func (n *NIC) Name() string {
}
// nudConfigs gets the NUD configurations for n.
-func (n *NIC) nudConfigs() (NUDConfigurations, *tcpip.Error) {
+func (n *NIC) nudConfigs() (NUDConfigurations, tcpip.Error) {
if n.neigh == nil {
- return NUDConfigurations{}, tcpip.ErrNotSupported
+ return NUDConfigurations{}, &tcpip.ErrNotSupported{}
}
return n.neigh.config(), nil
}
@@ -890,22 +931,22 @@ func (n *NIC) nudConfigs() (NUDConfigurations, *tcpip.Error) {
//
// Note, if c contains invalid NUD configuration values, it will be fixed to
// use default values for the erroneous values.
-func (n *NIC) setNUDConfigs(c NUDConfigurations) *tcpip.Error {
+func (n *NIC) setNUDConfigs(c NUDConfigurations) tcpip.Error {
if n.neigh == nil {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
c.resetInvalidFields()
n.neigh.setConfig(c)
return nil
}
-func (n *NIC) registerPacketEndpoint(netProto tcpip.NetworkProtocolNumber, ep PacketEndpoint) *tcpip.Error {
+func (n *NIC) registerPacketEndpoint(netProto tcpip.NetworkProtocolNumber, ep PacketEndpoint) tcpip.Error {
n.mu.Lock()
defer n.mu.Unlock()
eps, ok := n.mu.packetEPs[netProto]
if !ok {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
eps.add(ep)