mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user