mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Specify the source of outgoing NDP RS
If the NIC has a valid IPv6 address assigned, use it as the source address for outgoing NDP Router Solicitation packets. Test: stack_test.TestRouterSolicitation PiperOrigin-RevId: 299398763
This commit is contained in:
committed by
gVisor bot
parent
20170d4fd5
commit
f50d9a31e9
@@ -785,6 +785,52 @@ func NDPNSTargetAddress(want tcpip.Address) TransportChecker {
|
||||
}
|
||||
}
|
||||
|
||||
// ndpOptions checks that optsBuf only contains opts.
|
||||
func ndpOptions(t *testing.T, optsBuf header.NDPOptions, opts []header.NDPOption) {
|
||||
t.Helper()
|
||||
|
||||
it, err := optsBuf.Iter(true)
|
||||
if err != nil {
|
||||
t.Errorf("optsBuf.Iter(true): %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for {
|
||||
opt, done, err := it.Next()
|
||||
if err != nil {
|
||||
// This should never happen as Iter(true) above did not return an error.
|
||||
t.Fatalf("unexpected error when iterating over NDP options: %s", err)
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
|
||||
if i >= len(opts) {
|
||||
t.Errorf("got unexpected option: %s", opt)
|
||||
continue
|
||||
}
|
||||
|
||||
switch wantOpt := opts[i].(type) {
|
||||
case header.NDPSourceLinkLayerAddressOption:
|
||||
gotOpt, ok := opt.(header.NDPSourceLinkLayerAddressOption)
|
||||
if !ok {
|
||||
t.Errorf("got type = %T at index = %d; want = %T", opt, i, wantOpt)
|
||||
} else if got, want := gotOpt.EthernetAddress(), wantOpt.EthernetAddress(); got != want {
|
||||
t.Errorf("got EthernetAddress() = %s at index %d, want = %s", got, i, want)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("checker not implemented for expected NDP option: %T", wantOpt)
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
if missing := opts[i:]; len(missing) > 0 {
|
||||
t.Errorf("missing options: %s", missing)
|
||||
}
|
||||
}
|
||||
|
||||
// NDPNSOptions creates a checker that checks that the packet contains the
|
||||
// provided NDP options within an NDP Neighbor Solicitation message.
|
||||
//
|
||||
@@ -796,47 +842,31 @@ func NDPNSOptions(opts []header.NDPOption) TransportChecker {
|
||||
|
||||
icmp := h.(header.ICMPv6)
|
||||
ns := header.NDPNeighborSolicit(icmp.NDPPayload())
|
||||
it, err := ns.Options().Iter(true)
|
||||
if err != nil {
|
||||
t.Errorf("opts.Iter(true): %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for {
|
||||
opt, done, _ := it.Next()
|
||||
if done {
|
||||
break
|
||||
}
|
||||
|
||||
if i >= len(opts) {
|
||||
t.Errorf("got unexpected option: %s", opt)
|
||||
continue
|
||||
}
|
||||
|
||||
switch wantOpt := opts[i].(type) {
|
||||
case header.NDPSourceLinkLayerAddressOption:
|
||||
gotOpt, ok := opt.(header.NDPSourceLinkLayerAddressOption)
|
||||
if !ok {
|
||||
t.Errorf("got type = %T at index = %d; want = %T", opt, i, wantOpt)
|
||||
} else if got, want := gotOpt.EthernetAddress(), wantOpt.EthernetAddress(); got != want {
|
||||
t.Errorf("got EthernetAddress() = %s at index %d, want = %s", got, i, want)
|
||||
}
|
||||
default:
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
if missing := opts[i:]; len(missing) > 0 {
|
||||
t.Errorf("missing options: %s", missing)
|
||||
}
|
||||
ndpOptions(t, ns.Options(), opts)
|
||||
}
|
||||
}
|
||||
|
||||
// NDPRS creates a checker that checks that the packet contains a valid NDP
|
||||
// Router Solicitation message (as per the raw wire format).
|
||||
func NDPRS() NetworkChecker {
|
||||
return NDP(header.ICMPv6RouterSolicit, header.NDPRSMinimumSize)
|
||||
//
|
||||
// checkers may assume that a valid ICMPv6 is passed to it containing a valid
|
||||
// NDPRS as far as the size of the message is concerned. The values within the
|
||||
// message are up to checkers to validate.
|
||||
func NDPRS(checkers ...TransportChecker) NetworkChecker {
|
||||
return NDP(header.ICMPv6RouterSolicit, header.NDPRSMinimumSize, checkers...)
|
||||
}
|
||||
|
||||
// NDPRSOptions creates a checker that checks that the packet contains the
|
||||
// provided NDP options within an NDP Router Solicitation message.
|
||||
//
|
||||
// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
|
||||
// containing a valid NDPRS message as far as the size is concerned.
|
||||
func NDPRSOptions(opts []header.NDPOption) TransportChecker {
|
||||
return func(t *testing.T, h header.Transport) {
|
||||
t.Helper()
|
||||
|
||||
icmp := h.(header.ICMPv6)
|
||||
rs := header.NDPRouterSolicit(icmp.NDPPayload())
|
||||
ndpOptions(t, rs.Options(), opts)
|
||||
}
|
||||
}
|
||||
|
||||
+25
-4
@@ -1220,9 +1220,15 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
}
|
||||
|
||||
ndp.rtrSolicitTimer = time.AfterFunc(delay, func() {
|
||||
// Send an RS message with the unspecified source address.
|
||||
ref := ndp.nic.getRefOrCreateTemp(header.IPv6ProtocolNumber, header.IPv6Any, NeverPrimaryEndpoint, forceSpoofing)
|
||||
r := makeRoute(header.IPv6ProtocolNumber, header.IPv6Any, header.IPv6AllRoutersMulticastAddress, ndp.nic.linkEP.LinkAddress(), ref, false, false)
|
||||
// As per RFC 4861 section 4.1, the source of the RS is an address assigned
|
||||
// to the sending interface, or the unspecified address if no address is
|
||||
// assigned to the sending interface.
|
||||
ref := ndp.nic.primaryIPv6Endpoint(header.IPv6AllRoutersMulticastAddress)
|
||||
if ref == nil {
|
||||
ref = ndp.nic.getRefOrCreateTemp(header.IPv6ProtocolNumber, header.IPv6Any, NeverPrimaryEndpoint, forceSpoofing)
|
||||
}
|
||||
localAddr := ref.ep.ID().LocalAddress
|
||||
r := makeRoute(header.IPv6ProtocolNumber, localAddr, header.IPv6AllRoutersMulticastAddress, ndp.nic.linkEP.LinkAddress(), ref, false, false)
|
||||
defer r.Release()
|
||||
|
||||
// Route should resolve immediately since
|
||||
@@ -1234,10 +1240,25 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
log.Fatalf("ndp: route resolution not immediate for route to send NDP RS (%s -> %s on NIC(%d))", header.IPv6Any, header.IPv6AllRoutersMulticastAddress, ndp.nic.ID())
|
||||
}
|
||||
|
||||
payloadSize := header.ICMPv6HeaderSize + header.NDPRSMinimumSize
|
||||
// As per RFC 4861 section 4.1, an NDP RS SHOULD include the source
|
||||
// link-layer address option if the source address of the NDP RS is
|
||||
// specified. This option MUST NOT be included if the source address is
|
||||
// unspecified.
|
||||
//
|
||||
// TODO(b/141011931): Validate a LinkEndpoint's link address (provided by
|
||||
// LinkEndpoint.LinkAddress) before reaching this point.
|
||||
var optsSerializer header.NDPOptionsSerializer
|
||||
if localAddr != header.IPv6Any && header.IsValidUnicastEthernetAddress(r.LocalLinkAddress) {
|
||||
optsSerializer = header.NDPOptionsSerializer{
|
||||
header.NDPSourceLinkLayerAddressOption(r.LocalLinkAddress),
|
||||
}
|
||||
}
|
||||
payloadSize := header.ICMPv6HeaderSize + header.NDPRSMinimumSize + int(optsSerializer.Length())
|
||||
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + payloadSize)
|
||||
pkt := header.ICMPv6(hdr.Prepend(payloadSize))
|
||||
pkt.SetType(header.ICMPv6RouterSolicit)
|
||||
rs := header.NDPRouterSolicit(pkt.NDPPayload())
|
||||
rs.Options().Serialize(optsSerializer)
|
||||
pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
|
||||
|
||||
sent := r.Stats().ICMP.V6PacketsSent
|
||||
|
||||
@@ -3384,6 +3384,10 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
linkHeaderLen uint16
|
||||
linkAddr tcpip.LinkAddress
|
||||
nicAddr tcpip.Address
|
||||
expectedSrcAddr tcpip.Address
|
||||
expectedNDPOpts []header.NDPOption
|
||||
maxRtrSolicit uint8
|
||||
rtrSolicitInt time.Duration
|
||||
effectiveRtrSolicitInt time.Duration
|
||||
@@ -3392,6 +3396,7 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "Single RS with delay",
|
||||
expectedSrcAddr: header.IPv6Any,
|
||||
maxRtrSolicit: 1,
|
||||
rtrSolicitInt: time.Second,
|
||||
effectiveRtrSolicitInt: time.Second,
|
||||
@@ -3401,6 +3406,8 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
{
|
||||
name: "Two RS with delay",
|
||||
linkHeaderLen: 1,
|
||||
nicAddr: llAddr1,
|
||||
expectedSrcAddr: llAddr1,
|
||||
maxRtrSolicit: 2,
|
||||
rtrSolicitInt: time.Second,
|
||||
effectiveRtrSolicitInt: time.Second,
|
||||
@@ -3408,8 +3415,14 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
effectiveMaxRtrSolicitDelay: 500 * time.Millisecond,
|
||||
},
|
||||
{
|
||||
name: "Single RS without delay",
|
||||
linkHeaderLen: 2,
|
||||
name: "Single RS without delay",
|
||||
linkHeaderLen: 2,
|
||||
linkAddr: linkAddr1,
|
||||
nicAddr: llAddr1,
|
||||
expectedSrcAddr: llAddr1,
|
||||
expectedNDPOpts: []header.NDPOption{
|
||||
header.NDPSourceLinkLayerAddressOption(linkAddr1),
|
||||
},
|
||||
maxRtrSolicit: 1,
|
||||
rtrSolicitInt: time.Second,
|
||||
effectiveRtrSolicitInt: time.Second,
|
||||
@@ -3419,6 +3432,8 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
{
|
||||
name: "Two RS without delay and invalid zero interval",
|
||||
linkHeaderLen: 3,
|
||||
linkAddr: linkAddr1,
|
||||
expectedSrcAddr: header.IPv6Any,
|
||||
maxRtrSolicit: 2,
|
||||
rtrSolicitInt: 0,
|
||||
effectiveRtrSolicitInt: 4 * time.Second,
|
||||
@@ -3427,6 +3442,8 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Three RS without delay",
|
||||
linkAddr: linkAddr1,
|
||||
expectedSrcAddr: header.IPv6Any,
|
||||
maxRtrSolicit: 3,
|
||||
rtrSolicitInt: 500 * time.Millisecond,
|
||||
effectiveRtrSolicitInt: 500 * time.Millisecond,
|
||||
@@ -3435,6 +3452,8 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Two RS with invalid negative delay",
|
||||
linkAddr: linkAddr1,
|
||||
expectedSrcAddr: header.IPv6Any,
|
||||
maxRtrSolicit: 2,
|
||||
rtrSolicitInt: time.Second,
|
||||
effectiveRtrSolicitInt: time.Second,
|
||||
@@ -3457,7 +3476,7 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
e := channelLinkWithHeaderLength{
|
||||
Endpoint: channel.New(int(test.maxRtrSolicit), 1280, linkAddr1),
|
||||
Endpoint: channel.New(int(test.maxRtrSolicit), 1280, test.linkAddr),
|
||||
headerLength: test.linkHeaderLen,
|
||||
}
|
||||
e.Endpoint.LinkEPCapabilities |= stack.CapabilityResolutionRequired
|
||||
@@ -3481,10 +3500,10 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
|
||||
checker.IPv6(t,
|
||||
p.Pkt.Header.View(),
|
||||
checker.SrcAddr(header.IPv6Any),
|
||||
checker.SrcAddr(test.expectedSrcAddr),
|
||||
checker.DstAddr(header.IPv6AllRoutersMulticastAddress),
|
||||
checker.TTL(header.NDPHopLimit),
|
||||
checker.NDPRS(),
|
||||
checker.NDPRS(checker.NDPRSOptions(test.expectedNDPOpts)),
|
||||
)
|
||||
|
||||
if l, want := p.Pkt.Header.AvailableLength(), int(test.linkHeaderLen); l != want {
|
||||
@@ -3510,13 +3529,19 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
}
|
||||
|
||||
// Make sure each RS got sent at the right
|
||||
// times.
|
||||
if addr := test.nicAddr; addr != "" {
|
||||
if err := s.AddAddress(nicID, header.IPv6ProtocolNumber, addr); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, header.IPv6ProtocolNumber, addr, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure each RS is sent at the right time.
|
||||
remaining := test.maxRtrSolicit
|
||||
if remaining > 0 {
|
||||
waitForPkt(test.effectiveMaxRtrSolicitDelay + defaultAsyncEventTimeout)
|
||||
remaining--
|
||||
}
|
||||
|
||||
for ; remaining > 0; remaining-- {
|
||||
waitForNothing(test.effectiveRtrSolicitInt - defaultTimeout)
|
||||
waitForPkt(defaultAsyncEventTimeout)
|
||||
|
||||
Reference in New Issue
Block a user