mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support disabling a NIC
- Disabled NICs will have their associated NDP state cleared. - Disabled NICs will not accept incoming packets. - Writes through a Route with a disabled NIC will return an invalid endpoint state error. - stack.Stack.FindRoute will not return a route with a disabled NIC. - NIC's Running flag will report the NIC's enabled status. Tests: - stack_test.TestDisableUnknownNIC - stack_test.TestDisabledNICsNICInfoAndCheckNIC - stack_test.TestRoutesWithDisabledNIC - stack_test.TestRouteWritePacketWithDisabledNIC - stack_test.TestStopStartSolicitingRouters - stack_test.TestCleanupNDPState - stack_test.TestAddRemoveIPv4BroadcastAddressOnNICEnableDisable - stack_test.TestJoinLeaveAllNodesMulticastOnNICEnableDisable PiperOrigin-RevId: 296298588
This commit is contained in:
committed by
Copybara-Service
parent
d90d71474f
commit
67b615b86f
+14
-9
@@ -1148,22 +1148,27 @@ func (ndp *ndpState) cleanupAutoGenAddrResourcesAndNotify(addr tcpip.Address) bo
|
||||
return true
|
||||
}
|
||||
|
||||
// cleanupHostOnlyState cleans up any state that is only useful for hosts.
|
||||
// cleanupState cleans up ndp's state.
|
||||
//
|
||||
// cleanupHostOnlyState MUST be called when ndp's NIC is transitioning from a
|
||||
// host to a router. This function will invalidate all discovered on-link
|
||||
// prefixes, discovered routers, and auto-generated addresses as routers do not
|
||||
// normally process Router Advertisements to discover default routers and
|
||||
// on-link prefixes, and auto-generate addresses via SLAAC.
|
||||
// If hostOnly is true, then only host-specific state will be cleaned up.
|
||||
//
|
||||
// cleanupState MUST be called with hostOnly set to true when ndp's NIC is
|
||||
// transitioning from a host to a router. This function will invalidate all
|
||||
// discovered on-link prefixes, discovered routers, and auto-generated
|
||||
// addresses.
|
||||
//
|
||||
// If hostOnly is true, then the link-local auto-generated address will not be
|
||||
// invalidated as routers are also expected to generate a link-local address.
|
||||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) cleanupHostOnlyState() {
|
||||
func (ndp *ndpState) cleanupState(hostOnly bool) {
|
||||
linkLocalSubnet := header.IPv6LinkLocalPrefix.Subnet()
|
||||
linkLocalAddrs := 0
|
||||
for addr := range ndp.autoGenAddresses {
|
||||
// RFC 4862 section 5 states that routers are also expected to generate a
|
||||
// link-local address so we do not invalidate them.
|
||||
if linkLocalSubnet.Contains(addr) {
|
||||
// link-local address so we do not invalidate them if we are cleaning up
|
||||
// host-only state.
|
||||
if hostOnly && linkLocalSubnet.Contains(addr) {
|
||||
linkLocalAddrs++
|
||||
continue
|
||||
}
|
||||
|
||||
+476
-346
File diff suppressed because it is too large
Load Diff
+97
-13
@@ -27,6 +27,14 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
var ipv4BroadcastAddr = tcpip.ProtocolAddress{
|
||||
Protocol: header.IPv4ProtocolNumber,
|
||||
AddressWithPrefix: tcpip.AddressWithPrefix{
|
||||
Address: header.IPv4Broadcast,
|
||||
PrefixLen: 8 * header.IPv4AddressSize,
|
||||
},
|
||||
}
|
||||
|
||||
// NIC represents a "network interface card" to which the networking stack is
|
||||
// attached.
|
||||
type NIC struct {
|
||||
@@ -36,7 +44,8 @@ type NIC struct {
|
||||
linkEP LinkEndpoint
|
||||
context NICContext
|
||||
|
||||
stats NICStats
|
||||
stats NICStats
|
||||
attach sync.Once
|
||||
|
||||
mu struct {
|
||||
sync.RWMutex
|
||||
@@ -135,7 +144,69 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, ctx NICC
|
||||
return nic
|
||||
}
|
||||
|
||||
// enable enables the NIC. enable will attach the link to its LinkEndpoint and
|
||||
// enabled returns true if n is enabled.
|
||||
func (n *NIC) enabled() bool {
|
||||
n.mu.RLock()
|
||||
enabled := n.mu.enabled
|
||||
n.mu.RUnlock()
|
||||
return enabled
|
||||
}
|
||||
|
||||
// disable disables n.
|
||||
//
|
||||
// It undoes the work done by enable.
|
||||
func (n *NIC) disable() *tcpip.Error {
|
||||
n.mu.RLock()
|
||||
enabled := n.mu.enabled
|
||||
n.mu.RUnlock()
|
||||
if !enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
if !n.mu.enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(b/147015577): Should Routes that are currently bound to n be
|
||||
// invalidated? Currently, Routes will continue to work when a NIC is enabled
|
||||
// again, and applications may not know that the underlying NIC was ever
|
||||
// disabled.
|
||||
|
||||
if _, ok := n.stack.networkProtocols[header.IPv6ProtocolNumber]; ok {
|
||||
n.mu.ndp.stopSolicitingRouters()
|
||||
n.mu.ndp.cleanupState(false /* hostOnly */)
|
||||
|
||||
// Stop DAD for all the unicast IPv6 endpoints that are in the
|
||||
// permanentTentative state.
|
||||
for _, r := range n.mu.endpoints {
|
||||
if addr := r.ep.ID().LocalAddress; r.getKind() == permanentTentative && header.IsV6UnicastAddress(addr) {
|
||||
n.mu.ndp.stopDuplicateAddressDetection(addr)
|
||||
}
|
||||
}
|
||||
|
||||
// The NIC may have already left the multicast group.
|
||||
if err := n.leaveGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := n.stack.networkProtocols[header.IPv4ProtocolNumber]; ok {
|
||||
// The address may have already been removed.
|
||||
if err := n.removePermanentAddressLocked(ipv4BroadcastAddr.AddressWithPrefix.Address); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/147015577): Should n detach from its LinkEndpoint?
|
||||
|
||||
n.mu.enabled = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// enable enables n. enable will attach the nic to its LinkEndpoint and
|
||||
// join the IPv6 All-Nodes Multicast address (ff02::1).
|
||||
func (n *NIC) enable() *tcpip.Error {
|
||||
n.mu.RLock()
|
||||
@@ -158,10 +229,7 @@ func (n *NIC) enable() *tcpip.Error {
|
||||
|
||||
// Create an endpoint to receive broadcast packets on this interface.
|
||||
if _, ok := n.stack.networkProtocols[header.IPv4ProtocolNumber]; ok {
|
||||
if _, err := n.addAddressLocked(tcpip.ProtocolAddress{
|
||||
Protocol: header.IPv4ProtocolNumber,
|
||||
AddressWithPrefix: tcpip.AddressWithPrefix{header.IPv4Broadcast, 8 * header.IPv4AddressSize},
|
||||
}, NeverPrimaryEndpoint, permanent, static, false /* deprecated */); err != nil {
|
||||
if _, err := n.addAddressLocked(ipv4BroadcastAddr, NeverPrimaryEndpoint, permanent, static, false /* deprecated */); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -183,6 +251,14 @@ func (n *NIC) enable() *tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Join the All-Nodes multicast group before starting DAD as responses to DAD
|
||||
// (NDP NS) messages may be sent to the All-Nodes multicast group if the
|
||||
// source address of the NDP NS is the unspecified address, as per RFC 4861
|
||||
// section 7.2.4.
|
||||
if err := n.joinGroupLocked(header.IPv6ProtocolNumber, header.IPv6AllNodesMulticastAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Perform DAD on the all the unicast IPv6 endpoints that are in the permanent
|
||||
// state.
|
||||
//
|
||||
@@ -200,10 +276,6 @@ func (n *NIC) enable() *tcpip.Error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := n.joinGroupLocked(header.IPv6ProtocolNumber, header.IPv6AllNodesMulticastAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Do not auto-generate an IPv6 link-local address for loopback devices.
|
||||
if n.stack.autoGenIPv6LinkLocal && !n.isLoopback() {
|
||||
// The valid and preferred lifetime is infinite for the auto-generated
|
||||
@@ -234,7 +306,7 @@ func (n *NIC) becomeIPv6Router() {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
n.mu.ndp.cleanupHostOnlyState()
|
||||
n.mu.ndp.cleanupState(true /* hostOnly */)
|
||||
n.mu.ndp.stopSolicitingRouters()
|
||||
}
|
||||
|
||||
@@ -252,7 +324,9 @@ func (n *NIC) becomeIPv6Host() {
|
||||
// attachLinkEndpoint attaches the NIC to the endpoint, which will enable it
|
||||
// to start delivering packets.
|
||||
func (n *NIC) attachLinkEndpoint() {
|
||||
n.linkEP.Attach(n)
|
||||
n.attach.Do(func() {
|
||||
n.linkEP.Attach(n)
|
||||
})
|
||||
}
|
||||
|
||||
// setPromiscuousMode enables or disables promiscuous mode.
|
||||
@@ -712,6 +786,7 @@ func (n *NIC) AllAddresses() []tcpip.ProtocolAddress {
|
||||
case permanentExpired, temporary:
|
||||
continue
|
||||
}
|
||||
|
||||
addrs = append(addrs, tcpip.ProtocolAddress{
|
||||
Protocol: ref.protocol,
|
||||
AddressWithPrefix: tcpip.AddressWithPrefix{
|
||||
@@ -1009,6 +1084,15 @@ func (n *NIC) leaveGroupLocked(addr tcpip.Address) *tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isInGroup returns true if n has joined the multicast group addr.
|
||||
func (n *NIC) isInGroup(addr tcpip.Address) bool {
|
||||
n.mu.RLock()
|
||||
joins := n.mu.mcastJoins[NetworkEndpointID{addr}]
|
||||
n.mu.RUnlock()
|
||||
|
||||
return joins != 0
|
||||
}
|
||||
|
||||
func handlePacket(protocol tcpip.NetworkProtocolNumber, dst, src tcpip.Address, localLinkAddr, remotelinkAddr tcpip.LinkAddress, ref *referencedNetworkEndpoint, pkt tcpip.PacketBuffer) {
|
||||
r := makeRoute(protocol, dst, src, localLinkAddr, ref, false /* handleLocal */, false /* multicastLoop */)
|
||||
r.RemoteLinkAddress = remotelinkAddr
|
||||
@@ -1411,7 +1495,7 @@ func (r *referencedNetworkEndpoint) isValidForOutgoing() bool {
|
||||
//
|
||||
// r's NIC must be read locked.
|
||||
func (r *referencedNetworkEndpoint) isValidForOutgoingRLocked() bool {
|
||||
return r.getKind() != permanentExpired || r.nic.mu.spoofing
|
||||
return r.nic.mu.enabled && (r.getKind() != permanentExpired || r.nic.mu.spoofing)
|
||||
}
|
||||
|
||||
// decRef decrements the ref count and cleans up the endpoint once it reaches
|
||||
|
||||
@@ -921,23 +921,38 @@ func (s *Stack) EnableNIC(id tcpip.NICID) *tcpip.Error {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
nic := s.nics[id]
|
||||
if nic == nil {
|
||||
nic, ok := s.nics[id]
|
||||
if !ok {
|
||||
return tcpip.ErrUnknownNICID
|
||||
}
|
||||
|
||||
return nic.enable()
|
||||
}
|
||||
|
||||
// DisableNIC disables the given NIC.
|
||||
func (s *Stack) DisableNIC(id tcpip.NICID) *tcpip.Error {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
nic, ok := s.nics[id]
|
||||
if !ok {
|
||||
return tcpip.ErrUnknownNICID
|
||||
}
|
||||
|
||||
return nic.disable()
|
||||
}
|
||||
|
||||
// CheckNIC checks if a NIC is usable.
|
||||
func (s *Stack) CheckNIC(id tcpip.NICID) bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
nic, ok := s.nics[id]
|
||||
s.mu.RUnlock()
|
||||
if ok {
|
||||
return nic.linkEP.IsAttached()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
|
||||
return nic.enabled()
|
||||
}
|
||||
|
||||
// NICAddressRanges returns a map of NICIDs to their associated subnets.
|
||||
@@ -989,7 +1004,7 @@ func (s *Stack) NICInfo() map[tcpip.NICID]NICInfo {
|
||||
for id, nic := range s.nics {
|
||||
flags := NICStateFlags{
|
||||
Up: true, // Netstack interfaces are always up.
|
||||
Running: nic.linkEP.IsAttached(),
|
||||
Running: nic.enabled(),
|
||||
Promiscuous: nic.isPromiscuousMode(),
|
||||
Loopback: nic.isLoopback(),
|
||||
}
|
||||
@@ -1151,7 +1166,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
isMulticast := header.IsV4MulticastAddress(remoteAddr) || header.IsV6MulticastAddress(remoteAddr)
|
||||
needRoute := !(isBroadcast || isMulticast || header.IsV6LinkLocalAddress(remoteAddr))
|
||||
if id != 0 && !needRoute {
|
||||
if nic, ok := s.nics[id]; ok {
|
||||
if nic, ok := s.nics[id]; ok && nic.enabled() {
|
||||
if ref := s.getRefEP(nic, localAddr, remoteAddr, netProto); ref != nil {
|
||||
return makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, nic.linkEP.LinkAddress(), ref, s.handleLocal && !nic.isLoopback(), multicastLoop && !nic.isLoopback()), nil
|
||||
}
|
||||
@@ -1161,7 +1176,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
if (id != 0 && id != route.NIC) || (len(remoteAddr) != 0 && !route.Destination.Contains(remoteAddr)) {
|
||||
continue
|
||||
}
|
||||
if nic, ok := s.nics[route.NIC]; ok {
|
||||
if nic, ok := s.nics[route.NIC]; ok && nic.enabled() {
|
||||
if ref := s.getRefEP(nic, localAddr, remoteAddr, netProto); ref != nil {
|
||||
if len(remoteAddr) == 0 {
|
||||
// If no remote address was provided, then the route
|
||||
@@ -1614,6 +1629,18 @@ func (s *Stack) LeaveGroup(protocol tcpip.NetworkProtocolNumber, nicID tcpip.NIC
|
||||
return tcpip.ErrUnknownNICID
|
||||
}
|
||||
|
||||
// IsInGroup returns true if the NIC with ID nicID has joined the multicast
|
||||
// group multicastAddr.
|
||||
func (s *Stack) IsInGroup(nicID tcpip.NICID, multicastAddr tcpip.Address) (bool, *tcpip.Error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
if nic, ok := s.nics[nicID]; ok {
|
||||
return nic.isInGroup(multicastAddr), nil
|
||||
}
|
||||
return false, tcpip.ErrUnknownNICID
|
||||
}
|
||||
|
||||
// IPTables returns the stack's iptables.
|
||||
func (s *Stack) IPTables() iptables.IPTables {
|
||||
s.tablesMu.RLock()
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
@@ -509,6 +510,257 @@ func testNoRoute(t *testing.T, s *stack.Stack, nic tcpip.NICID, srcAddr, dstAddr
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisableUnknownNIC(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
|
||||
})
|
||||
|
||||
if err := s.DisableNIC(1); err != tcpip.ErrUnknownNICID {
|
||||
t.Fatalf("got s.DisableNIC(1) = %v, want = %s", err, tcpip.ErrUnknownNICID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisabledNICsNICInfoAndCheckNIC(t *testing.T) {
|
||||
const nicID = 1
|
||||
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
|
||||
})
|
||||
|
||||
e := loopback.New()
|
||||
nicOpts := stack.NICOptions{Disabled: true}
|
||||
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
|
||||
t.Fatalf("CreateNICWithOptions(%d, _, %+v) = %s", nicID, nicOpts, err)
|
||||
}
|
||||
|
||||
checkNIC := func(enabled bool) {
|
||||
t.Helper()
|
||||
|
||||
allNICInfo := s.NICInfo()
|
||||
nicInfo, ok := allNICInfo[nicID]
|
||||
if !ok {
|
||||
t.Errorf("entry for %d missing from allNICInfo = %+v", nicID, allNICInfo)
|
||||
} else if nicInfo.Flags.Running != enabled {
|
||||
t.Errorf("got nicInfo.Flags.Running = %t, want = %t", nicInfo.Flags.Running, enabled)
|
||||
}
|
||||
|
||||
if got := s.CheckNIC(nicID); got != enabled {
|
||||
t.Errorf("got s.CheckNIC(%d) = %t, want = %t", nicID, got, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
// NIC should initially report itself as disabled.
|
||||
checkNIC(false)
|
||||
|
||||
if err := s.EnableNIC(nicID); err != nil {
|
||||
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
|
||||
}
|
||||
checkNIC(true)
|
||||
|
||||
// If the NIC is not reporting a correct enabled status, we cannot trust the
|
||||
// next check so end the test here.
|
||||
if t.Failed() {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if err := s.DisableNIC(nicID); err != nil {
|
||||
t.Fatalf("s.DisableNIC(%d): %s", nicID, err)
|
||||
}
|
||||
checkNIC(false)
|
||||
}
|
||||
|
||||
func TestRoutesWithDisabledNIC(t *testing.T) {
|
||||
const unspecifiedNIC = 0
|
||||
const nicID1 = 1
|
||||
const nicID2 = 2
|
||||
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
|
||||
})
|
||||
|
||||
ep1 := channel.New(0, defaultMTU, "")
|
||||
if err := s.CreateNIC(nicID1, ep1); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _): %s", nicID1, err)
|
||||
}
|
||||
|
||||
addr1 := tcpip.Address("\x01")
|
||||
if err := s.AddAddress(nicID1, fakeNetNumber, addr1); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s): %s", nicID1, fakeNetNumber, addr1, err)
|
||||
}
|
||||
|
||||
ep2 := channel.New(0, defaultMTU, "")
|
||||
if err := s.CreateNIC(nicID2, ep2); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _): %s", nicID2, err)
|
||||
}
|
||||
|
||||
addr2 := tcpip.Address("\x02")
|
||||
if err := s.AddAddress(nicID2, fakeNetNumber, addr2); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s): %s", nicID2, fakeNetNumber, addr2, err)
|
||||
}
|
||||
|
||||
// Set a route table that sends all packets with odd destination
|
||||
// addresses through the first NIC, and all even destination address
|
||||
// through the second one.
|
||||
{
|
||||
subnet0, err := tcpip.NewSubnet("\x00", "\x01")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subnet1, err := tcpip.NewSubnet("\x01", "\x01")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.SetRouteTable([]tcpip.Route{
|
||||
{Destination: subnet1, Gateway: "\x00", NIC: nicID1},
|
||||
{Destination: subnet0, Gateway: "\x00", NIC: nicID2},
|
||||
})
|
||||
}
|
||||
|
||||
// Test routes to odd address.
|
||||
testRoute(t, s, unspecifiedNIC, "", "\x05", addr1)
|
||||
testRoute(t, s, unspecifiedNIC, addr1, "\x05", addr1)
|
||||
testRoute(t, s, nicID1, addr1, "\x05", addr1)
|
||||
|
||||
// Test routes to even address.
|
||||
testRoute(t, s, unspecifiedNIC, "", "\x06", addr2)
|
||||
testRoute(t, s, unspecifiedNIC, addr2, "\x06", addr2)
|
||||
testRoute(t, s, nicID2, addr2, "\x06", addr2)
|
||||
|
||||
// Disabling NIC1 should result in no routes to odd addresses. Routes to even
|
||||
// addresses should continue to be available as NIC2 is still enabled.
|
||||
if err := s.DisableNIC(nicID1); err != nil {
|
||||
t.Fatalf("s.DisableNIC(%d): %s", nicID1, err)
|
||||
}
|
||||
nic1Dst := tcpip.Address("\x05")
|
||||
testNoRoute(t, s, unspecifiedNIC, "", nic1Dst)
|
||||
testNoRoute(t, s, unspecifiedNIC, addr1, nic1Dst)
|
||||
testNoRoute(t, s, nicID1, addr1, nic1Dst)
|
||||
nic2Dst := tcpip.Address("\x06")
|
||||
testRoute(t, s, unspecifiedNIC, "", nic2Dst, addr2)
|
||||
testRoute(t, s, unspecifiedNIC, addr2, nic2Dst, addr2)
|
||||
testRoute(t, s, nicID2, addr2, nic2Dst, addr2)
|
||||
|
||||
// Disabling NIC2 should result in no routes to even addresses. No route
|
||||
// should be available to any address as routes to odd addresses were made
|
||||
// unavailable by disabling NIC1 above.
|
||||
if err := s.DisableNIC(nicID2); err != nil {
|
||||
t.Fatalf("s.DisableNIC(%d): %s", nicID2, err)
|
||||
}
|
||||
testNoRoute(t, s, unspecifiedNIC, "", nic1Dst)
|
||||
testNoRoute(t, s, unspecifiedNIC, addr1, nic1Dst)
|
||||
testNoRoute(t, s, nicID1, addr1, nic1Dst)
|
||||
testNoRoute(t, s, unspecifiedNIC, "", nic2Dst)
|
||||
testNoRoute(t, s, unspecifiedNIC, addr2, nic2Dst)
|
||||
testNoRoute(t, s, nicID2, addr2, nic2Dst)
|
||||
|
||||
// Enabling NIC1 should make routes to odd addresses available again. Routes
|
||||
// to even addresses should continue to be unavailable as NIC2 is still
|
||||
// disabled.
|
||||
if err := s.EnableNIC(nicID1); err != nil {
|
||||
t.Fatalf("s.EnableNIC(%d): %s", nicID1, err)
|
||||
}
|
||||
testRoute(t, s, unspecifiedNIC, "", nic1Dst, addr1)
|
||||
testRoute(t, s, unspecifiedNIC, addr1, nic1Dst, addr1)
|
||||
testRoute(t, s, nicID1, addr1, nic1Dst, addr1)
|
||||
testNoRoute(t, s, unspecifiedNIC, "", nic2Dst)
|
||||
testNoRoute(t, s, unspecifiedNIC, addr2, nic2Dst)
|
||||
testNoRoute(t, s, nicID2, addr2, nic2Dst)
|
||||
}
|
||||
|
||||
func TestRouteWritePacketWithDisabledNIC(t *testing.T) {
|
||||
const unspecifiedNIC = 0
|
||||
const nicID1 = 1
|
||||
const nicID2 = 2
|
||||
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
|
||||
})
|
||||
|
||||
ep1 := channel.New(1, defaultMTU, "")
|
||||
if err := s.CreateNIC(nicID1, ep1); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _): %s", nicID1, err)
|
||||
}
|
||||
|
||||
addr1 := tcpip.Address("\x01")
|
||||
if err := s.AddAddress(nicID1, fakeNetNumber, addr1); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s): %s", nicID1, fakeNetNumber, addr1, err)
|
||||
}
|
||||
|
||||
ep2 := channel.New(1, defaultMTU, "")
|
||||
if err := s.CreateNIC(nicID2, ep2); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _): %s", nicID2, err)
|
||||
}
|
||||
|
||||
addr2 := tcpip.Address("\x02")
|
||||
if err := s.AddAddress(nicID2, fakeNetNumber, addr2); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s): %s", nicID2, fakeNetNumber, addr2, err)
|
||||
}
|
||||
|
||||
// Set a route table that sends all packets with odd destination
|
||||
// addresses through the first NIC, and all even destination address
|
||||
// through the second one.
|
||||
{
|
||||
subnet0, err := tcpip.NewSubnet("\x00", "\x01")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subnet1, err := tcpip.NewSubnet("\x01", "\x01")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.SetRouteTable([]tcpip.Route{
|
||||
{Destination: subnet1, Gateway: "\x00", NIC: nicID1},
|
||||
{Destination: subnet0, Gateway: "\x00", NIC: nicID2},
|
||||
})
|
||||
}
|
||||
|
||||
nic1Dst := tcpip.Address("\x05")
|
||||
r1, err := s.FindRoute(nicID1, addr1, nic1Dst, fakeNetNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Errorf("FindRoute(%d, %s, %s, %d, false): %s", nicID1, addr1, nic1Dst, fakeNetNumber, err)
|
||||
}
|
||||
defer r1.Release()
|
||||
|
||||
nic2Dst := tcpip.Address("\x06")
|
||||
r2, err := s.FindRoute(nicID2, addr2, nic2Dst, fakeNetNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Errorf("FindRoute(%d, %s, %s, %d, false): %s", nicID2, addr2, nic2Dst, fakeNetNumber, err)
|
||||
}
|
||||
defer r2.Release()
|
||||
|
||||
// If we failed to get routes r1 or r2, we cannot proceed with the test.
|
||||
if t.Failed() {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
buf := buffer.View([]byte{1})
|
||||
testSend(t, r1, ep1, buf)
|
||||
testSend(t, r2, ep2, buf)
|
||||
|
||||
// Writes with Routes that use the disabled NIC1 should fail.
|
||||
if err := s.DisableNIC(nicID1); err != nil {
|
||||
t.Fatalf("s.DisableNIC(%d): %s", nicID1, err)
|
||||
}
|
||||
testFailingSend(t, r1, ep1, buf, tcpip.ErrInvalidEndpointState)
|
||||
testSend(t, r2, ep2, buf)
|
||||
|
||||
// Writes with Routes that use the disabled NIC2 should fail.
|
||||
if err := s.DisableNIC(nicID2); err != nil {
|
||||
t.Fatalf("s.DisableNIC(%d): %s", nicID2, err)
|
||||
}
|
||||
testFailingSend(t, r1, ep1, buf, tcpip.ErrInvalidEndpointState)
|
||||
testFailingSend(t, r2, ep2, buf, tcpip.ErrInvalidEndpointState)
|
||||
|
||||
// Writes with Routes that use the re-enabled NIC1 should succeed.
|
||||
// TODO(b/147015577): Should we instead completely invalidate all Routes that
|
||||
// were bound to a disabled NIC at some point?
|
||||
if err := s.EnableNIC(nicID1); err != nil {
|
||||
t.Fatalf("s.EnableNIC(%d): %s", nicID1, err)
|
||||
}
|
||||
testSend(t, r1, ep1, buf)
|
||||
testFailingSend(t, r2, ep2, buf, tcpip.ErrInvalidEndpointState)
|
||||
}
|
||||
|
||||
func TestRoutes(t *testing.T) {
|
||||
// Create a stack with the fake network protocol, two nics, and two
|
||||
// addresses per nic, the first nic has odd address, the second one has
|
||||
@@ -2173,13 +2425,29 @@ func TestNICAutoGenLinkLocalAddr(t *testing.T) {
|
||||
|
||||
e := channel.New(0, 1280, test.linkAddr)
|
||||
s := stack.New(opts)
|
||||
nicOpts := stack.NICOptions{Name: test.nicName}
|
||||
nicOpts := stack.NICOptions{Name: test.nicName, Disabled: true}
|
||||
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
|
||||
t.Fatalf("CreateNICWithOptions(%d, _, %+v) = %s", nicID, opts, err)
|
||||
}
|
||||
|
||||
var expectedMainAddr tcpip.AddressWithPrefix
|
||||
// A new disabled NIC should not have any address, even if auto generation
|
||||
// was enabled.
|
||||
allStackAddrs := s.AllAddresses()
|
||||
allNICAddrs, ok := allStackAddrs[nicID]
|
||||
if !ok {
|
||||
t.Fatalf("entry for %d missing from allStackAddrs = %+v", nicID, allStackAddrs)
|
||||
}
|
||||
if l := len(allNICAddrs); l != 0 {
|
||||
t.Fatalf("got len(allNICAddrs) = %d, want = 0", l)
|
||||
}
|
||||
|
||||
// Enabling the NIC should attempt auto-generation of a link-local
|
||||
// address.
|
||||
if err := s.EnableNIC(nicID); err != nil {
|
||||
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
|
||||
}
|
||||
|
||||
var expectedMainAddr tcpip.AddressWithPrefix
|
||||
if test.shouldGen {
|
||||
expectedMainAddr = tcpip.AddressWithPrefix{
|
||||
Address: test.expectedAddr,
|
||||
@@ -2609,6 +2877,111 @@ func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddRemoveIPv4BroadcastAddressOnNICEnableDisable(t *testing.T) {
|
||||
const nicID = 1
|
||||
|
||||
e := loopback.New()
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol()},
|
||||
})
|
||||
nicOpts := stack.NICOptions{Disabled: true}
|
||||
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _, %+v) = %s", nicID, nicOpts, err)
|
||||
}
|
||||
|
||||
allStackAddrs := s.AllAddresses()
|
||||
allNICAddrs, ok := allStackAddrs[nicID]
|
||||
if !ok {
|
||||
t.Fatalf("entry for %d missing from allStackAddrs = %+v", nicID, allStackAddrs)
|
||||
}
|
||||
if l := len(allNICAddrs); l != 0 {
|
||||
t.Fatalf("got len(allNICAddrs) = %d, want = 0", l)
|
||||
}
|
||||
|
||||
// Enabling the NIC should add the IPv4 broadcast address.
|
||||
if err := s.EnableNIC(nicID); err != nil {
|
||||
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
|
||||
}
|
||||
allStackAddrs = s.AllAddresses()
|
||||
allNICAddrs, ok = allStackAddrs[nicID]
|
||||
if !ok {
|
||||
t.Fatalf("entry for %d missing from allStackAddrs = %+v", nicID, allStackAddrs)
|
||||
}
|
||||
if l := len(allNICAddrs); l != 1 {
|
||||
t.Fatalf("got len(allNICAddrs) = %d, want = 1", l)
|
||||
}
|
||||
want := tcpip.ProtocolAddress{
|
||||
Protocol: header.IPv4ProtocolNumber,
|
||||
AddressWithPrefix: tcpip.AddressWithPrefix{
|
||||
Address: header.IPv4Broadcast,
|
||||
PrefixLen: 32,
|
||||
},
|
||||
}
|
||||
if allNICAddrs[0] != want {
|
||||
t.Fatalf("got allNICAddrs[0] = %+v, want = %+v", allNICAddrs[0], want)
|
||||
}
|
||||
|
||||
// Disabling the NIC should remove the IPv4 broadcast address.
|
||||
if err := s.DisableNIC(nicID); err != nil {
|
||||
t.Fatalf("s.DisableNIC(%d): %s", nicID, err)
|
||||
}
|
||||
allStackAddrs = s.AllAddresses()
|
||||
allNICAddrs, ok = allStackAddrs[nicID]
|
||||
if !ok {
|
||||
t.Fatalf("entry for %d missing from allStackAddrs = %+v", nicID, allStackAddrs)
|
||||
}
|
||||
if l := len(allNICAddrs); l != 0 {
|
||||
t.Fatalf("got len(allNICAddrs) = %d, want = 0", l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinLeaveAllNodesMulticastOnNICEnableDisable(t *testing.T) {
|
||||
const nicID = 1
|
||||
|
||||
e := loopback.New()
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
|
||||
})
|
||||
nicOpts := stack.NICOptions{Disabled: true}
|
||||
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _, %+v) = %s", nicID, nicOpts, err)
|
||||
}
|
||||
|
||||
// Should not be in the IPv6 all-nodes multicast group yet because the NIC has
|
||||
// not been enabled yet.
|
||||
isInGroup, err := s.IsInGroup(nicID, header.IPv6AllNodesMulticastAddress)
|
||||
if err != nil {
|
||||
t.Fatalf("IsInGroup(%d, %s): %s", nicID, header.IPv6AllNodesMulticastAddress, err)
|
||||
}
|
||||
if isInGroup {
|
||||
t.Fatalf("got IsInGroup(%d, %s) = true, want = false", nicID, header.IPv6AllNodesMulticastAddress)
|
||||
}
|
||||
|
||||
// The all-nodes multicast group should be joined when the NIC is enabled.
|
||||
if err := s.EnableNIC(nicID); err != nil {
|
||||
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
|
||||
}
|
||||
isInGroup, err = s.IsInGroup(nicID, header.IPv6AllNodesMulticastAddress)
|
||||
if err != nil {
|
||||
t.Fatalf("IsInGroup(%d, %s): %s", nicID, header.IPv6AllNodesMulticastAddress, err)
|
||||
}
|
||||
if !isInGroup {
|
||||
t.Fatalf("got IsInGroup(%d, %s) = false, want = true", nicID, header.IPv6AllNodesMulticastAddress)
|
||||
}
|
||||
|
||||
// The all-nodes multicast group should be left when the NIC is disabled.
|
||||
if err := s.DisableNIC(nicID); err != nil {
|
||||
t.Fatalf("s.DisableNIC(%d): %s", nicID, err)
|
||||
}
|
||||
isInGroup, err = s.IsInGroup(nicID, header.IPv6AllNodesMulticastAddress)
|
||||
if err != nil {
|
||||
t.Fatalf("IsInGroup(%d, %s): %s", nicID, header.IPv6AllNodesMulticastAddress, err)
|
||||
}
|
||||
if isInGroup {
|
||||
t.Fatalf("got IsInGroup(%d, %s) = true, want = false", nicID, header.IPv6AllNodesMulticastAddress)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDoDADWhenNICEnabled tests that IPv6 endpoints that were added while a NIC
|
||||
// was disabled have DAD performed on them when the NIC is enabled.
|
||||
func TestDoDADWhenNICEnabled(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user