From d7fd00bad1416fb44b3303331ee223c28c69fe9b Mon Sep 17 00:00:00 2001
From: Ghanan Gowripalan <ghanan@google.com>
Date: Tue, 6 Apr 2021 10:14:10 -0700
Subject: Do not perform MLD for certain multicast scopes

...as per RFC 2710 section 5 page 10.

Test: ipv6_test.TestMLDSkipProtocol
PiperOrigin-RevId: 367031126
---
 pkg/tcpip/header/ipv6.go      | 49 ++++++++++++++++++++++----
 pkg/tcpip/header/ipv6_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+), 6 deletions(-)

(limited to 'pkg/tcpip/header')

diff --git a/pkg/tcpip/header/ipv6.go b/pkg/tcpip/header/ipv6.go
index f2403978c..7711abec1 100644
--- a/pkg/tcpip/header/ipv6.go
+++ b/pkg/tcpip/header/ipv6.go
@@ -142,11 +142,6 @@ const (
 	// ipv6MulticastAddressScopeMask is the mask for the scope (scop) field,
 	// within the byte holding the field, as per RFC 4291 section 2.7.
 	ipv6MulticastAddressScopeMask = 0xF
-
-	// ipv6LinkLocalMulticastScope is the value of the scope (scop) field within
-	// a multicast IPv6 address that indicates the address has link-local scope,
-	// as per RFC 4291 section 2.7.
-	ipv6LinkLocalMulticastScope = 2
 )
 
 // IPv6EmptySubnet is the empty IPv6 subnet. It may also be known as the
@@ -399,7 +394,7 @@ func IsV6LoopbackAddress(addr tcpip.Address) bool {
 // IsV6LinkLocalMulticastAddress determines if the provided address is an IPv6
 // link-local multicast address.
 func IsV6LinkLocalMulticastAddress(addr tcpip.Address) bool {
-	return IsV6MulticastAddress(addr) && addr[ipv6MulticastAddressScopeByteIdx]&ipv6MulticastAddressScopeMask == ipv6LinkLocalMulticastScope
+	return IsV6MulticastAddress(addr) && V6MulticastScope(addr) == IPv6LinkLocalMulticastScope
 }
 
 // AppendOpaqueInterfaceIdentifier appends a 64 bit opaque interface identifier
@@ -520,3 +515,45 @@ func GenerateTempIPv6SLAACAddr(tempIIDHistory []byte, stableAddr tcpip.Address)
 		PrefixLen: IIDOffsetInIPv6Address * 8,
 	}
 }
+
+// IPv6MulticastScope is the scope of a multicast IPv6 address.
+type IPv6MulticastScope uint8
+
+// The various values for IPv6 multicast scopes, as per RFC 7346 section 2:
+//
+//      +------+--------------------------+-------------------------+
+//      | scop | NAME                     | REFERENCE               |
+//      +------+--------------------------+-------------------------+
+//      |  0   | Reserved                 | [RFC4291], RFC 7346     |
+//      |  1   | Interface-Local scope    | [RFC4291], RFC 7346     |
+//      |  2   | Link-Local scope         | [RFC4291], RFC 7346     |
+//      |  3   | Realm-Local scope        | [RFC4291], RFC 7346     |
+//      |  4   | Admin-Local scope        | [RFC4291], RFC 7346     |
+//      |  5   | Site-Local scope         | [RFC4291], RFC 7346     |
+//      |  6   | Unassigned               |                         |
+//      |  7   | Unassigned               |                         |
+//      |  8   | Organization-Local scope | [RFC4291], RFC 7346     |
+//      |  9   | Unassigned               |                         |
+//      |  A   | Unassigned               |                         |
+//      |  B   | Unassigned               |                         |
+//      |  C   | Unassigned               |                         |
+//      |  D   | Unassigned               |                         |
+//      |  E   | Global scope             | [RFC4291], RFC 7346     |
+//      |  F   | Reserved                 | [RFC4291], RFC 7346     |
+//      +------+--------------------------+-------------------------+
+const (
+	IPv6Reserved0MulticastScope         = IPv6MulticastScope(0x0)
+	IPv6InterfaceLocalMulticastScope    = IPv6MulticastScope(0x1)
+	IPv6LinkLocalMulticastScope         = IPv6MulticastScope(0x2)
+	IPv6RealmLocalMulticastScope        = IPv6MulticastScope(0x3)
+	IPv6AdminLocalMulticastScope        = IPv6MulticastScope(0x4)
+	IPv6SiteLocalMulticastScope         = IPv6MulticastScope(0x5)
+	IPv6OrganizationLocalMulticastScope = IPv6MulticastScope(0x8)
+	IPv6GlobalMulticastScope            = IPv6MulticastScope(0xE)
+	IPv6ReservedFMulticastScope         = IPv6MulticastScope(0xF)
+)
+
+// V6MulticastScope returns the scope of a multicast address.
+func V6MulticastScope(addr tcpip.Address) IPv6MulticastScope {
+	return IPv6MulticastScope(addr[ipv6MulticastAddressScopeByteIdx] & ipv6MulticastAddressScopeMask)
+}
diff --git a/pkg/tcpip/header/ipv6_test.go b/pkg/tcpip/header/ipv6_test.go
index f10f446a6..38b6dbc18 100644
--- a/pkg/tcpip/header/ipv6_test.go
+++ b/pkg/tcpip/header/ipv6_test.go
@@ -373,3 +373,83 @@ func TestSolicitedNodeAddr(t *testing.T) {
 		})
 	}
 }
+
+func TestV6MulticastScope(t *testing.T) {
+	tests := []struct {
+		addr tcpip.Address
+		want header.IPv6MulticastScope
+	}{
+		{
+			addr: "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6Reserved0MulticastScope,
+		},
+		{
+			addr: "\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6InterfaceLocalMulticastScope,
+		},
+		{
+			addr: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6LinkLocalMulticastScope,
+		},
+		{
+			addr: "\xff\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6RealmLocalMulticastScope,
+		},
+		{
+			addr: "\xff\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6AdminLocalMulticastScope,
+		},
+		{
+			addr: "\xff\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6SiteLocalMulticastScope,
+		},
+		{
+			addr: "\xff\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6MulticastScope(6),
+		},
+		{
+			addr: "\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6MulticastScope(7),
+		},
+		{
+			addr: "\xff\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6OrganizationLocalMulticastScope,
+		},
+		{
+			addr: "\xff\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6MulticastScope(9),
+		},
+		{
+			addr: "\xff\x0a\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6MulticastScope(10),
+		},
+		{
+			addr: "\xff\x0b\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6MulticastScope(11),
+		},
+		{
+			addr: "\xff\x0c\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6MulticastScope(12),
+		},
+		{
+			addr: "\xff\x0d\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6MulticastScope(13),
+		},
+		{
+			addr: "\xff\x0e\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6GlobalMulticastScope,
+		},
+		{
+			addr: "\xff\x0f\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+			want: header.IPv6ReservedFMulticastScope,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(fmt.Sprintf("%s", test.addr), func(t *testing.T) {
+			if got := header.V6MulticastScope(test.addr); got != test.want {
+				t.Fatalf("got header.V6MulticastScope(%s) = %d, want = %d", test.addr, got, test.want)
+			}
+		})
+	}
+}
-- 
cgit v1.2.3