mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Do not perform MLD for certain multicast scopes
...as per RFC 2710 section 5 page 10. Test: ipv6_test.TestMLDSkipProtocol PiperOrigin-RevId: 367031126
This commit is contained in:
committed by
gVisor bot
parent
fb4c700d06
commit
d7fd00bad1
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,14 +156,6 @@ type GenericMulticastProtocolOptions struct {
|
||||
//
|
||||
// Unsolicited reports are transmitted when a group is newly joined.
|
||||
MaxUnsolicitedReportDelay time.Duration
|
||||
|
||||
// AllNodesAddress is a multicast address that all nodes on a network should
|
||||
// be a member of.
|
||||
//
|
||||
// This address will not have the generic multicast protocol performed on it;
|
||||
// it will be left in the non member/listener state, and packets will never
|
||||
// be sent for it.
|
||||
AllNodesAddress tcpip.Address
|
||||
}
|
||||
|
||||
// MulticastGroupProtocol is a multicast group protocol whose core state machine
|
||||
@@ -188,6 +180,10 @@ type MulticastGroupProtocol interface {
|
||||
|
||||
// SendLeave sends a multicast leave for the specified group address.
|
||||
SendLeave(groupAddress tcpip.Address) tcpip.Error
|
||||
|
||||
// ShouldPerformProtocol returns true iff the protocol should be performed for
|
||||
// the specified group.
|
||||
ShouldPerformProtocol(tcpip.Address) bool
|
||||
}
|
||||
|
||||
// GenericMulticastProtocolState is the per interface generic multicast protocol
|
||||
@@ -455,20 +451,7 @@ func (g *GenericMulticastProtocolState) initializeNewMemberLocked(groupAddress t
|
||||
|
||||
info.lastToSendReport = false
|
||||
|
||||
if groupAddress == g.opts.AllNodesAddress {
|
||||
// As per RFC 2236 section 6 page 10 (for IGMPv2),
|
||||
//
|
||||
// The all-systems group (address 224.0.0.1) is handled as a special
|
||||
// case. The host starts in Idle Member state for that group on every
|
||||
// interface, never transitions to another state, and never sends a
|
||||
// report for that group.
|
||||
//
|
||||
// As per RFC 2710 section 5 page 10 (for MLDv1),
|
||||
//
|
||||
// The link-scope all-nodes address (FF02::1) is handled as a special
|
||||
// case. The node starts in Idle Listener state for that address on
|
||||
// every interface, never transitions to another state, and never sends
|
||||
// a Report or Done for that address.
|
||||
if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
|
||||
info.state = idleMember
|
||||
return
|
||||
}
|
||||
@@ -537,20 +520,7 @@ func (g *GenericMulticastProtocolState) maybeSendLeave(groupAddress tcpip.Addres
|
||||
return
|
||||
}
|
||||
|
||||
if groupAddress == g.opts.AllNodesAddress {
|
||||
// As per RFC 2236 section 6 page 10 (for IGMPv2),
|
||||
//
|
||||
// The all-systems group (address 224.0.0.1) is handled as a special
|
||||
// case. The host starts in Idle Member state for that group on every
|
||||
// interface, never transitions to another state, and never sends a
|
||||
// report for that group.
|
||||
//
|
||||
// As per RFC 2710 section 5 page 10 (for MLDv1),
|
||||
//
|
||||
// The link-scope all-nodes address (FF02::1) is handled as a special
|
||||
// case. The node starts in Idle Listener state for that address on
|
||||
// every interface, never transitions to another state, and never sends
|
||||
// a Report or Done for that address.
|
||||
if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -627,20 +597,7 @@ func (g *GenericMulticastProtocolState) setDelayTimerForAddressRLocked(groupAddr
|
||||
return
|
||||
}
|
||||
|
||||
if groupAddress == g.opts.AllNodesAddress {
|
||||
// As per RFC 2236 section 6 page 10 (for IGMPv2),
|
||||
//
|
||||
// The all-systems group (address 224.0.0.1) is handled as a special
|
||||
// case. The host starts in Idle Member state for that group on every
|
||||
// interface, never transitions to another state, and never sends a
|
||||
// report for that group.
|
||||
//
|
||||
// As per RFC 2710 section 5 page 10 (for MLDv1),
|
||||
//
|
||||
// The link-scope all-nodes address (FF02::1) is handled as a special
|
||||
// case. The node starts in Idle Listener state for that address on
|
||||
// every interface, never transitions to another state, and never sends
|
||||
// a Report or Done for that address.
|
||||
if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ type mockMulticastGroupProtocolProtectedFields struct {
|
||||
type mockMulticastGroupProtocol struct {
|
||||
t *testing.T
|
||||
|
||||
skipProtocolAddress tcpip.Address
|
||||
|
||||
mu mockMulticastGroupProtocolProtectedFields
|
||||
}
|
||||
|
||||
@@ -165,6 +167,11 @@ func (m *mockMulticastGroupProtocol) SendLeave(groupAddress tcpip.Address) tcpip
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShouldPerformProtocol implements ip.MulticastGroupProtocol.
|
||||
func (m *mockMulticastGroupProtocol) ShouldPerformProtocol(groupAddress tcpip.Address) bool {
|
||||
return groupAddress != m.skipProtocolAddress
|
||||
}
|
||||
|
||||
func (m *mockMulticastGroupProtocol) check(sendReportGroupAddresses []tcpip.Address, sendLeaveGroupAddresses []tcpip.Address) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
@@ -193,10 +200,11 @@ func (m *mockMulticastGroupProtocol) check(sendReportGroupAddresses []tcpip.Addr
|
||||
cmp.FilterPath(
|
||||
func(p cmp.Path) bool {
|
||||
switch p.Last().String() {
|
||||
case ".RWMutex", ".t", ".makeQueuePackets", ".disabled", ".genericMulticastGroup":
|
||||
case ".RWMutex", ".t", ".makeQueuePackets", ".disabled", ".genericMulticastGroup", ".skipProtocolAddress":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
},
|
||||
cmp.Ignore(),
|
||||
),
|
||||
@@ -225,14 +233,13 @@ func TestJoinGroup(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
mgp := mockMulticastGroupProtocol{t: t}
|
||||
mgp := mockMulticastGroupProtocol{t: t, skipProtocolAddress: addr2}
|
||||
clock := faketime.NewManualClock()
|
||||
|
||||
mgp.init(ip.GenericMulticastProtocolOptions{
|
||||
Rand: rand.New(rand.NewSource(0)),
|
||||
Clock: clock,
|
||||
MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
|
||||
AllNodesAddress: addr2,
|
||||
})
|
||||
|
||||
// Joining a group should send a report immediately and another after
|
||||
@@ -279,14 +286,13 @@ func TestLeaveGroup(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
mgp := mockMulticastGroupProtocol{t: t}
|
||||
mgp := mockMulticastGroupProtocol{t: t, skipProtocolAddress: addr2}
|
||||
clock := faketime.NewManualClock()
|
||||
|
||||
mgp.init(ip.GenericMulticastProtocolOptions{
|
||||
Rand: rand.New(rand.NewSource(1)),
|
||||
Clock: clock,
|
||||
MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
|
||||
AllNodesAddress: addr2,
|
||||
})
|
||||
|
||||
mgp.joinGroup(test.addr)
|
||||
@@ -356,14 +362,13 @@ func TestHandleReport(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
mgp := mockMulticastGroupProtocol{t: t}
|
||||
mgp := mockMulticastGroupProtocol{t: t, skipProtocolAddress: addr3}
|
||||
clock := faketime.NewManualClock()
|
||||
|
||||
mgp.init(ip.GenericMulticastProtocolOptions{
|
||||
Rand: rand.New(rand.NewSource(2)),
|
||||
Clock: clock,
|
||||
MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
|
||||
AllNodesAddress: addr3,
|
||||
})
|
||||
|
||||
mgp.joinGroup(addr1)
|
||||
@@ -446,14 +451,13 @@ func TestHandleQuery(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
mgp := mockMulticastGroupProtocol{t: t}
|
||||
mgp := mockMulticastGroupProtocol{t: t, skipProtocolAddress: addr3}
|
||||
clock := faketime.NewManualClock()
|
||||
|
||||
mgp.init(ip.GenericMulticastProtocolOptions{
|
||||
Rand: rand.New(rand.NewSource(3)),
|
||||
Clock: clock,
|
||||
MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
|
||||
AllNodesAddress: addr3,
|
||||
})
|
||||
|
||||
mgp.joinGroup(addr1)
|
||||
@@ -574,14 +578,13 @@ func TestJoinCount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMakeAllNonMemberAndInitialize(t *testing.T) {
|
||||
mgp := mockMulticastGroupProtocol{t: t}
|
||||
mgp := mockMulticastGroupProtocol{t: t, skipProtocolAddress: addr3}
|
||||
clock := faketime.NewManualClock()
|
||||
|
||||
mgp.init(ip.GenericMulticastProtocolOptions{
|
||||
Rand: rand.New(rand.NewSource(3)),
|
||||
Clock: clock,
|
||||
MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
|
||||
AllNodesAddress: addr3,
|
||||
})
|
||||
|
||||
mgp.joinGroup(addr1)
|
||||
|
||||
@@ -126,6 +126,17 @@ func (igmp *igmpState) SendLeave(groupAddress tcpip.Address) tcpip.Error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ShouldPerformProtocol implements ip.MulticastGroupProtocol.
|
||||
func (igmp *igmpState) ShouldPerformProtocol(groupAddress tcpip.Address) bool {
|
||||
// As per RFC 2236 section 6 page 10,
|
||||
//
|
||||
// The all-systems group (address 224.0.0.1) is handled as a special
|
||||
// case. The host starts in Idle Member state for that group on every
|
||||
// interface, never transitions to another state, and never sends a
|
||||
// report for that group.
|
||||
return groupAddress != header.IPv4AllSystems
|
||||
}
|
||||
|
||||
// init sets up an igmpState struct, and is required to be called before using
|
||||
// a new igmpState.
|
||||
//
|
||||
@@ -137,7 +148,6 @@ func (igmp *igmpState) init(ep *endpoint) {
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: igmp,
|
||||
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
|
||||
AllNodesAddress: header.IPv4AllSystems,
|
||||
})
|
||||
igmp.igmpV1Present = igmpV1PresentDefault
|
||||
igmp.igmpV1Job = ep.protocol.stack.NewJob(&ep.mu, func() {
|
||||
|
||||
@@ -80,6 +80,25 @@ func (mld *mldState) SendLeave(groupAddress tcpip.Address) tcpip.Error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ShouldPerformProtocol implements ip.MulticastGroupProtocol.
|
||||
func (mld *mldState) ShouldPerformProtocol(groupAddress tcpip.Address) bool {
|
||||
// As per RFC 2710 section 5 page 10,
|
||||
//
|
||||
// The link-scope all-nodes address (FF02::1) is handled as a special
|
||||
// case. The node starts in Idle Listener state for that address on
|
||||
// every interface, never transitions to another state, and never sends
|
||||
// a Report or Done for that address.
|
||||
//
|
||||
// MLD messages are never sent for multicast addresses whose scope is 0
|
||||
// (reserved) or 1 (node-local).
|
||||
if groupAddress == header.IPv6AllNodesMulticastAddress {
|
||||
return false
|
||||
}
|
||||
|
||||
scope := header.V6MulticastScope(groupAddress)
|
||||
return scope != header.IPv6Reserved0MulticastScope && scope != header.IPv6InterfaceLocalMulticastScope
|
||||
}
|
||||
|
||||
// init sets up an mldState struct, and is required to be called before using
|
||||
// a new mldState.
|
||||
//
|
||||
@@ -91,7 +110,6 @@ func (mld *mldState) init(ep *endpoint) {
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: mld,
|
||||
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
|
||||
AllNodesAddress: header.IPv6AllNodesMulticastAddress,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -464,3 +464,141 @@ func TestMLDPacketValidation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMLDSkipProtocol(t *testing.T) {
|
||||
const nicID = 1
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
group tcpip.Address
|
||||
expectReport bool
|
||||
}{
|
||||
{
|
||||
name: "Reserverd0",
|
||||
group: "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: false,
|
||||
},
|
||||
{
|
||||
name: "Interface Local",
|
||||
group: "\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: false,
|
||||
},
|
||||
{
|
||||
name: "Link Local",
|
||||
group: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Realm Local",
|
||||
group: "\xff\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Admin Local",
|
||||
group: "\xff\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Site Local",
|
||||
group: "\xff\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Unassigned(6)",
|
||||
group: "\xff\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Unassigned(7)",
|
||||
group: "\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Organization Local",
|
||||
group: "\xff\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Unassigned(9)",
|
||||
group: "\xff\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Unassigned(A)",
|
||||
group: "\xff\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Unassigned(B)",
|
||||
group: "\xff\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Unassigned(C)",
|
||||
group: "\xff\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Unassigned(D)",
|
||||
group: "\xff\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "Global",
|
||||
group: "\xff\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
{
|
||||
name: "ReservedF",
|
||||
group: "\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11",
|
||||
expectReport: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
|
||||
MLD: ipv6.MLDOptions{
|
||||
Enabled: true,
|
||||
},
|
||||
})},
|
||||
})
|
||||
e := channel.New(1, header.IPv6MinimumMTU, "")
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
if err := s.AddAddress(nicID, ipv6.ProtocolNumber, linkLocalAddr); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ipv6.ProtocolNumber, linkLocalAddr, err)
|
||||
}
|
||||
if p, ok := e.Read(); !ok {
|
||||
t.Fatal("expected a report message to be sent")
|
||||
} else {
|
||||
validateMLDPacket(t, stack.PayloadSince(p.Pkt.NetworkHeader()), linkLocalAddr, linkLocalAddrSNMC, header.ICMPv6MulticastListenerReport, linkLocalAddrSNMC)
|
||||
}
|
||||
|
||||
if err := s.JoinGroup(ipv6.ProtocolNumber, nicID, test.group); err != nil {
|
||||
t.Fatalf("s.JoinGroup(%d, %d, %s): %s", ipv6.ProtocolNumber, nicID, test.group, err)
|
||||
}
|
||||
if isInGroup, err := s.IsInGroup(nicID, test.group); err != nil {
|
||||
t.Fatalf("IsInGroup(%d, %s): %s", nicID, test.group, err)
|
||||
} else if !isInGroup {
|
||||
t.Fatalf("got IsInGroup(%d, %s) = false, want = true", nicID, test.group)
|
||||
}
|
||||
|
||||
if !test.expectReport {
|
||||
if p, ok := e.Read(); ok {
|
||||
t.Fatalf("got e.Read() = (%#v, true), want = (_, false)", p)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if p, ok := e.Read(); !ok {
|
||||
t.Fatal("expected a report message to be sent")
|
||||
} else {
|
||||
validateMLDPacket(t, stack.PayloadSince(p.Pkt.NetworkHeader()), linkLocalAddr, test.group, header.ICMPv6MulticastListenerReport, test.group)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user