Validate MLD packets

Fixes #5490

PiperOrigin-RevId: 359401532
This commit is contained in:
Arthur Sfez
2021-02-24 16:56:19 -08:00
committed by gVisor bot
parent f5692f7dcc
commit 1d2975ffbe
6 changed files with 367 additions and 93 deletions
+24 -5
View File
@@ -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
}
+67 -37
View File
@@ -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 {
+43 -27
View File
@@ -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 {
+61 -6
View File
@@ -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
@@ -398,6 +399,42 @@ func TestReceiveIPv6ExtHdrs(t *testing.T) {
shouldAccept: true,
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) {
@@ -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
+152 -3
View File
@@ -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)
}
})
}
}
+20 -15
View File
@@ -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(),