summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/network
diff options
context:
space:
mode:
authorArthur Sfez <asfez@google.com>2021-02-24 16:54:11 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-24 16:56:19 -0800
commit1d2975ffbe0e32ebcd2fe9307544799b2f9ae632 (patch)
treeae1db85dec625cc2f38dee9a66acc71f73782de0 /pkg/tcpip/network
parentf5692f7dcc48a76a5d7b45cdf71b59be876adb42 (diff)
Validate MLD packets
Fixes #5490 PiperOrigin-RevId: 359401532
Diffstat (limited to 'pkg/tcpip/network')
-rw-r--r--pkg/tcpip/network/ipv6/icmp.go29
-rw-r--r--pkg/tcpip/network/ipv6/icmp_test.go104
-rw-r--r--pkg/tcpip/network/ipv6/ipv6.go70
-rw-r--r--pkg/tcpip/network/ipv6/ipv6_test.go67
-rw-r--r--pkg/tcpip/network/ipv6/mld_test.go155
-rw-r--r--pkg/tcpip/network/multicast_group_test.go35
6 files changed, 367 insertions, 93 deletions
diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go
index 2690644d6..5f44ab317 100644
--- a/pkg/tcpip/network/ipv6/icmp.go
+++ b/pkg/tcpip/network/ipv6/icmp.go
@@ -260,12 +260,31 @@ func getTargetLinkAddr(it header.NDPOptionIterator) (tcpip.LinkAddress, bool) {
})
}
-func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
+func isMLDValid(pkt *stack.PacketBuffer, iph header.IPv6, routerAlert *header.IPv6RouterAlertOption) bool {
+ // As per RFC 2710 section 3:
+ // All MLD messages described in this document are sent with a link-local
+ // IPv6 Source Address, an IPv6 Hop Limit of 1, and an IPv6 Router Alert
+ // option in a Hop-by-Hop Options header.
+ if routerAlert == nil || routerAlert.Value != header.IPv6RouterAlertMLD {
+ return false
+ }
+ if pkt.Data.Size() < header.ICMPv6HeaderSize+header.MLDMinimumSize {
+ return false
+ }
+ if iph.HopLimit() != header.MLDHopLimit {
+ return false
+ }
+ if !header.IsV6LinkLocalAddress(iph.SourceAddress()) {
+ return false
+ }
+ return true
+}
+
+func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, routerAlert *header.IPv6RouterAlertOption) {
sent := e.stats.icmp.packetsSent
received := e.stats.icmp.packetsReceived
- // TODO(gvisor.dev/issue/170): ICMP packets don't have their
- // TransportHeader fields set. See icmp/protocol.go:protocol.Parse for a
- // full explanation.
+ // TODO(gvisor.dev/issue/170): ICMP packets don't have their TransportHeader
+ // fields set. See icmp/protocol.go:protocol.Parse for a full explanation.
v, ok := pkt.Data.PullUp(header.ICMPv6HeaderSize)
if !ok {
received.invalid.Increment()
@@ -823,7 +842,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
panic(fmt.Sprintf("unrecognized MLD message = %d", icmpType))
}
- if pkt.Data.Size()-header.ICMPv6HeaderSize < header.MLDMinimumSize {
+ if !isMLDValid(pkt, iph, routerAlert) {
received.invalid.Increment()
return
}
diff --git a/pkg/tcpip/network/ipv6/icmp_test.go b/pkg/tcpip/network/ipv6/icmp_test.go
index 69c1e4bea..c27164344 100644
--- a/pkg/tcpip/network/ipv6/icmp_test.go
+++ b/pkg/tcpip/network/ipv6/icmp_test.go
@@ -48,6 +48,8 @@ const (
// Extra time to use when waiting for an async event to occur.
defaultAsyncPositiveEventTimeout = 30 * time.Second
+
+ arbitraryHopLimit = 42
)
var (
@@ -157,20 +159,29 @@ func (*testInterface) CheckLocalAddress(tcpip.NetworkProtocolNumber, tcpip.Addre
return false
}
-func handleICMPInIPv6(ep stack.NetworkEndpoint, src, dst tcpip.Address, icmp header.ICMPv6) {
- ip := buffer.NewView(header.IPv6MinimumSize)
+func handleICMPInIPv6(ep stack.NetworkEndpoint, src, dst tcpip.Address, icmp header.ICMPv6, hopLimit uint8, includeRouterAlert bool) {
+ var extensionHeaders header.IPv6ExtHdrSerializer
+ if includeRouterAlert {
+ extensionHeaders = header.IPv6ExtHdrSerializer{
+ header.IPv6SerializableHopByHopExtHdr{
+ &header.IPv6RouterAlertOption{Value: header.IPv6RouterAlertMLD},
+ },
+ }
+ }
+ ip := buffer.NewView(header.IPv6MinimumSize + extensionHeaders.Length())
header.IPv6(ip).Encode(&header.IPv6Fields{
PayloadLength: uint16(len(icmp)),
TransportProtocol: header.ICMPv6ProtocolNumber,
- HopLimit: header.NDPHopLimit,
+ HopLimit: hopLimit,
SrcAddr: src,
DstAddr: dst,
+ ExtensionHeaders: extensionHeaders,
})
+
vv := ip.ToVectorisedView()
vv.AppendView(buffer.View(icmp))
ep.HandlePacket(stack.NewPacketBuffer(stack.PacketBufferOptions{
- ReserveHeaderBytes: header.IPv6MinimumSize,
- Data: vv,
+ Data: vv,
}))
}
@@ -223,66 +234,85 @@ func TestICMPCounts(t *testing.T) {
})
types := []struct {
- typ header.ICMPv6Type
- size int
- extraData []byte
+ typ header.ICMPv6Type
+ hopLimit uint8
+ includeRouterAlert bool
+ size int
+ extraData []byte
}{
{
- typ: header.ICMPv6DstUnreachable,
- size: header.ICMPv6DstUnreachableMinimumSize,
+ typ: header.ICMPv6DstUnreachable,
+ hopLimit: arbitraryHopLimit,
+ size: header.ICMPv6DstUnreachableMinimumSize,
},
{
- typ: header.ICMPv6PacketTooBig,
- size: header.ICMPv6PacketTooBigMinimumSize,
+ typ: header.ICMPv6PacketTooBig,
+ hopLimit: arbitraryHopLimit,
+ size: header.ICMPv6PacketTooBigMinimumSize,
},
{
- typ: header.ICMPv6TimeExceeded,
- size: header.ICMPv6MinimumSize,
+ typ: header.ICMPv6TimeExceeded,
+ hopLimit: arbitraryHopLimit,
+ size: header.ICMPv6MinimumSize,
},
{
- typ: header.ICMPv6ParamProblem,
- size: header.ICMPv6MinimumSize,
+ typ: header.ICMPv6ParamProblem,
+ hopLimit: arbitraryHopLimit,
+ size: header.ICMPv6MinimumSize,
},
{
- typ: header.ICMPv6EchoRequest,
- size: header.ICMPv6EchoMinimumSize,
+ typ: header.ICMPv6EchoRequest,
+ hopLimit: arbitraryHopLimit,
+ size: header.ICMPv6EchoMinimumSize,
},
{
- typ: header.ICMPv6EchoReply,
- size: header.ICMPv6EchoMinimumSize,
+ typ: header.ICMPv6EchoReply,
+ hopLimit: arbitraryHopLimit,
+ size: header.ICMPv6EchoMinimumSize,
},
{
- typ: header.ICMPv6RouterSolicit,
- size: header.ICMPv6MinimumSize,
+ typ: header.ICMPv6RouterSolicit,
+ hopLimit: header.NDPHopLimit,
+ size: header.ICMPv6MinimumSize,
},
{
- typ: header.ICMPv6RouterAdvert,
- size: header.ICMPv6HeaderSize + header.NDPRAMinimumSize,
+ typ: header.ICMPv6RouterAdvert,
+ hopLimit: header.NDPHopLimit,
+ size: header.ICMPv6HeaderSize + header.NDPRAMinimumSize,
},
{
- typ: header.ICMPv6NeighborSolicit,
- size: header.ICMPv6NeighborSolicitMinimumSize,
+ typ: header.ICMPv6NeighborSolicit,
+ hopLimit: header.NDPHopLimit,
+ size: header.ICMPv6NeighborSolicitMinimumSize,
},
{
typ: header.ICMPv6NeighborAdvert,
+ hopLimit: header.NDPHopLimit,
size: header.ICMPv6NeighborAdvertMinimumSize,
extraData: tllData[:],
},
{
- typ: header.ICMPv6RedirectMsg,
- size: header.ICMPv6MinimumSize,
+ typ: header.ICMPv6RedirectMsg,
+ hopLimit: header.NDPHopLimit,
+ size: header.ICMPv6MinimumSize,
},
{
- typ: header.ICMPv6MulticastListenerQuery,
- size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
+ typ: header.ICMPv6MulticastListenerQuery,
+ hopLimit: header.MLDHopLimit,
+ includeRouterAlert: true,
+ size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
},
{
- typ: header.ICMPv6MulticastListenerReport,
- size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
+ typ: header.ICMPv6MulticastListenerReport,
+ hopLimit: header.MLDHopLimit,
+ includeRouterAlert: true,
+ size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
},
{
- typ: header.ICMPv6MulticastListenerDone,
- size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
+ typ: header.ICMPv6MulticastListenerDone,
+ hopLimit: header.MLDHopLimit,
+ includeRouterAlert: true,
+ size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
},
{
typ: 255, /* Unrecognized */
@@ -295,12 +325,12 @@ func TestICMPCounts(t *testing.T) {
copy(icmp[typ.size:], typ.extraData)
icmp.SetType(typ.typ)
icmp.SetChecksum(header.ICMPv6Checksum(icmp[:typ.size], lladdr0, lladdr1, buffer.View(typ.extraData).ToVectorisedView()))
- handleICMPInIPv6(ep, lladdr1, lladdr0, icmp)
+ handleICMPInIPv6(ep, lladdr1, lladdr0, icmp, typ.hopLimit, typ.includeRouterAlert)
}
// Construct an empty ICMP packet so that
// Stats().ICMP.ICMPv6ReceivedPacketStats.Invalid is incremented.
- handleICMPInIPv6(ep, lladdr1, lladdr0, header.ICMPv6(buffer.NewView(header.IPv6MinimumSize)))
+ handleICMPInIPv6(ep, lladdr1, lladdr0, header.ICMPv6(buffer.NewView(header.IPv6MinimumSize)), arbitraryHopLimit, false)
icmpv6Stats := s.Stats().ICMP.V6.PacketsReceived
visitStats(reflect.ValueOf(&icmpv6Stats).Elem(), func(name string, s *tcpip.StatCounter) {
@@ -1632,7 +1662,7 @@ func TestCallsToNeighborCache(t *testing.T) {
icmp := test.createPacket()
icmp.SetChecksum(header.ICMPv6Checksum(icmp, test.source, test.destination, buffer.VectorisedView{}))
- handleICMPInIPv6(ep, test.source, test.destination, icmp)
+ handleICMPInIPv6(ep, test.source, test.destination, icmp, header.NDPHopLimit, false)
// Confirm the endpoint calls the correct NUDHandler method.
if testInterface.probeCount != test.wantProbeCount {
diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go
index b16a2d322..7638ade35 100644
--- a/pkg/tcpip/network/ipv6/ipv6.go
+++ b/pkg/tcpip/network/ipv6/ipv6.go
@@ -1001,7 +1001,6 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
vv.AppendView(pkt.TransportHeader().View())
vv.Append(pkt.Data)
it := header.MakeIPv6PayloadIterator(header.IPv6ExtensionHeaderIdentifier(h.NextHeader()), vv)
- hasFragmentHeader := false
// iptables filtering. All packets that reach here are intended for
// this machine and need not be forwarded.
@@ -1012,6 +1011,11 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
return
}
+ var (
+ hasFragmentHeader bool
+ routerAlert *header.IPv6RouterAlertOption
+ )
+
for {
// Keep track of the start of the previous header so we can report the
// special case of a Hop by Hop at a location other than at the start.
@@ -1049,34 +1053,46 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
break
}
- // We currently do not support any IPv6 Hop By Hop extension header
- // options.
- switch opt.UnknownAction() {
- case header.IPv6OptionUnknownActionSkip:
- case header.IPv6OptionUnknownActionDiscard:
- return
- case header.IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest:
- if header.IsV6MulticastAddress(dstAddr) {
+ switch opt := opt.(type) {
+ case *header.IPv6RouterAlertOption:
+ if routerAlert != nil {
+ // As per RFC 2711 section 3, there should be at most one Router
+ // Alert option per packet.
+ //
+ // There MUST only be one option of this type, regardless of
+ // value, per Hop-by-Hop header.
+ stats.MalformedPacketsReceived.Increment()
return
}
- fallthrough
- case header.IPv6OptionUnknownActionDiscardSendICMP:
- // This case satisfies a requirement of RFC 8200 section 4.2
- // which states that an unknown option starting with bits [10] should:
- //
- // discard the packet and, regardless of whether or not the
- // packet's Destination Address was a multicast address, send an
- // ICMP Parameter Problem, Code 2, message to the packet's
- // Source Address, pointing to the unrecognized Option Type.
- //
- _ = e.protocol.returnError(&icmpReasonParameterProblem{
- code: header.ICMPv6UnknownOption,
- pointer: it.ParseOffset() + optsIt.OptionOffset(),
- respondToMulticast: true,
- }, pkt)
- return
+ routerAlert = opt
+ stats.OptionRouterAlertReceived.Increment()
default:
- panic(fmt.Sprintf("unrecognized action for an unrecognized Hop By Hop extension header option = %d", opt))
+ switch opt.UnknownAction() {
+ case header.IPv6OptionUnknownActionSkip:
+ case header.IPv6OptionUnknownActionDiscard:
+ return
+ case header.IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest:
+ if header.IsV6MulticastAddress(dstAddr) {
+ return
+ }
+ fallthrough
+ case header.IPv6OptionUnknownActionDiscardSendICMP:
+ // This case satisfies a requirement of RFC 8200 section 4.2 which
+ // states that an unknown option starting with bits [10] should:
+ //
+ // discard the packet and, regardless of whether or not the
+ // packet's Destination Address was a multicast address, send an
+ // ICMP Parameter Problem, Code 2, message to the packet's
+ // Source Address, pointing to the unrecognized Option Type.
+ _ = e.protocol.returnError(&icmpReasonParameterProblem{
+ code: header.ICMPv6UnknownOption,
+ pointer: it.ParseOffset() + optsIt.OptionOffset(),
+ respondToMulticast: true,
+ }, pkt)
+ return
+ default:
+ panic(fmt.Sprintf("unrecognized action for an unrecognized Hop By Hop extension header option = %d", opt))
+ }
}
}
@@ -1303,7 +1319,7 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
stats.PacketsDelivered.Increment()
if p := tcpip.TransportProtocolNumber(extHdr.Identifier); p == header.ICMPv6ProtocolNumber {
pkt.TransportProtocolNumber = p
- e.handleICMP(pkt, hasFragmentHeader)
+ e.handleICMP(pkt, hasFragmentHeader, routerAlert)
} else {
stats.PacketsDelivered.Increment()
switch res := e.dispatcher.DeliverTransportPacket(p, pkt); res {
diff --git a/pkg/tcpip/network/ipv6/ipv6_test.go b/pkg/tcpip/network/ipv6/ipv6_test.go
index 7e714b50e..05c9f4dbf 100644
--- a/pkg/tcpip/network/ipv6/ipv6_test.go
+++ b/pkg/tcpip/network/ipv6/ipv6_test.go
@@ -382,9 +382,10 @@ func TestAddIpv6Address(t *testing.T) {
func TestReceiveIPv6ExtHdrs(t *testing.T) {
tests := []struct {
- name string
- extHdr func(nextHdr uint8) ([]byte, uint8)
- shouldAccept bool
+ name string
+ extHdr func(nextHdr uint8) ([]byte, uint8)
+ shouldAccept bool
+ countersToBeIncremented func(*tcpip.Stats) []*tcpip.StatCounter
// Should we expect an ICMP response and if so, with what contents?
expectICMP bool
ICMPType header.ICMPv6Type
@@ -399,6 +400,42 @@ func TestReceiveIPv6ExtHdrs(t *testing.T) {
expectICMP: false,
},
{
+ name: "hopbyhop with router alert option",
+ extHdr: func(nextHdr uint8) ([]byte, uint8) {
+ return []byte{
+ nextHdr, 0,
+
+ // Router Alert option.
+ 5, 2, 0, 0, 0, 0,
+ }, hopByHopExtHdrID
+ },
+ shouldAccept: true,
+ countersToBeIncremented: func(stats *tcpip.Stats) []*tcpip.StatCounter {
+ return []*tcpip.StatCounter{stats.IP.OptionRouterAlertReceived}
+ },
+ },
+ {
+ name: "hopbyhop with two router alert options",
+ extHdr: func(nextHdr uint8) ([]byte, uint8) {
+ return []byte{
+ nextHdr, 1,
+
+ // Router Alert option.
+ 5, 2, 0, 0, 0, 0,
+
+ // Router Alert option.
+ 5, 2, 0, 0, 0, 0, 0, 0,
+ }, hopByHopExtHdrID
+ },
+ shouldAccept: false,
+ countersToBeIncremented: func(stats *tcpip.Stats) []*tcpip.StatCounter {
+ return []*tcpip.StatCounter{
+ stats.IP.OptionRouterAlertReceived,
+ stats.IP.MalformedPacketsReceived,
+ }
+ },
+ },
+ {
name: "hopbyhop with unknown option skippable action",
extHdr: func(nextHdr uint8) ([]byte, uint8) {
return []byte{
@@ -924,14 +961,32 @@ func TestReceiveIPv6ExtHdrs(t *testing.T) {
DstAddr: dstAddr,
})
+ stats := s.Stats()
+ var counters []*tcpip.StatCounter
+ // Make sure that the counters we expect to be incremented are initially
+ // set to zero.
+ if fn := test.countersToBeIncremented; fn != nil {
+ counters = fn(&stats)
+ }
+ for i := range counters {
+ if got := counters[i].Value(); got != 0 {
+ t.Errorf("before writing packet: got test.countersToBeIncremented(&stats)[%d].Value() = %d, want = 0", i, got)
+ }
+ }
+
e.InjectInbound(ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: hdr.View().ToVectorisedView(),
}))
- stats := s.Stats().UDP.PacketsReceived
+ for i := range counters {
+ if got := counters[i].Value(); got != 1 {
+ t.Errorf("after writing packet: got test.countersToBeIncremented(&stats)[%d].Value() = %d, want = 1", i, got)
+ }
+ }
+ udpReceiveStat := stats.UDP.PacketsReceived
if !test.shouldAccept {
- if got := stats.Value(); got != 0 {
+ if got := udpReceiveStat.Value(); got != 0 {
t.Errorf("got UDP Rx Packets = %d, want = 0", got)
}
@@ -977,7 +1032,7 @@ func TestReceiveIPv6ExtHdrs(t *testing.T) {
}
// Expect a UDP packet.
- if got := stats.Value(); got != 1 {
+ if got := udpReceiveStat.Value(); got != 1 {
t.Errorf("got UDP Rx Packets = %d, want = 1", got)
}
var buf bytes.Buffer
diff --git a/pkg/tcpip/network/ipv6/mld_test.go b/pkg/tcpip/network/ipv6/mld_test.go
index fe39555e0..f1b8d58f2 100644
--- a/pkg/tcpip/network/ipv6/mld_test.go
+++ b/pkg/tcpip/network/ipv6/mld_test.go
@@ -48,8 +48,7 @@ func validateMLDPacket(t *testing.T, p buffer.View, localAddress, remoteAddress
),
checker.SrcAddr(localAddress),
checker.DstAddr(remoteAddress),
- // Hop Limit for an MLD message must be 1 as per RFC 2710 section 3.
- checker.TTL(1),
+ checker.TTL(header.MLDHopLimit),
checker.MLD(mldType, header.MLDMinimumSize,
checker.MLDMaxRespDelay(0),
checker.MLDMulticastAddress(groupAddress),
@@ -195,7 +194,7 @@ func TestSendQueuedMLDReports(t *testing.T) {
// Adding a global address should not send reports for the already joined
// group since we should only send queued reports when a link-local
- // addres sis assigned.
+ // address is assigned.
//
// Note, we will still expect to send a report for the global address's
// solicited node address from the unspecified address as per RFC 3590
@@ -295,3 +294,153 @@ func TestSendQueuedMLDReports(t *testing.T) {
})
}
}
+
+// createAndInjectMLDPacket creates and injects an MLD packet with the
+// specified fields.
+func createAndInjectMLDPacket(e *channel.Endpoint, mldType header.ICMPv6Type, hopLimit uint8, srcAddress tcpip.Address, withRouterAlertOption bool, routerAlertValue header.IPv6RouterAlertValue) {
+ var extensionHeaders header.IPv6ExtHdrSerializer
+ if withRouterAlertOption {
+ extensionHeaders = header.IPv6ExtHdrSerializer{
+ header.IPv6SerializableHopByHopExtHdr{
+ &header.IPv6RouterAlertOption{Value: routerAlertValue},
+ },
+ }
+ }
+
+ extensionHeadersLength := extensionHeaders.Length()
+ payloadLength := extensionHeadersLength + header.ICMPv6HeaderSize + header.MLDMinimumSize
+ buf := buffer.NewView(header.IPv6MinimumSize + payloadLength)
+
+ ip := header.IPv6(buf)
+ ip.Encode(&header.IPv6Fields{
+ PayloadLength: uint16(payloadLength),
+ HopLimit: hopLimit,
+ TransportProtocol: header.ICMPv6ProtocolNumber,
+ SrcAddr: srcAddress,
+ DstAddr: header.IPv6AllNodesMulticastAddress,
+ ExtensionHeaders: extensionHeaders,
+ })
+
+ icmp := header.ICMPv6(ip.Payload()[extensionHeadersLength:])
+ icmp.SetType(mldType)
+ mld := header.MLD(icmp.MessageBody())
+ mld.SetMaximumResponseDelay(0)
+ mld.SetMulticastAddress(header.IPv6Any)
+ icmp.SetChecksum(header.ICMPv6Checksum(icmp, srcAddress, header.IPv6AllNodesMulticastAddress, buffer.VectorisedView{}))
+
+ e.InjectInbound(ipv6.ProtocolNumber, &stack.PacketBuffer{
+ Data: buf.ToVectorisedView(),
+ })
+}
+
+func TestMLDPacketValidation(t *testing.T) {
+ const (
+ nicID = 1
+ linkLocalAddr2 = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
+ )
+
+ tests := []struct {
+ name string
+ messageType header.ICMPv6Type
+ srcAddr tcpip.Address
+ includeRouterAlertOption bool
+ routerAlertValue header.IPv6RouterAlertValue
+ hopLimit uint8
+ expectValidMLD bool
+ getMessageTypeStatValue func(tcpip.Stats) uint64
+ }{
+ {
+ name: "valid",
+ messageType: header.ICMPv6MulticastListenerQuery,
+ includeRouterAlertOption: true,
+ routerAlertValue: header.IPv6RouterAlertMLD,
+ srcAddr: linkLocalAddr2,
+ hopLimit: header.MLDHopLimit,
+ expectValidMLD: true,
+ getMessageTypeStatValue: func(stats tcpip.Stats) uint64 { return stats.ICMP.V6.PacketsReceived.MulticastListenerQuery.Value() },
+ },
+ {
+ name: "bad hop limit",
+ messageType: header.ICMPv6MulticastListenerReport,
+ includeRouterAlertOption: true,
+ routerAlertValue: header.IPv6RouterAlertMLD,
+ srcAddr: linkLocalAddr2,
+ hopLimit: header.MLDHopLimit + 1,
+ expectValidMLD: false,
+ getMessageTypeStatValue: func(stats tcpip.Stats) uint64 { return stats.ICMP.V6.PacketsReceived.MulticastListenerReport.Value() },
+ },
+ {
+ name: "src ip not link local",
+ messageType: header.ICMPv6MulticastListenerReport,
+ includeRouterAlertOption: true,
+ routerAlertValue: header.IPv6RouterAlertMLD,
+ srcAddr: globalAddr,
+ hopLimit: header.MLDHopLimit,
+ expectValidMLD: false,
+ getMessageTypeStatValue: func(stats tcpip.Stats) uint64 { return stats.ICMP.V6.PacketsReceived.MulticastListenerReport.Value() },
+ },
+ {
+ name: "missing router alert ip option",
+ messageType: header.ICMPv6MulticastListenerDone,
+ includeRouterAlertOption: false,
+ srcAddr: linkLocalAddr2,
+ hopLimit: header.MLDHopLimit,
+ expectValidMLD: false,
+ getMessageTypeStatValue: func(stats tcpip.Stats) uint64 { return stats.ICMP.V6.PacketsReceived.MulticastListenerDone.Value() },
+ },
+ {
+ name: "incorrect router alert value",
+ messageType: header.ICMPv6MulticastListenerDone,
+ includeRouterAlertOption: true,
+ routerAlertValue: header.IPv6RouterAlertRSVP,
+ srcAddr: linkLocalAddr2,
+ hopLimit: header.MLDHopLimit,
+ expectValidMLD: false,
+ getMessageTypeStatValue: func(stats tcpip.Stats) uint64 { return stats.ICMP.V6.PacketsReceived.MulticastListenerDone.Value() },
+ },
+ }
+
+ 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(nicID, header.IPv6MinimumMTU, "")
+ if err := s.CreateNIC(nicID, e); err != nil {
+ t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
+ }
+ stats := s.Stats()
+ // Verify that every relevant stats is zero'd before we send a packet.
+ if got := test.getMessageTypeStatValue(s.Stats()); got != 0 {
+ t.Errorf("got test.getMessageTypeStatValue(s.Stats()) = %d, want = 0", got)
+ }
+ if got := stats.ICMP.V6.PacketsReceived.Invalid.Value(); got != 0 {
+ t.Errorf("got stats.ICMP.V6.PacketsReceived.Invalid.Value() = %d, want = 0", got)
+ }
+ if got := stats.IP.PacketsDelivered.Value(); got != 0 {
+ t.Fatalf("got stats.IP.PacketsDelivered.Value() = %d, want = 0", got)
+ }
+ createAndInjectMLDPacket(e, test.messageType, test.hopLimit, test.srcAddr, test.includeRouterAlertOption, test.routerAlertValue)
+ // We always expect the packet to pass IP validation.
+ if got := stats.IP.PacketsDelivered.Value(); got != 1 {
+ t.Fatalf("got stats.IP.PacketsDelivered.Value() = %d, want = 1", got)
+ }
+ // Even when the MLD-specific validation checks fail, we expect the
+ // corresponding MLD counter to be incremented.
+ if got := test.getMessageTypeStatValue(s.Stats()); got != 1 {
+ t.Errorf("got test.getMessageTypeStatValue(s.Stats()) = %d, want = 1", got)
+ }
+ var expectedInvalidCount uint64
+ if !test.expectValidMLD {
+ expectedInvalidCount = 1
+ }
+ if got := stats.ICMP.V6.PacketsReceived.Invalid.Value(); got != expectedInvalidCount {
+ t.Errorf("got stats.ICMP.V6.PacketsReceived.Invalid.Value() = %d, want = %d", got, expectedInvalidCount)
+ }
+ })
+ }
+}
diff --git a/pkg/tcpip/network/multicast_group_test.go b/pkg/tcpip/network/multicast_group_test.go
index 03b7ecffb..73913aef8 100644
--- a/pkg/tcpip/network/multicast_group_test.go
+++ b/pkg/tcpip/network/multicast_group_test.go
@@ -37,7 +37,8 @@ const (
stackIPv4Addr = tcpip.Address("\x0a\x00\x00\x01")
defaultIPv4PrefixLength = 24
- ipv6Addr = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
+ linkLocalIPv6Addr1 = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
+ linkLocalIPv6Addr2 = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
ipv4MulticastAddr1 = tcpip.Address("\xe0\x00\x00\x03")
ipv4MulticastAddr2 = tcpip.Address("\xe0\x00\x00\x04")
@@ -69,7 +70,7 @@ var (
return uint8(ipv4.UnsolicitedReportIntervalMax / decisecond)
}()
- ipv6AddrSNMC = header.SolicitedNodeAddr(ipv6Addr)
+ ipv6AddrSNMC = header.SolicitedNodeAddr(linkLocalIPv6Addr1)
)
// validateMLDPacket checks that a passed PacketInfo is an IPv6 MLD packet
@@ -82,7 +83,7 @@ func validateMLDPacket(t *testing.T, p channel.PacketInfo, remoteAddress tcpip.A
checker.IPv6ExtHdr(
checker.IPv6HopByHopExtensionHeader(checker.IPv6RouterAlert(header.IPv6RouterAlertMLD)),
),
- checker.SrcAddr(ipv6Addr),
+ checker.SrcAddr(linkLocalIPv6Addr1),
checker.DstAddr(remoteAddress),
// Hop Limit for an MLD message must be 1 as per RFC 2710 section 3.
checker.TTL(1),
@@ -153,8 +154,8 @@ func createStackWithLinkEndpoint(t *testing.T, v4, mgpEnabled bool, e stack.Link
if err := s.AddAddressWithPrefix(nicID, ipv4.ProtocolNumber, addr); err != nil {
t.Fatalf("AddAddressWithPrefix(%d, %d, %s): %s", nicID, ipv4.ProtocolNumber, addr, err)
}
- if err := s.AddAddress(nicID, ipv6.ProtocolNumber, ipv6Addr); err != nil {
- t.Fatalf("AddAddress(%d, %d, %s): %s", nicID, ipv6.ProtocolNumber, ipv6Addr, err)
+ if err := s.AddAddress(nicID, ipv6.ProtocolNumber, linkLocalIPv6Addr1); err != nil {
+ t.Fatalf("AddAddress(%d, %d, %s): %s", nicID, ipv6.ProtocolNumber, linkLocalIPv6Addr1, err)
}
return s, clock
@@ -236,29 +237,33 @@ func createAndInjectIGMPPacket(e *channel.Endpoint, igmpType byte, maxRespTime b
// createAndInjectMLDPacket creates and injects an MLD packet with the
// specified fields.
-//
-// Note, the router alert option is not included in this packet.
-//
-// TODO(b/162198658): set the router alert option.
func createAndInjectMLDPacket(e *channel.Endpoint, mldType uint8, maxRespDelay byte, groupAddress tcpip.Address) {
- icmpSize := header.ICMPv6HeaderSize + header.MLDMinimumSize
- buf := buffer.NewView(header.IPv6MinimumSize + icmpSize)
+ extensionHeaders := header.IPv6ExtHdrSerializer{
+ header.IPv6SerializableHopByHopExtHdr{
+ &header.IPv6RouterAlertOption{Value: header.IPv6RouterAlertMLD},
+ },
+ }
+
+ extensionHeadersLength := extensionHeaders.Length()
+ payloadLength := extensionHeadersLength + header.ICMPv6HeaderSize + header.MLDMinimumSize
+ buf := buffer.NewView(header.IPv6MinimumSize + payloadLength)
ip := header.IPv6(buf)
ip.Encode(&header.IPv6Fields{
- PayloadLength: uint16(icmpSize),
+ PayloadLength: uint16(payloadLength),
HopLimit: header.MLDHopLimit,
TransportProtocol: header.ICMPv6ProtocolNumber,
- SrcAddr: header.IPv4Any,
+ SrcAddr: linkLocalIPv6Addr2,
DstAddr: header.IPv6AllNodesMulticastAddress,
+ ExtensionHeaders: extensionHeaders,
})
- icmp := header.ICMPv6(buf[header.IPv6MinimumSize:])
+ icmp := header.ICMPv6(ip.Payload()[extensionHeadersLength:])
icmp.SetType(header.ICMPv6Type(mldType))
mld := header.MLD(icmp.MessageBody())
mld.SetMaximumResponseDelay(uint16(maxRespDelay))
mld.SetMulticastAddress(groupAddress)
- icmp.SetChecksum(header.ICMPv6Checksum(icmp, header.IPv6Any, header.IPv6AllNodesMulticastAddress, buffer.VectorisedView{}))
+ icmp.SetChecksum(header.ICMPv6Checksum(icmp, linkLocalIPv6Addr2, header.IPv6AllNodesMulticastAddress, buffer.VectorisedView{}))
e.InjectInbound(ipv6.ProtocolNumber, &stack.PacketBuffer{
Data: buf.ToVectorisedView(),