From e491ebbacf548a2a2f818f1093b09d6f1c13e7e0 Mon Sep 17 00:00:00 2001 From: Chris Kuiper Date: Sun, 16 Dec 2018 23:04:56 -0800 Subject: Allow sending of multicast and IPv6 link-local packets w/o route. Same as with broadcast packets, sending of a multicast packet shouldn't require accessing the route table. The same applies to IPv6 link-local addresses, which aren't routable at all (they don't belong to any subnet by definition). PiperOrigin-RevId: 225775870 Change-Id: Ic53e6560c125a83be2be9c3d112e66b36e8dfe7b --- pkg/tcpip/header/ipv6.go | 12 ++++++ pkg/tcpip/stack/stack.go | 7 +++- pkg/tcpip/stack/stack_test.go | 86 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) (limited to 'pkg/tcpip') diff --git a/pkg/tcpip/header/ipv6.go b/pkg/tcpip/header/ipv6.go index d985b745d..3d24736c7 100644 --- a/pkg/tcpip/header/ipv6.go +++ b/pkg/tcpip/header/ipv6.go @@ -77,6 +77,9 @@ const ( // IPv6MinimumMTU is the minimum MTU required by IPv6, per RFC 2460, // section 5. IPv6MinimumMTU = 1280 + + // IPv6Any is the non-routable IPv6 "any" meta address. + IPv6Any tcpip.Address = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) // PayloadLength returns the value of the "payload length" field of the ipv6 @@ -234,3 +237,12 @@ func LinkLocalAddr(linkAddr tcpip.LinkAddress) tcpip.Address { } return tcpip.Address(lladdrb[:]) } + +// IsV6LinkLocalAddress determines if the provided address is an IPv6 +// link-local address (fe80::/10). +func IsV6LinkLocalAddress(addr tcpip.Address) bool { + if len(addr) != IPv6AddressSize { + return false + } + return addr[0] == 0xfe && (addr[1]&0xc0) == 0x80 +} diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index d39878a88..0ac116675 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -729,8 +729,11 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n s.mu.RLock() defer s.mu.RUnlock() - // We don't require a route in the table to send a broadcast out on a NIC. - if id != 0 && remoteAddr == header.IPv4Broadcast { + // We don't require a route in the table to send a broadcast, multicast or + // IPv6 link-local packet out on a NIC. + isBroadcast := remoteAddr == header.IPv4Broadcast + isMulticast := header.IsV4MulticastAddress(remoteAddr) || header.IsV6MulticastAddress(remoteAddr) + if id != 0 && (isBroadcast || isMulticast || header.IsV6LinkLocalAddress(remoteAddr)) { if nic, ok := s.nics[id]; ok { if ref := s.getRefEP(nic, localAddr, netProto); ref != nil { return makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, nic.linkEP.LinkAddress(), ref), nil diff --git a/pkg/tcpip/stack/stack_test.go b/pkg/tcpip/stack/stack_test.go index dc05517ba..391319f35 100644 --- a/pkg/tcpip/stack/stack_test.go +++ b/pkg/tcpip/stack/stack_test.go @@ -680,6 +680,92 @@ func TestBroadcastNeedsNoRoute(t *testing.T) { } } +func TestMulticastOrIPv6LinkLocalNeedsNoRoute(t *testing.T) { + for _, tc := range []struct { + name string + routeNeeded bool + address tcpip.Address + }{ + // IPv4 multicast address range: 224.0.0.0 - 239.255.255.255 + // <=> 0xe0.0x00.0x00.0x00 - 0xef.0xff.0xff.0xff + {"IPv4 Multicast 1", false, "\xe0\x00\x00\x00"}, + {"IPv4 Multicast 2", false, "\xef\xff\xff\xff"}, + {"IPv4 Unicast 1", true, "\xdf\xff\xff\xff"}, + {"IPv4 Unicast 2", true, "\xf0\x00\x00\x00"}, + {"IPv4 Unicast 3", true, "\x00\x00\x00\x00"}, + + // IPv6 multicast address is 0xff[8] + flags[4] + scope[4] + groupId[112] + {"IPv6 Multicast 1", false, "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Multicast 2", false, "\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Multicast 3", false, "\xff\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"}, + + // IPv6 link-local address starts with fe80::/10. + {"IPv6 Unicast Link-Local 1", false, "\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Unicast Link-Local 2", false, "\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"}, + {"IPv6 Unicast Link-Local 3", false, "\xfe\x80\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff"}, + {"IPv6 Unicast Link-Local 4", false, "\xfe\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Unicast Link-Local 5", false, "\xfe\xbf\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"}, + + // IPv6 addresses that are neither multicast nor link-local. + {"IPv6 Unicast Not Link-Local 1", true, "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Unicast Not Link-Local 2", true, "\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"}, + {"IPv6 Unicast Not Link-local 3", true, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Unicast Not Link-Local 4", true, "\xfe\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Unicast Not Link-Local 5", true, "\xfe\xdf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Unicast Not Link-Local 6", true, "\xfd\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + {"IPv6 Unicast Not Link-Local 7", true, "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}, + } { + t.Run(tc.name, func(t *testing.T) { + s := stack.New([]string{"fakeNet"}, nil, stack.Options{}) + + id, _ := channel.New(10, defaultMTU, "") + if err := s.CreateNIC(1, id); err != nil { + t.Fatalf("CreateNIC failed: %v", err) + } + + s.SetRouteTable([]tcpip.Route{}) + + var anyAddr tcpip.Address + if len(tc.address) == header.IPv4AddressSize { + anyAddr = header.IPv4Any + } else { + anyAddr = header.IPv6Any + } + + // If there is no endpoint, it won't work. + if _, err := s.FindRoute(1, anyAddr, tc.address, fakeNetNumber); err != tcpip.ErrNoRoute { + t.Fatalf("got FindRoute(1, %v, %v, %v) = %v, want = %v", anyAddr, tc.address, fakeNetNumber, err, tcpip.ErrNoRoute) + } + + if err := s.AddAddress(1, fakeNetNumber, anyAddr); err != nil { + t.Fatalf("AddAddress(%v, %v) failed: %v", fakeNetNumber, anyAddr, err) + } + + r, err := s.FindRoute(1, anyAddr, tc.address, fakeNetNumber) + if tc.routeNeeded { + // Route table is empty but we need a route, this should cause an error. + if err != tcpip.ErrNoRoute { + t.Fatalf("got FindRoute(1, %v, %v, %v) = %v, want = %v", anyAddr, tc.address, fakeNetNumber, err, tcpip.ErrNoRoute) + } + } else { + if err != nil { + t.Fatalf("FindRoute(1, %v, %v, %v) failed: %v", anyAddr, tc.address, fakeNetNumber, err) + } + if r.LocalAddress != anyAddr { + t.Errorf("Bad local address: got %v, want = %v", r.LocalAddress, anyAddr) + } + if r.RemoteAddress != tc.address { + t.Errorf("Bad remote address: got %v, want = %v", r.RemoteAddress, tc.address) + } + } + // If the NIC doesn't exist, it won't work. + if _, err := s.FindRoute(2, anyAddr, tc.address, fakeNetNumber); err != tcpip.ErrNoRoute { + t.Fatalf("got FindRoute(2, %v, %v, %v) = %v want = %v", anyAddr, tc.address, fakeNetNumber, err, tcpip.ErrNoRoute) + } + }) + } +} + // Set the subnet, then check that packet is delivered. func TestSubnetAcceptsMatchingPacket(t *testing.T) { s := stack.New([]string{"fakeNet"}, nil, stack.Options{}) -- cgit v1.2.3