Define constants and add support to send ICMPv6 Parameter Problem errors as defined in draft-ietf-6man-icmp-limits-02.
ICMPV6_TOOBIG_OPTION is sent if a packet exceeding the padding limit is received (more than seven consecutive bytes of padding), ICMPV6_TOOMANY_OPTIONS is sent if a packet is received and HBH option count exceeds ipv6.sysctl.max_hbh_opts_cnt or DO option count exceeds ipv6.sysctl.max_dst_opts_cnt. ICMPV6_EXTHDR_TOOBIG is sent if length of HBH EH exceeds ipv6.sysctl.max_hbh_opts_len or length of DO EH exceeds ipv6.sysctl.max_dst_opts_len. Additionally, when packets are dropped in the above cases bump IPSTATS_MIB_INHDRERRORS. Signed-off-by: Tom Herbert <t...@quantonium.net> --- include/uapi/linux/icmpv6.h | 6 ++++++ net/ipv6/exthdrs.c | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/include/uapi/linux/icmpv6.h b/include/uapi/linux/icmpv6.h index 2622b5a..966279b 100644 --- a/include/uapi/linux/icmpv6.h +++ b/include/uapi/linux/icmpv6.h @@ -124,6 +124,7 @@ struct icmp6hdr { #define ICMPV6_PORT_UNREACH 4 #define ICMPV6_POLICY_FAIL 5 #define ICMPV6_REJECT_ROUTE 6 +#define ICMPV6_SRCRT_ERR 7 /* * Codes for Time Exceeded @@ -137,6 +138,11 @@ struct icmp6hdr { #define ICMPV6_HDR_FIELD 0 #define ICMPV6_UNK_NEXTHDR 1 #define ICMPV6_UNK_OPTION 2 +#define ICMPV6_FIRST_FRAG_INCOMP 3 +#define ICMPV6_EXTHDR_TOOBIG 4 +#define ICMPV6_EXTHDR_CHAINLONG 5 +#define ICMPV6_TOOMANY_OPTIONS 6 +#define ICMPV6_TOOBIG_OPTION 7 /* * constants for (set|get)sockopt diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 20291c2..ed0e4f5 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -145,8 +145,11 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, case IPV6_TLV_PAD1: optlen = 1; padlen++; - if (padlen > 7) + if (padlen > 7) { + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_TOOBIG_OPTION, off); goto bad; + } break; case IPV6_TLV_PADN: @@ -156,8 +159,11 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, * See also RFC 4942, Section 2.1.9.5. */ padlen += optlen; - if (padlen > 7) + if (padlen > 7) { + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_TOOBIG_OPTION, off); goto bad; + } /* RFC 4942 recommends receiving hosts to * actively check PadN payload to contain * only zeroes. @@ -173,8 +179,11 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, goto bad; tlv_count++; - if (tlv_count > max_count) + if (tlv_count > max_count) { + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_TOOMANY_OPTIONS, off); goto bad; + } for (curr = procs; curr->type >= 0; curr++) { if (curr->type == nh[off]) { @@ -200,6 +209,8 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, if (len == 0) return true; bad: + __IP6_INC_STATS(dev_net(skb->dev), __in6_dev_get(skb->dev), + IPSTATS_MIB_INHDRERRORS); kfree_skb(skb); return false; } @@ -300,8 +311,15 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) } extlen = (skb_transport_header(skb)[1] + 1) << 3; - if (extlen > net->ipv6.sysctl.max_dst_opts_len) + if (extlen > net->ipv6.sysctl.max_dst_opts_len) { + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_EXTHDR_TOOBIG, + skb_network_header_len(skb) + + net->ipv6.sysctl.max_dst_opts_len); + __IP6_INC_STATS(dev_net(dst->dev), idev, + IPSTATS_MIB_INHDRERRORS); goto fail_and_free; + } opt->lastopt = opt->dst1 = skb_network_header_len(skb); #if IS_ENABLED(CONFIG_IPV6_MIP6) @@ -843,8 +861,15 @@ int ipv6_parse_hopopts(struct sk_buff *skb) } extlen = (skb_transport_header(skb)[1] + 1) << 3; - if (extlen > net->ipv6.sysctl.max_hbh_opts_len) + if (extlen > net->ipv6.sysctl.max_hbh_opts_len) { + __IP6_INC_STATS(net, __in6_dev_get(skb->dev), + IPSTATS_MIB_INHDRERRORS); + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_EXTHDR_TOOBIG, + skb_network_header_len(skb) + + net->ipv6.sysctl.max_hbh_opts_len); goto fail_and_free; + } opt->flags |= IP6SKB_HOPBYHOP; if (ip6_parse_tlv(tlvprochopopt_lst, skb, -- 2.7.4