diff options
-rw-r--r-- | pkg/tcpip/network/ipv6/icmp.go | 29 | ||||
-rw-r--r-- | pkg/tcpip/network/ipv6/ipv6.go | 70 |
2 files changed, 67 insertions, 32 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/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 { |