github.com/vpnishe/netstack@v1.10.6/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/vpnishe/netstack/tcpip" 22 "github.com/vpnishe/netstack/tcpip/buffer" 23 "github.com/vpnishe/netstack/tcpip/header" 24 "github.com/vpnishe/netstack/tcpip/link/channel" 25 "github.com/vpnishe/netstack/tcpip/stack" 26 "github.com/vpnishe/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 }