summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChris Kuiper <ckuiper@google.com>2018-12-16 23:04:56 -0800
committerShentubot <shentubot@google.com>2018-12-16 23:05:59 -0800
commite491ebbacf548a2a2f818f1093b09d6f1c13e7e0 (patch)
tree054813166ec5294ff089682c8528a43fdf704a66
parentf74eed464b55d9640432432cd96d811578e9081e (diff)
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
-rw-r--r--pkg/tcpip/header/ipv6.go12
-rw-r--r--pkg/tcpip/stack/stack.go7
-rw-r--r--pkg/tcpip/stack/stack_test.go86
3 files changed, 103 insertions, 2 deletions
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{})