// Copyright 2019 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stack_test import ( "testing" "time" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/buffer" "gvisor.dev/gvisor/pkg/tcpip/checker" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/link/channel" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" ) const ( addr1 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" addr2 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02" linkAddr1 = "\x02\x02\x03\x04\x05\x06" ) // TestDADDisabled tests that an address successfully resolves immediately // when DAD is not enabled (the default for an empty stack.Options). func TestDADDisabled(t *testing.T) { opts := stack.Options{ NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()}, } e := channel.New(10, 1280, linkAddr1) s := stack.New(opts) if err := s.CreateNIC(1, e); err != nil { t.Fatalf("CreateNIC(_) = %s", err) } if err := s.AddAddress(1, header.IPv6ProtocolNumber, addr1); err != nil { t.Fatalf("AddAddress(_, %d, %s) = %s", header.IPv6ProtocolNumber, addr1, err) } // Should get the address immediately since we should not have performed // DAD on it. addr, err := s.GetMainNICAddress(1, header.IPv6ProtocolNumber) if err != nil { t.Fatalf("stack.GetMainNICAddress(_, _) err = %s", err) } if addr.Address != addr1 { t.Fatalf("got stack.GetMainNICAddress(_, _) = %s, want = %s", addr, addr1) } // We should not have sent any NDP NS messages. if got := s.Stats().ICMP.V6PacketsSent.NeighborSolicit.Value(); got != 0 { t.Fatalf("got NeighborSolicit = %d, want = 0", got) } } // TestDADResolve tests that an address successfully resolves after performing // DAD for various values of DupAddrDetectTransmits and RetransmitTimer. // Included in the subtests is a test to make sure that an invalid // RetransmitTimer (<1ms) values get fixed to the default RetransmitTimer of 1s. func TestDADResolve(t *testing.T) { tests := []struct { name string dupAddrDetectTransmits uint8 retransTimer time.Duration expectedRetransmitTimer time.Duration }{ {"1:1s:1s", 1, time.Second, time.Second}, {"2:1s:1s", 2, time.Second, time.Second}, {"1:2s:2s", 1, 2 * time.Second, 2 * time.Second}, // 0s is an invalid RetransmitTimer timer and will be fixed to // the default RetransmitTimer value of 1s. {"1:0s:1s", 1, 0, time.Second}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { opts := stack.Options{ NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()}, } opts.NDPConfigs.RetransmitTimer = test.retransTimer opts.NDPConfigs.DupAddrDetectTransmits = test.dupAddrDetectTransmits e := channel.New(10, 1280, linkAddr1) s := stack.New(opts) if err := s.CreateNIC(1, e); err != nil { t.Fatalf("CreateNIC(_) = %s", err) } if err := s.AddAddress(1, header.IPv6ProtocolNumber, addr1); err != nil { t.Fatalf("AddAddress(_, %d, %s) = %s", header.IPv6ProtocolNumber, addr1, err) } stat := s.Stats().ICMP.V6PacketsSent.NeighborSolicit // Should have sent an NDP NS almost immediately. time.Sleep(100 * time.Millisecond) if got := stat.Value(); got != 1 { t.Fatalf("got NeighborSolicit = %d, want = 1", got) } // Address should not be considered bound to the NIC yet // (DAD ongoing). addr, err := s.GetMainNICAddress(1, header.IPv6ProtocolNumber) if err != nil { t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err) } if want := (tcpip.AddressWithPrefix{}); addr != want { t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", addr, want) } // Wait for the remaining time - 500ms, to make sure // the address is still not resolved. Note, we subtract // 600ms because we already waited for 100ms earlier, // so our remaining time is 100ms less than the expected // time. // (X - 100ms) - 500ms = X - 600ms // // TODO(b/140896005): Use events from the netstack to // be signalled before DAD resolves. time.Sleep(test.expectedRetransmitTimer*time.Duration(test.dupAddrDetectTransmits) - 600*time.Millisecond) addr, err = s.GetMainNICAddress(1, header.IPv6ProtocolNumber) if err != nil { t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err) } if want := (tcpip.AddressWithPrefix{}); addr != want { t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", addr, want) } // Wait for the remaining time + 250ms, at which point // the address should be resolved. Note, the remaining // time is 500ms. See above comments. // // TODO(b/140896005): Use events from the netstack to // know immediately when DAD completes. time.Sleep(750 * time.Millisecond) addr, err = s.GetMainNICAddress(1, header.IPv6ProtocolNumber) if err != nil { t.Fatalf("stack.GetMainNICAddress(_, _) err = %s", err) } if addr.Address != addr1 { t.Fatalf("got stack.GetMainNICAddress(_, _) = %s, want = %s", addr, addr1) } // Should not have sent any more NS messages. if got := stat.Value(); got != uint64(test.dupAddrDetectTransmits) { t.Fatalf("got NeighborSolicit = %d, want = %d", got, test.dupAddrDetectTransmits) } // Validate the sent Neighbor Solicitation messages. for i := uint8(0); i < test.dupAddrDetectTransmits; i++ { p := <-e.C // Make sure its an IPv6 packet. if p.Proto != header.IPv6ProtocolNumber { t.Fatalf("got Proto = %d, want = %d", p.Proto, header.IPv6ProtocolNumber) } // Check NDP packet. checker.IPv6(t, p.Header.ToVectorisedView().First(), checker.TTL(header.NDPHopLimit), checker.NDPNS( checker.NDPNSTargetAddress(addr1))) } }) } } // TestDADFail tests to make sure that the DAD process fails if another node is // detected to be performing DAD on the same address (receive an NS message from // a node doing DAD for the same address), or if another node is detected to own // the address already (receive an NA message for the tentative address). func TestDADFail(t *testing.T) { tests := []struct { name string makeBuf func(tgt tcpip.Address) buffer.Prependable getStat func(s tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter }{ { "RxSolicit", func(tgt tcpip.Address) buffer.Prependable { hdr := buffer.NewPrependable(header.IPv6MinimumSize + header.ICMPv6NeighborSolicitMinimumSize) pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborSolicitMinimumSize)) pkt.SetType(header.ICMPv6NeighborSolicit) ns := header.NDPNeighborSolicit(pkt.NDPPayload()) ns.SetTargetAddress(tgt) snmc := header.SolicitedNodeAddr(tgt) pkt.SetChecksum(header.ICMPv6Checksum(pkt, header.IPv6Any, snmc, buffer.VectorisedView{})) payloadLength := hdr.UsedLength() ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) ip.Encode(&header.IPv6Fields{ PayloadLength: uint16(payloadLength), NextHeader: uint8(icmp.ProtocolNumber6), HopLimit: 255, SrcAddr: header.IPv6Any, DstAddr: snmc, }) return hdr }, func(s tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter { return s.NeighborSolicit }, }, { "RxAdvert", func(tgt tcpip.Address) buffer.Prependable { hdr := buffer.NewPrependable(header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize) pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize)) pkt.SetType(header.ICMPv6NeighborAdvert) na := header.NDPNeighborAdvert(pkt.NDPPayload()) na.SetSolicitedFlag(true) na.SetOverrideFlag(true) na.SetTargetAddress(tgt) pkt.SetChecksum(header.ICMPv6Checksum(pkt, tgt, header.IPv6AllNodesMulticastAddress, buffer.VectorisedView{})) payloadLength := hdr.UsedLength() ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) ip.Encode(&header.IPv6Fields{ PayloadLength: uint16(payloadLength), NextHeader: uint8(icmp.ProtocolNumber6), HopLimit: 255, SrcAddr: tgt, DstAddr: header.IPv6AllNodesMulticastAddress, }) return hdr }, func(s tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter { return s.NeighborAdvert }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { opts := stack.Options{ NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()}, NDPConfigs: stack.DefaultNDPConfigurations(), } opts.NDPConfigs.RetransmitTimer = time.Second * 2 e := channel.New(10, 1280, linkAddr1) s := stack.New(opts) if err := s.CreateNIC(1, e); err != nil { t.Fatalf("CreateNIC(_) = %s", err) } if err := s.AddAddress(1, header.IPv6ProtocolNumber, addr1); err != nil { t.Fatalf("AddAddress(_, %d, %s) = %s", header.IPv6ProtocolNumber, addr1, err) } // Address should not be considered bound to the NIC yet // (DAD ongoing). addr, err := s.GetMainNICAddress(1, header.IPv6ProtocolNumber) if err != nil { t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err) } if want := (tcpip.AddressWithPrefix{}); addr != want { t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", addr, want) } // Receive a packet to simulate multiple nodes owning or // attempting to own the same address. hdr := test.makeBuf(addr1) e.Inject(header.IPv6ProtocolNumber, hdr.View().ToVectorisedView()) stat := test.getStat(s.Stats().ICMP.V6PacketsReceived) if got := stat.Value(); got != 1 { t.Fatalf("got stat = %d, want = 1", got) } // Wait 3 seconds to make sure that DAD did not resolve time.Sleep(3 * time.Second) addr, err = s.GetMainNICAddress(1, header.IPv6ProtocolNumber) if err != nil { t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err) } if want := (tcpip.AddressWithPrefix{}); addr != want { t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", addr, want) } }) } } // TestDADStop tests to make sure that the DAD process stops when an address is // removed. func TestDADStop(t *testing.T) { opts := stack.Options{ NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()}, } opts.NDPConfigs.RetransmitTimer = time.Second opts.NDPConfigs.DupAddrDetectTransmits = 2 e := channel.New(10, 1280, linkAddr1) s := stack.New(opts) if err := s.CreateNIC(1, e); err != nil { t.Fatalf("CreateNIC(_) = %s", err) } if err := s.AddAddress(1, header.IPv6ProtocolNumber, addr1); err != nil { t.Fatalf("AddAddress(_, %d, %s) = %s", header.IPv6ProtocolNumber, addr1, err) } // Address should not be considered bound to the NIC yet (DAD ongoing). addr, err := s.GetMainNICAddress(1, header.IPv6ProtocolNumber) if err != nil { t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err) } if want := (tcpip.AddressWithPrefix{}); addr != want { t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", addr, want) } // Remove the address. This should stop DAD. if err := s.RemoveAddress(1, addr1); err != nil { t.Fatalf("RemoveAddress(_, %s) = %s", addr1, err) } // Wait for the time to normally resolve // DupAddrDetectTransmits(2) * RetransmitTimer(1s) = 2s. // An extra 250ms is added to make sure that if DAD was still running // it resolves and the check below fails. time.Sleep(2*time.Second + 250*time.Millisecond) addr, err = s.GetMainNICAddress(1, header.IPv6ProtocolNumber) if err != nil { t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err) } if want := (tcpip.AddressWithPrefix{}); addr != want { t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", addr, want) } // Should not have sent more than 1 NS message. if got := s.Stats().ICMP.V6PacketsSent.NeighborSolicit.Value(); got > 1 { t.Fatalf("got NeighborSolicit = %d, want <= 1", got) } }