From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 33629C2D0C8 for ; Fri, 20 Dec 2019 23:39:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E4C7E2053B for ; Fri, 20 Dec 2019 23:39:33 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=herbertland-com.20150623.gappssmtp.com header.i=@herbertland-com.20150623.gappssmtp.com header.b="El9KClSj" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726750AbfLTXjc (ORCPT ); Fri, 20 Dec 2019 18:39:32 -0500 Received: from mail-pj1-f65.google.com ([209.85.216.65]:52475 "EHLO mail-pj1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726470AbfLTXjc (ORCPT ); Fri, 20 Dec 2019 18:39:32 -0500 Received: by mail-pj1-f65.google.com with SMTP id w23so4773467pjd.2 for ; Fri, 20 Dec 2019 15:39:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=herbertland-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=8bnCEFhTLD7HUdTgeeE3FhxQ9K8LBjdeLIzCTtaMk5A=; b=El9KClSjw2zrfjA+MjVtjrj9pLYiUCQ4trk5vVe02f1GgAO5rDZ8GzuLZ8p86SArVk wGeEM40AWUXwBSINRy56GAgJQ8I9vkiEueBxDc+zsp7oj3tW5aDsCad5sgGap+iohNY9 6ovW34qPR2HVEw1Mm3deUjPG9IQz1bFNFIH4bh4ppqOReSpXIE9Qd7ZeixnzbqYOAYU2 X8sHMTAuPw3mNgx3Z2abigL61P+DBICQh94yUzq2a7+iShCHNg41Bhv3+DK/0xmvoZcc v6QSThDOovpgnTzAD42+jYAjjHt4ioCUSgMxJOeXTpTBub5Pg0kfdbW6s/ZJx+EijlK0 IUyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=8bnCEFhTLD7HUdTgeeE3FhxQ9K8LBjdeLIzCTtaMk5A=; b=UHyYOjakU16eoJWX+qbdlvzgNJBM39F+XwyjlhS2I7j1SAU6e0r8EUyKrFQd1geSQU UhP9On85YUOZWisHE1ZWMlebKGacDQrwsInJa3NmDrxbEaCFtKxbQhPAnD6TNjE6ou7S S2wv9E2tzPUphGdI0OBb1qDtNdwwjpk4SUggGs/k7JkFphkKfc4kBZ71KOoeTUBGUcWt 4bsxbc/H95h/9GBr1X3UOYaubX82qRy3hQZmmjirKvCf8CVmdwxsTCJIWjgybm2r6qN3 fCTCL42FglER9M+YIbIy1tdVVBeyJDgZT54um33nlV8wBmzKKU1CMGjF2pfrxcj20346 c77g== X-Gm-Message-State: APjAAAWbZDFSM1pOp0iAYl6nFvy98MWo1PVTEcvkXyMGb0drdejjY5Y8 OG+vRczXocoL5PHlyQLeEXjBNKKdRkY= X-Google-Smtp-Source: APXvYqyOJ05dJ4W+sgDdzAx5pOCZpDyn2+7f4LRJQQMOoLU6Dec3IxfHaA8OP5ROJKVOfXxTGeGozg== X-Received: by 2002:a17:90a:e28e:: with SMTP id d14mr19796082pjz.56.1576885171250; Fri, 20 Dec 2019 15:39:31 -0800 (PST) Received: from localhost.localdomain (c-73-202-182-113.hsd1.ca.comcast.net. [73.202.182.113]) by smtp.gmail.com with ESMTPSA id 207sm14833555pfu.88.2019.12.20.15.39.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 20 Dec 2019 15:39:30 -0800 (PST) From: Tom Herbert To: davem@davemloft.net, netdev@vger.kernel.org, simon.horman@netronome.com Cc: Tom Herbert , Tom Herbert Subject: [PATCH v6 net-next 4/9] ipeh: Generic TLV parser Date: Fri, 20 Dec 2019 15:38:39 -0800 Message-Id: <1576885124-14576-5-git-send-email-tom@herbertland.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1576885124-14576-1-git-send-email-tom@herbertland.com> References: <1576885124-14576-1-git-send-email-tom@herbertland.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Tom Herbert Create a generic TLV parser. This will be used with various extension headers that carry options including Destination, Hop-by-Hop, Segment Routing TLVs, and other cases of simple stateless TLV parsing. Signed-off-by: Tom Herbert --- include/net/ipeh.h | 11 ++++ net/ipv6/exthdrs.c | 142 ++-------------------------------------------- net/ipv6/exthdrs_common.c | 137 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 136 deletions(-) diff --git a/include/net/ipeh.h b/include/net/ipeh.h index 3b24831..81f92f8 100644 --- a/include/net/ipeh.h +++ b/include/net/ipeh.h @@ -31,4 +31,15 @@ struct ipv6_txoptions *ipeh_renew_options(struct sock *sk, struct ipv6_txoptions *ipeh_fixup_options(struct ipv6_txoptions *opt_space, struct ipv6_txoptions *opt); +/* The generic TLV parser assumes that the type value of PAD1 is 0, and PADN + * is 1. This is true for IPv6 Destination and Hop-by-Hop Options. For Segment + * Routing TLVs, PAD1 is also 0, however PADN is 4 so the latter necessitates + * some change to the parser to support Segment Routing TLVs. + */ +#define IPEH_TLV_PAD1 0 +#define IPEH_TLV_PADN 1 + +bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb, + int max_count, int off, int len); + #endif /* _NET_IPEH_H */ diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index ea87017..b094efe 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -54,138 +54,6 @@ Generic functions *********************/ -/* An unknown option is detected, decide what to do */ - -static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff, - bool disallow_unknowns) -{ - if (disallow_unknowns) { - /* If unknown TLVs are disallowed by configuration - * then always silently drop packet. Note this also - * means no ICMP parameter problem is sent which - * could be a good property to mitigate a reflection DOS - * attack. - */ - - goto drop; - } - - switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) { - case 0: /* ignore */ - return true; - - case 1: /* drop packet */ - break; - - case 3: /* Send ICMP if not a multicast address and drop packet */ - /* Actually, it is redundant check. icmp_send - will recheck in any case. - */ - if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) - break; - /* fall through */ - case 2: /* send ICMP PARM PROB regardless and drop packet */ - icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff); - return false; - } - -drop: - kfree_skb(skb); - return false; -} - -/* Parse tlv encoded option header (hop-by-hop or destination) */ - -static bool ip6_parse_tlv(const struct tlvtype_proc *procs, - struct sk_buff *skb, - int max_count) -{ - int len = (skb_transport_header(skb)[1] + 1) << 3; - const unsigned char *nh = skb_network_header(skb); - int off = skb_network_header_len(skb); - const struct tlvtype_proc *curr; - bool disallow_unknowns = false; - int tlv_count = 0; - int padlen = 0; - - if (unlikely(max_count < 0)) { - disallow_unknowns = true; - max_count = -max_count; - } - - if (skb_transport_offset(skb) + len > skb_headlen(skb)) - goto bad; - - off += 2; - len -= 2; - - while (len > 0) { - int optlen = nh[off + 1] + 2; - int i; - - switch (nh[off]) { - case IPV6_TLV_PAD1: - optlen = 1; - padlen++; - if (padlen > 7) - goto bad; - break; - - case IPV6_TLV_PADN: - /* RFC 2460 states that the purpose of PadN is - * to align the containing header to multiples - * of 8. 7 is therefore the highest valid value. - * See also RFC 4942, Section 2.1.9.5. - */ - padlen += optlen; - if (padlen > 7) - goto bad; - /* RFC 4942 recommends receiving hosts to - * actively check PadN payload to contain - * only zeroes. - */ - for (i = 2; i < optlen; i++) { - if (nh[off + i] != 0) - goto bad; - } - break; - - default: /* Other TLV code so scan list */ - if (optlen > len) - goto bad; - - tlv_count++; - if (tlv_count > max_count) - goto bad; - - for (curr = procs; curr->type >= 0; curr++) { - if (curr->type == nh[off]) { - /* type specific length/alignment - checks will be performed in the - func(). */ - if (curr->func(skb, off) == false) - return false; - break; - } - } - if (curr->type < 0 && - !ip6_tlvopt_unknown(skb, off, disallow_unknowns)) - return false; - - padlen = 0; - break; - } - off += optlen; - len -= optlen; - } - - if (len == 0) - return true; -bad: - kfree_skb(skb); - return false; -} - static int ipv6_destopt_rcv(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); @@ -216,8 +84,9 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) dstbuf = opt->dst1; #endif - if (ip6_parse_tlv(tlvprocdestopt_lst, skb, - init_net.ipv6.sysctl.max_dst_opts_cnt)) { + if (ipeh_parse_tlv(tlvprocdestopt_lst, skb, + init_net.ipv6.sysctl.max_dst_opts_cnt, + 2, extlen - 2)) { skb->transport_header += extlen; opt = IP6CB(skb); #if IS_ENABLED(CONFIG_IPV6_MIP6) @@ -643,8 +512,9 @@ int ipv6_parse_hopopts(struct sk_buff *skb) goto fail_and_free; opt->flags |= IP6SKB_HOPBYHOP; - if (ip6_parse_tlv(tlvprochopopt_lst, skb, - init_net.ipv6.sysctl.max_hbh_opts_cnt)) { + if (ipeh_parse_tlv(tlvprochopopt_lst, skb, + init_net.ipv6.sysctl.max_hbh_opts_cnt, + 2, extlen - 2)) { skb->transport_header += extlen; opt = IP6CB(skb); opt->nhoff = sizeof(struct ipv6hdr); diff --git a/net/ipv6/exthdrs_common.c b/net/ipv6/exthdrs_common.c index 2c68184..d0c4ec3 100644 --- a/net/ipv6/exthdrs_common.c +++ b/net/ipv6/exthdrs_common.c @@ -142,3 +142,140 @@ struct ipv6_txoptions *ipeh_fixup_options(struct ipv6_txoptions *opt_space, return opt; } EXPORT_SYMBOL_GPL(ipeh_fixup_options); + +/* An unknown option is detected, decide what to do */ + +static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff, + bool disallow_unknowns) +{ + if (disallow_unknowns) { + /* If unknown TLVs are disallowed by configuration + * then always silently drop packet. Note this also + * means no ICMP parameter problem is sent which + * could be a good property to mitigate a reflection DOS + * attack. + */ + + goto drop; + } + + switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) { + case 0: /* ignore */ + return true; + + case 1: /* drop packet */ + break; + + case 3: /* Send ICMP if not a multicast address and drop packet */ + /* Actually, it is redundant check. icmp_send + * will recheck in any case. + */ + if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) + break; + /* fall through */ + case 2: /* send ICMP PARM PROB regardless and drop packet */ + icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff); + return false; + } + +drop: + kfree_skb(skb); + return false; +} + +/* Generic extension header TLV parser + * + * Arguments: + * - skb_transport_header points to the extension header containing options + * - off is offset from skb_transport_header where first TLV is + * - len is length of TLV block + */ +bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb, + int max_count, int off, int len) +{ + const unsigned char *nh = skb_network_header(skb); + const struct tlvtype_proc *curr; + bool disallow_unknowns = false; + int tlv_count = 0; + int padlen = 0; + + if (unlikely(max_count < 0)) { + disallow_unknowns = true; + max_count = -max_count; + } + + if (skb_transport_offset(skb) + off + len > skb_headlen(skb)) + goto bad; + + /* ops function based offset on network header */ + off += skb_network_header_len(skb); + + while (len > 0) { + int optlen = nh[off + 1] + 2; + int i; + + switch (nh[off]) { + case IPEH_TLV_PAD1: + optlen = 1; + padlen++; + if (padlen > 7) + goto bad; + break; + + case IPEH_TLV_PADN: + /* RFC 2460 states that the purpose of PadN is + * to align the containing header to multiples + * of 8. 7 is therefore the highest valid value. + * See also RFC 4942, Section 2.1.9.5. + */ + padlen += optlen; + if (padlen > 7) + goto bad; + + /* RFC 4942 recommends receiving hosts to + * actively check PadN payload to contain + * only zeroes. + */ + for (i = 2; i < optlen; i++) { + if (nh[off + i] != 0) + goto bad; + } + break; + + default: /* Other TLV code so scan list */ + if (optlen > len) + goto bad; + + tlv_count++; + if (tlv_count > max_count) + goto bad; + + for (curr = procs; curr->type >= 0; curr++) { + if (curr->type == nh[off]) { + /* type specific length/alignment + * checks will be performed in the + * func(). + */ + if (curr->func(skb, off) == false) + return false; + break; + } + } + if (curr->type < 0 && + !ip6_tlvopt_unknown(skb, off, disallow_unknowns)) + return false; + + padlen = 0; + break; + } + off += optlen; + len -= optlen; + } + + if (len == 0) + return true; +bad: + kfree_skb(skb); + return false; +} +EXPORT_SYMBOL(ipeh_parse_tlv); -- 2.7.4