mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
committed by
gVisor bot
parent
663fe840f7
commit
ab3f7bc393
+38
-11
@@ -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[:])
|
||||
}
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+577
-39
File diff suppressed because it is too large
Load Diff
+58
-16
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user