Do IPv6 Stateless Address Auto-Configuration (SLAAC)

This change allows the netstack to do SLAAC as outlined by RFC 4862 section 5.5.

Note, this change will not break existing uses of netstack as the default
configuration for the stack options is set in such a way that SLAAC
will not be performed. See `stack.Options` and `stack.NDPConfigurations` for
more details.

This change reuses 1 option and introduces a new one that is required to take
advantage of SLAAC, all available under NDPConfigurations:
- HandleRAs: Whether or not NDP RAs are processes
- AutoGenGlobalAddresses: Whether or not SLAAC is performed.

Also note, this change does not deprecate SLAAC generated addresses after the
preferred lifetime. That will come in a later change (b/143713887). Currently,
only the valid lifetime is honoured.

Tests: Unittest to make sure that SLAAC generates and adds addresses only when
configured to do so. Tests also makes sure that conflicts with static addresses
do not modify the static address.
PiperOrigin-RevId: 284265317
This commit is contained in:
Ghanan Gowripalan
2019-12-06 14:41:30 -08:00
committed by gVisor bot
parent 663fe840f7
commit ab3f7bc393
5 changed files with 1109 additions and 148 deletions
+38 -11
View File
@@ -90,6 +90,18 @@ const (
// IPv6Any is the non-routable IPv6 "any" meta address. It is also
// known as the unspecified address.
IPv6Any tcpip.Address = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
// IIDSize is the size of an interface identifier (IID), in bytes, as
// defined by RFC 4291 section 2.5.1.
IIDSize = 8
// IIDOffsetInIPv6Address is the offset, in bytes, from the start
// of an IPv6 address to the beginning of the interface identifier
// (IID) for auto-generated addresses. That is, all bytes before
// the IIDOffsetInIPv6Address-th byte are the prefix bytes, and all
// bytes including and after the IIDOffsetInIPv6Address-th byte are
// for the IID.
IIDOffsetInIPv6Address = 8
)
// IPv6EmptySubnet is the empty IPv6 subnet. It may also be known as the
@@ -266,6 +278,28 @@ func SolicitedNodeAddr(addr tcpip.Address) tcpip.Address {
return solicitedNodeMulticastPrefix + addr[len(addr)-3:]
}
// EthernetAdddressToEUI64IntoBuf populates buf with a EUI-64 from a 48-bit
// Ethernet/MAC address.
//
// buf MUST be at least 8 bytes.
func EthernetAdddressToEUI64IntoBuf(linkAddr tcpip.LinkAddress, buf []byte) {
buf[0] = linkAddr[0] ^ 2
buf[1] = linkAddr[1]
buf[2] = linkAddr[2]
buf[3] = 0xFE
buf[4] = 0xFE
buf[5] = linkAddr[3]
buf[6] = linkAddr[4]
buf[7] = linkAddr[5]
}
// EthernetAddressToEUI64 computes an EUI-64 from a 48-bit Ethernet/MAC address.
func EthernetAddressToEUI64(linkAddr tcpip.LinkAddress) [IIDSize]byte {
var buf [IIDSize]byte
EthernetAdddressToEUI64IntoBuf(linkAddr, buf[:])
return buf
}
// LinkLocalAddr computes the default IPv6 link-local address from a link-layer
// (MAC) address.
func LinkLocalAddr(linkAddr tcpip.LinkAddress) tcpip.Address {
@@ -275,18 +309,11 @@ func LinkLocalAddr(linkAddr tcpip.LinkAddress) tcpip.Address {
// The conversion is very nearly:
// aa:bb:cc:dd:ee:ff => FE80::Aabb:ccFF:FEdd:eeff
// Note the capital A. The conversion aa->Aa involves a bit flip.
lladdrb := [16]byte{
0: 0xFE,
1: 0x80,
8: linkAddr[0] ^ 2,
9: linkAddr[1],
10: linkAddr[2],
11: 0xFF,
12: 0xFE,
13: linkAddr[3],
14: linkAddr[4],
15: linkAddr[5],
lladdrb := [IPv6AddressSize]byte{
0: 0xFE,
1: 0x80,
}
EthernetAdddressToEUI64IntoBuf(linkAddr, lladdrb[IIDOffsetInIPv6Address:])
return tcpip.Address(lladdrb[:])
}
+5 -4
View File
@@ -17,6 +17,7 @@ package header
import (
"encoding/binary"
"errors"
"math"
"time"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -109,13 +110,13 @@ const (
)
var (
// NDPInfiniteLifetime is a value that represents
// infinity for the Valid and Preferred Lifetime fields in a NDP Prefix
// Information option. Its value is (2^32 - 1)s = 4294967295s
// NDPInfiniteLifetime is a value that represents infinity for the
// 4-byte lifetime fields found in various NDP options. Its value is
// (2^32 - 1)s = 4294967295s.
//
// This is a variable instead of a constant so that tests can change
// this value to a smaller value. It should only be modified by tests.
NDPInfiniteLifetime = time.Second * 4294967295
NDPInfiniteLifetime = time.Second * math.MaxUint32
)
// NDPOptionIterator is an iterator of NDPOption.
+431 -78
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+58 -16
View File
@@ -115,10 +115,11 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, loopback
},
},
ndp: ndpState{
configs: stack.ndpConfigs,
dad: make(map[tcpip.Address]dadState),
defaultRouters: make(map[tcpip.Address]defaultRouterState),
onLinkPrefixes: make(map[tcpip.Subnet]onLinkPrefixState),
configs: stack.ndpConfigs,
dad: make(map[tcpip.Address]dadState),
defaultRouters: make(map[tcpip.Address]defaultRouterState),
onLinkPrefixes: make(map[tcpip.Subnet]onLinkPrefixState),
autoGenAddresses: make(map[tcpip.Address]autoGenAddressState),
},
}
nic.ndp.nic = nic
@@ -244,6 +245,20 @@ func (n *NIC) primaryEndpoint(protocol tcpip.NetworkProtocolNumber) *referencedN
return nil
}
// hasPermanentAddrLocked returns true if n has a permanent (including currently
// tentative) address, addr.
func (n *NIC) hasPermanentAddrLocked(addr tcpip.Address) bool {
ref, ok := n.endpoints[NetworkEndpointID{addr}]
if !ok {
return false
}
kind := ref.getKind()
return kind == permanent || kind == permanentTentative
}
func (n *NIC) getRef(protocol tcpip.NetworkProtocolNumber, dst tcpip.Address) *referencedNetworkEndpoint {
return n.getRefOrCreateTemp(protocol, dst, CanBePrimaryEndpoint, n.promiscuous)
}
@@ -335,7 +350,7 @@ func (n *NIC) getRefOrCreateTemp(protocol tcpip.NetworkProtocolNumber, address t
Address: address,
PrefixLen: netProto.DefaultPrefixLen(),
},
}, peb, temporary)
}, peb, temporary, static)
n.mu.Unlock()
return ref
@@ -384,10 +399,10 @@ func (n *NIC) addPermanentAddressLocked(protocolAddress tcpip.ProtocolAddress, p
}
}
return n.addAddressLocked(protocolAddress, peb, permanent)
return n.addAddressLocked(protocolAddress, peb, permanent, static)
}
func (n *NIC) addAddressLocked(protocolAddress tcpip.ProtocolAddress, peb PrimaryEndpointBehavior, kind networkEndpointKind) (*referencedNetworkEndpoint, *tcpip.Error) {
func (n *NIC) addAddressLocked(protocolAddress tcpip.ProtocolAddress, peb PrimaryEndpointBehavior, kind networkEndpointKind, configType networkEndpointConfigType) (*referencedNetworkEndpoint, *tcpip.Error) {
// TODO(b/141022673): Validate IP address before adding them.
// Sanity check.
@@ -417,11 +432,12 @@ func (n *NIC) addAddressLocked(protocolAddress tcpip.ProtocolAddress, peb Primar
}
ref := &referencedNetworkEndpoint{
refs: 1,
ep: ep,
nic: n,
protocol: protocolAddress.Protocol,
kind: kind,
refs: 1,
ep: ep,
nic: n,
protocol: protocolAddress.Protocol,
kind: kind,
configType: configType,
}
// Set up cache if link address resolution exists for this protocol.
@@ -624,9 +640,18 @@ func (n *NIC) removePermanentAddressLocked(addr tcpip.Address) *tcpip.Error {
isIPv6Unicast := r.protocol == header.IPv6ProtocolNumber && header.IsV6UnicastAddress(addr)
// If we are removing a tentative IPv6 unicast address, stop DAD.
if isIPv6Unicast && kind == permanentTentative {
n.ndp.stopDuplicateAddressDetection(addr)
if isIPv6Unicast {
// If we are removing a tentative IPv6 unicast address, stop
// DAD.
if kind == permanentTentative {
n.ndp.stopDuplicateAddressDetection(addr)
}
// If we are removing an address generated via SLAAC, cleanup
// its SLAAC resources and notify the integrator.
if r.configType == slaac {
n.ndp.cleanupAutoGenAddrResourcesAndNotify(addr)
}
}
r.setKind(permanentExpired)
@@ -989,7 +1014,7 @@ const (
// removing the permanent address from the NIC.
permanent
// An expired permanent endoint is a permanent endoint that had its address
// An expired permanent endpoint is a permanent endpoint that had its address
// removed from the NIC, and it is waiting to be removed once no more routes
// hold a reference to it. This is achieved by decreasing its reference count
// by 1. If its address is re-added before the endpoint is removed, its type
@@ -1035,6 +1060,19 @@ func (n *NIC) unregisterPacketEndpoint(netProto tcpip.NetworkProtocolNumber, ep
}
}
type networkEndpointConfigType int32
const (
// A statically configured endpoint is an address that was added by
// some user-specified action (adding an explicit address, joining a
// multicast group).
static networkEndpointConfigType = iota
// A slaac configured endpoint is an IPv6 endpoint that was
// added by SLAAC as per RFC 4862 section 5.5.3.
slaac
)
type referencedNetworkEndpoint struct {
ep NetworkEndpoint
nic *NIC
@@ -1050,6 +1088,10 @@ type referencedNetworkEndpoint struct {
// networkEndpointKind must only be accessed using {get,set}Kind().
kind networkEndpointKind
// configType is the method that was used to configure this endpoint.
// This must never change after the endpoint is added to a NIC.
configType networkEndpointConfigType
}
func (r *referencedNetworkEndpoint) getKind() networkEndpointKind {