github.com/polevpn/netstack@v1.10.9/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/polevpn/netstack/tcpip"
    22  	"github.com/polevpn/netstack/tcpip/buffer"
    23  	"github.com/polevpn/netstack/tcpip/header"
    24  	"github.com/polevpn/netstack/tcpip/link/channel"
    25  	"github.com/polevpn/netstack/tcpip/stack"
    26  	"github.com/polevpn/netstack/tcpip/transport/icmp"
    27  )
    28  
    29  // setupStackAndEndpoint creates a stack with a single NIC with a link-local
    30  // address llladdr and an IPv6 endpoint to a remote with link-local address
    31  // rlladdr
    32  func setupStackAndEndpoint(t *testing.T, llladdr, rlladdr tcpip.Address) (*stack.Stack, stack.NetworkEndpoint) {
    33  	t.Helper()
    34  
    35  	s := stack.New(stack.Options{
    36  		NetworkProtocols:   []stack.NetworkProtocol{NewProtocol()},
    37  		TransportProtocols: []stack.TransportProtocol{icmp.NewProtocol6()},
    38  	})
    39  
    40  	if err := s.CreateNIC(1, &stubLinkEndpoint{}); err != nil {
    41  		t.Fatalf("CreateNIC(_) = %s", err)
    42  	}
    43  	if err := s.AddAddress(1, ProtocolNumber, llladdr); err != nil {
    44  		t.Fatalf("AddAddress(_, %d, %s) = %s", ProtocolNumber, llladdr, err)
    45  	}
    46  
    47  	{
    48  		subnet, err := tcpip.NewSubnet(rlladdr, tcpip.AddressMask(strings.Repeat("\xff", len(rlladdr))))
    49  		if err != nil {
    50  			t.Fatal(err)
    51  		}
    52  		s.SetRouteTable(
    53  			[]tcpip.Route{{
    54  				Destination: subnet,
    55  				NIC:         1,
    56  			}},
    57  		)
    58  	}
    59  
    60  	netProto := s.NetworkProtocolInstance(ProtocolNumber)
    61  	if netProto == nil {
    62  		t.Fatalf("cannot find protocol instance for network protocol %d", ProtocolNumber)
    63  	}
    64  
    65  	ep, err := netProto.NewEndpoint(0, tcpip.AddressWithPrefix{rlladdr, netProto.DefaultPrefixLen()}, &stubLinkAddressCache{}, &stubDispatcher{}, nil)
    66  	if err != nil {
    67  		t.Fatalf("NewEndpoint(_) = _, %s, want = _, nil", err)
    68  	}
    69  
    70  	return s, ep
    71  }
    72  
    73  // TestHopLimitValidation is a test that makes sure that NDP packets are only
    74  // received if their IP header's hop limit is set to 255.
    75  func TestHopLimitValidation(t *testing.T) {
    76  	setup := func(t *testing.T) (*stack.Stack, stack.NetworkEndpoint, stack.Route) {
    77  		t.Helper()
    78  
    79  		// Create a stack with the assigned link-local address lladdr0
    80  		// and an endpoint to lladdr1.
    81  		s, ep := setupStackAndEndpoint(t, lladdr0, lladdr1)
    82  
    83  		r, err := s.FindRoute(1, lladdr0, lladdr1, ProtocolNumber, false /* multicastLoop */)
    84  		if err != nil {
    85  			t.Fatalf("FindRoute(_) = _, %s, want = _, nil", err)
    86  		}
    87  
    88  		return s, ep, r
    89  	}
    90  
    91  	handleIPv6Payload := func(hdr buffer.Prependable, hopLimit uint8, ep stack.NetworkEndpoint, r *stack.Route) {
    92  		payloadLength := hdr.UsedLength()
    93  		ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
    94  		ip.Encode(&header.IPv6Fields{
    95  			PayloadLength: uint16(payloadLength),
    96  			NextHeader:    uint8(header.ICMPv6ProtocolNumber),
    97  			HopLimit:      hopLimit,
    98  			SrcAddr:       r.LocalAddress,
    99  			DstAddr:       r.RemoteAddress,
   100  		})
   101  		ep.HandlePacket(r, tcpip.PacketBuffer{
   102  			Data: hdr.View().ToVectorisedView(),
   103  		})
   104  	}
   105  
   106  	types := []struct {
   107  		name        string
   108  		typ         header.ICMPv6Type
   109  		size        int
   110  		statCounter func(tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter
   111  	}{
   112  		{"RouterSolicit", header.ICMPv6RouterSolicit, header.ICMPv6MinimumSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   113  			return stats.RouterSolicit
   114  		}},
   115  		{"RouterAdvert", header.ICMPv6RouterAdvert, header.ICMPv6HeaderSize + header.NDPRAMinimumSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   116  			return stats.RouterAdvert
   117  		}},
   118  		{"NeighborSolicit", header.ICMPv6NeighborSolicit, header.ICMPv6NeighborSolicitMinimumSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   119  			return stats.NeighborSolicit
   120  		}},
   121  		{"NeighborAdvert", header.ICMPv6NeighborAdvert, header.ICMPv6NeighborAdvertSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   122  			return stats.NeighborAdvert
   123  		}},
   124  		{"RedirectMsg", header.ICMPv6RedirectMsg, header.ICMPv6MinimumSize, func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   125  			return stats.RedirectMsg
   126  		}},
   127  	}
   128  
   129  	for _, typ := range types {
   130  		t.Run(typ.name, func(t *testing.T) {
   131  			s, ep, r := setup(t)
   132  			defer r.Release()
   133  
   134  			stats := s.Stats().ICMP.V6PacketsReceived
   135  			invalid := stats.Invalid
   136  			typStat := typ.statCounter(stats)
   137  
   138  			hdr := buffer.NewPrependable(header.IPv6MinimumSize + typ.size)
   139  			pkt := header.ICMPv6(hdr.Prepend(typ.size))
   140  			pkt.SetType(typ.typ)
   141  			pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
   142  
   143  			// Invalid count should initially be 0.
   144  			if got := invalid.Value(); got != 0 {
   145  				t.Fatalf("got invalid = %d, want = 0", got)
   146  			}
   147  
   148  			// Should not have received any ICMPv6 packets with
   149  			// type = typ.typ.
   150  			if got := typStat.Value(); got != 0 {
   151  				t.Fatalf("got %s = %d, want = 0", typ.name, got)
   152  			}
   153  
   154  			// Receive the NDP packet with an invalid hop limit
   155  			// value.
   156  			handleIPv6Payload(hdr, header.NDPHopLimit-1, ep, &r)
   157  
   158  			// Invalid count should have increased.
   159  			if got := invalid.Value(); got != 1 {
   160  				t.Fatalf("got invalid = %d, want = 1", got)
   161  			}
   162  
   163  			// Rx count of NDP packet of type typ.typ should not
   164  			// have increased.
   165  			if got := typStat.Value(); got != 0 {
   166  				t.Fatalf("got %s = %d, want = 0", typ.name, got)
   167  			}
   168  
   169  			// Receive the NDP packet with a valid hop limit value.
   170  			handleIPv6Payload(hdr, header.NDPHopLimit, ep, &r)
   171  
   172  			// Rx count of NDP packet of type typ.typ should have
   173  			// increased.
   174  			if got := typStat.Value(); got != 1 {
   175  				t.Fatalf("got %s = %d, want = 1", typ.name, got)
   176  			}
   177  
   178  			// Invalid count should not have increased again.
   179  			if got := invalid.Value(); got != 1 {
   180  				t.Fatalf("got invalid = %d, want = 1", got)
   181  			}
   182  		})
   183  	}
   184  }
   185  
   186  // TestRouterAdvertValidation tests that when the NIC is configured to handle
   187  // NDP Router Advertisement packets, it validates the Router Advertisement
   188  // properly before handling them.
   189  func TestRouterAdvertValidation(t *testing.T) {
   190  	tests := []struct {
   191  		name            string
   192  		src             tcpip.Address
   193  		hopLimit        uint8
   194  		code            uint8
   195  		ndpPayload      []byte
   196  		expectedSuccess bool
   197  	}{
   198  		{
   199  			"OK",
   200  			lladdr0,
   201  			255,
   202  			0,
   203  			[]byte{
   204  				0, 0, 0, 0,
   205  				0, 0, 0, 0,
   206  				0, 0, 0, 0,
   207  			},
   208  			true,
   209  		},
   210  		{
   211  			"NonLinkLocalSourceAddr",
   212  			addr1,
   213  			255,
   214  			0,
   215  			[]byte{
   216  				0, 0, 0, 0,
   217  				0, 0, 0, 0,
   218  				0, 0, 0, 0,
   219  			},
   220  			false,
   221  		},
   222  		{
   223  			"HopLimitNot255",
   224  			lladdr0,
   225  			254,
   226  			0,
   227  			[]byte{
   228  				0, 0, 0, 0,
   229  				0, 0, 0, 0,
   230  				0, 0, 0, 0,
   231  			},
   232  			false,
   233  		},
   234  		{
   235  			"NonZeroCode",
   236  			lladdr0,
   237  			255,
   238  			1,
   239  			[]byte{
   240  				0, 0, 0, 0,
   241  				0, 0, 0, 0,
   242  				0, 0, 0, 0,
   243  			},
   244  			false,
   245  		},
   246  		{
   247  			"NDPPayloadTooSmall",
   248  			lladdr0,
   249  			255,
   250  			0,
   251  			[]byte{
   252  				0, 0, 0, 0,
   253  				0, 0, 0, 0,
   254  				0, 0, 0,
   255  			},
   256  			false,
   257  		},
   258  		{
   259  			"OKWithOptions",
   260  			lladdr0,
   261  			255,
   262  			0,
   263  			[]byte{
   264  				// RA payload
   265  				0, 0, 0, 0,
   266  				0, 0, 0, 0,
   267  				0, 0, 0, 0,
   268  
   269  				// Option #1 (TargetLinkLayerAddress)
   270  				2, 1, 0, 0, 0, 0, 0, 0,
   271  
   272  				// Option #2 (unrecognized)
   273  				255, 1, 0, 0, 0, 0, 0, 0,
   274  
   275  				// Option #3 (PrefixInformation)
   276  				3, 4, 0, 0, 0, 0, 0, 0,
   277  				0, 0, 0, 0, 0, 0, 0, 0,
   278  				0, 0, 0, 0, 0, 0, 0, 0,
   279  				0, 0, 0, 0, 0, 0, 0, 0,
   280  			},
   281  			true,
   282  		},
   283  		{
   284  			"OptionWithZeroLength",
   285  			lladdr0,
   286  			255,
   287  			0,
   288  			[]byte{
   289  				// RA payload
   290  				0, 0, 0, 0,
   291  				0, 0, 0, 0,
   292  				0, 0, 0, 0,
   293  
   294  				// Option #1 (TargetLinkLayerAddress)
   295  				// Invalid as it has 0 length.
   296  				2, 0, 0, 0, 0, 0, 0, 0,
   297  
   298  				// Option #2 (unrecognized)
   299  				255, 1, 0, 0, 0, 0, 0, 0,
   300  
   301  				// Option #3 (PrefixInformation)
   302  				3, 4, 0, 0, 0, 0, 0, 0,
   303  				0, 0, 0, 0, 0, 0, 0, 0,
   304  				0, 0, 0, 0, 0, 0, 0, 0,
   305  				0, 0, 0, 0, 0, 0, 0, 0,
   306  			},
   307  			false,
   308  		},
   309  	}
   310  
   311  	for _, test := range tests {
   312  		t.Run(test.name, func(t *testing.T) {
   313  			e := channel.New(10, 1280, linkAddr1)
   314  			s := stack.New(stack.Options{
   315  				NetworkProtocols: []stack.NetworkProtocol{NewProtocol()},
   316  			})
   317  
   318  			if err := s.CreateNIC(1, e); err != nil {
   319  				t.Fatalf("CreateNIC(_) = %s", err)
   320  			}
   321  
   322  			icmpSize := header.ICMPv6HeaderSize + len(test.ndpPayload)
   323  			hdr := buffer.NewPrependable(header.IPv6MinimumSize + icmpSize)
   324  			pkt := header.ICMPv6(hdr.Prepend(icmpSize))
   325  			pkt.SetType(header.ICMPv6RouterAdvert)
   326  			pkt.SetCode(test.code)
   327  			copy(pkt.NDPPayload(), test.ndpPayload)
   328  			payloadLength := hdr.UsedLength()
   329  			pkt.SetChecksum(header.ICMPv6Checksum(pkt, test.src, header.IPv6AllNodesMulticastAddress, buffer.VectorisedView{}))
   330  			ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
   331  			ip.Encode(&header.IPv6Fields{
   332  				PayloadLength: uint16(payloadLength),
   333  				NextHeader:    uint8(icmp.ProtocolNumber6),
   334  				HopLimit:      test.hopLimit,
   335  				SrcAddr:       test.src,
   336  				DstAddr:       header.IPv6AllNodesMulticastAddress,
   337  			})
   338  
   339  			stats := s.Stats().ICMP.V6PacketsReceived
   340  			invalid := stats.Invalid
   341  			rxRA := stats.RouterAdvert
   342  
   343  			if got := invalid.Value(); got != 0 {
   344  				t.Fatalf("got invalid = %d, want = 0", got)
   345  			}
   346  			if got := rxRA.Value(); got != 0 {
   347  				t.Fatalf("got rxRA = %d, want = 0", got)
   348  			}
   349  
   350  			e.InjectInbound(header.IPv6ProtocolNumber, tcpip.PacketBuffer{
   351  				Data: hdr.View().ToVectorisedView(),
   352  			})
   353  
   354  			if test.expectedSuccess {
   355  				if got := invalid.Value(); got != 0 {
   356  					t.Fatalf("got invalid = %d, want = 0", got)
   357  				}
   358  				if got := rxRA.Value(); got != 1 {
   359  					t.Fatalf("got rxRA = %d, want = 1", got)
   360  				}
   361  
   362  			} else {
   363  				if got := invalid.Value(); got != 1 {
   364  					t.Fatalf("got invalid = %d, want = 1", got)
   365  				}
   366  				if got := rxRA.Value(); got != 0 {
   367  					t.Fatalf("got rxRA = %d, want = 0", got)
   368  				}
   369  			}
   370  		})
   371  	}
   372  }