github.com/FlowerWrong/netstack@v0.0.0-20191009141956-e5848263af28/tcpip/network/ipv6/ndp_test.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ipv6
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/FlowerWrong/netstack/tcpip"
    22  	"github.com/FlowerWrong/netstack/tcpip/buffer"
    23  	"github.com/FlowerWrong/netstack/tcpip/header"
    24  	"github.com/FlowerWrong/netstack/tcpip/stack"
    25  	"github.com/FlowerWrong/netstack/tcpip/transport/icmp"
    26  )
    27  
    28  // setupStackAndEndpoint creates a stack with a single NIC with a link-local
    29  // address llladdr and an IPv6 endpoint to a remote with link-local address
    30  // rlladdr
    31  func setupStackAndEndpoint(t *testing.T, llladdr, rlladdr tcpip.Address) (*stack.Stack, stack.NetworkEndpoint) {
    32  	t.Helper()
    33  
    34  	s := stack.New(stack.Options{
    35  		NetworkProtocols:   []stack.NetworkProtocol{NewProtocol()},
    36  		TransportProtocols: []stack.TransportProtocol{icmp.NewProtocol6()},
    37  	})
    38  
    39  	if err := s.CreateNIC(1, &stubLinkEndpoint{}); err != nil {
    40  		t.Fatalf("CreateNIC(_) = %s", err)
    41  	}
    42  	if err := s.AddAddress(1, ProtocolNumber, llladdr); err != nil {
    43  		t.Fatalf("AddAddress(_, %d, %s) = %s", ProtocolNumber, llladdr, err)
    44  	}
    45  
    46  	{
    47  		subnet, err := tcpip.NewSubnet(rlladdr, tcpip.AddressMask(strings.Repeat("\xff", len(rlladdr))))
    48  		if err != nil {
    49  			t.Fatal(err)
    50  		}
    51  		s.SetRouteTable(
    52  			[]tcpip.Route{{
    53  				Destination: subnet,
    54  				NIC:         1,
    55  			}},
    56  		)
    57  	}
    58  
    59  	netProto := s.NetworkProtocolInstance(ProtocolNumber)
    60  	if netProto == nil {
    61  		t.Fatalf("cannot find protocol instance for network protocol %d", ProtocolNumber)
    62  	}
    63  
    64  	ep, err := netProto.NewEndpoint(0, tcpip.AddressWithPrefix{rlladdr, netProto.DefaultPrefixLen()}, &stubLinkAddressCache{}, &stubDispatcher{}, nil)
    65  	if err != nil {
    66  		t.Fatalf("NewEndpoint(_) = _, %s, want = _, nil", err)
    67  	}
    68  
    69  	return s, ep
    70  }
    71  
    72  // TestHopLimitValidation is a test that makes sure that NDP packets are only
    73  // received if their IP header's hop limit is set to 255.
    74  func TestHopLimitValidation(t *testing.T) {
    75  	setup := func(t *testing.T) (*stack.Stack, stack.NetworkEndpoint, stack.Route) {
    76  		t.Helper()
    77  
    78  		// Create a stack with the assigned link-local address lladdr0
    79  		// and an endpoint to lladdr1.
    80  		s, ep := setupStackAndEndpoint(t, lladdr0, lladdr1)
    81  
    82  		r, err := s.FindRoute(1, lladdr0, lladdr1, ProtocolNumber, false /* multicastLoop */)
    83  		if err != nil {
    84  			t.Fatalf("FindRoute(_) = _, %s, want = _, nil", err)
    85  		}
    86  
    87  		return s, ep, r
    88  	}
    89  
    90  	handleIPv6Payload := func(hdr buffer.Prependable, hopLimit uint8, ep stack.NetworkEndpoint, r *stack.Route) {
    91  		payloadLength := hdr.UsedLength()
    92  		ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
    93  		ip.Encode(&header.IPv6Fields{
    94  			PayloadLength: uint16(payloadLength),
    95  			NextHeader:    uint8(header.ICMPv6ProtocolNumber),
    96  			HopLimit:      hopLimit,
    97  			SrcAddr:       r.LocalAddress,
    98  			DstAddr:       r.RemoteAddress,
    99  		})
   100  		ep.HandlePacket(r, hdr.View().ToVectorisedView())
   101  	}
   102  
   103  	types := []struct {
   104  		name        string
   105  		typ         header.ICMPv6Type
   106  		size        int
   107  		statCounter func(tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter
   108  	}{
   109  		{"RouterSolicit", header.ICMPv6RouterSolicit, header.ICMPv6MinimumSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   110  			return stats.RouterSolicit
   111  		}},
   112  		{"RouterAdvert", header.ICMPv6RouterAdvert, header.ICMPv6MinimumSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   113  			return stats.RouterAdvert
   114  		}},
   115  		{"NeighborSolicit", header.ICMPv6NeighborSolicit, header.ICMPv6NeighborSolicitMinimumSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   116  			return stats.NeighborSolicit
   117  		}},
   118  		{"NeighborAdvert", header.ICMPv6NeighborAdvert, header.ICMPv6NeighborAdvertSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   119  			return stats.NeighborAdvert
   120  		}},
   121  		{"RedirectMsg", header.ICMPv6RedirectMsg, header.ICMPv6MinimumSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   122  			return stats.RedirectMsg
   123  		}},
   124  	}
   125  
   126  	for _, typ := range types {
   127  		t.Run(typ.name, func(t *testing.T) {
   128  			s, ep, r := setup(t)
   129  			defer r.Release()
   130  
   131  			stats := s.Stats().ICMP.V6PacketsReceived
   132  			invalid := stats.Invalid
   133  			typStat := typ.statCounter(stats)
   134  
   135  			hdr := buffer.NewPrependable(header.IPv6MinimumSize + typ.size)
   136  			pkt := header.ICMPv6(hdr.Prepend(typ.size))
   137  			pkt.SetType(typ.typ)
   138  			pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
   139  
   140  			// Invalid count should initially be 0.
   141  			if got := invalid.Value(); got != 0 {
   142  				t.Fatalf("got invalid = %d, want = 0", got)
   143  			}
   144  
   145  			// Should not have received any ICMPv6 packets with
   146  			// type = typ.typ.
   147  			if got := typStat.Value(); got != 0 {
   148  				t.Fatalf("got %s = %d, want = 0", typ.name, got)
   149  			}
   150  
   151  			// Receive the NDP packet with an invalid hop limit
   152  			// value.
   153  			handleIPv6Payload(hdr, ndpHopLimit-1, ep, &r)
   154  
   155  			// Invalid count should have increased.
   156  			if got := invalid.Value(); got != 1 {
   157  				t.Fatalf("got invalid = %d, want = 1", got)
   158  			}
   159  
   160  			// Rx count of NDP packet of type typ.typ should not
   161  			// have increased.
   162  			if got := typStat.Value(); got != 0 {
   163  				t.Fatalf("got %s = %d, want = 0", typ.name, got)
   164  			}
   165  
   166  			// Receive the NDP packet with a valid hop limit value.
   167  			handleIPv6Payload(hdr, ndpHopLimit, ep, &r)
   168  
   169  			// Rx count of NDP packet of type typ.typ should have
   170  			// increased.
   171  			if got := typStat.Value(); got != 1 {
   172  				t.Fatalf("got %s = %d, want = 1", typ.name, got)
   173  			}
   174  
   175  			// Invalid count should not have increased again.
   176  			if got := invalid.Value(); got != 1 {
   177  				t.Fatalf("got invalid = %d, want = 1", got)
   178  			}
   179  		})
   180  	}
   181  }