From: Tom Herbert <tom@herbertland.com>
To: davem@davemloft.net, netdev@vger.kernel.org, simon.horman@netronome.com
Cc: Tom Herbert <tom@quantonium.net>, Tom Herbert <tom@herbertland.com>
Subject: [PATCH v6 net-next 4/9] ipeh: Generic TLV parser
Date: Fri, 20 Dec 2019 15:38:39 -0800 [thread overview]
Message-ID: <1576885124-14576-5-git-send-email-tom@herbertland.com> (raw)
In-Reply-To: <1576885124-14576-1-git-send-email-tom@herbertland.com>
From: Tom Herbert <tom@quantonium.net>
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 <tom@herbertland.com>
---
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
next prev parent reply other threads:[~2019-12-20 23:39 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-12-20 23:38 [PATCH v6 net-next 0/9] ipv6: Extension header infrastructure Tom Herbert
2019-12-20 23:38 ` [PATCH v6 net-next 1/9] ipeh: Fix destopts and hopopts counters on drop Tom Herbert
2019-12-22 16:20 ` Willem de Bruijn
2019-12-23 16:53 ` Tom Herbert
2019-12-23 18:52 ` Willem de Bruijn
2019-12-23 20:02 ` Tom Herbert
2019-12-20 23:38 ` [PATCH v6 net-next 2/9] ipeh: Create exthdrs_options.c and ipeh.h Tom Herbert
2019-12-20 23:38 ` [PATCH v6 net-next 3/9] ipeh: Move generic EH functions to exthdrs_common.c Tom Herbert
2019-12-20 23:38 ` Tom Herbert [this message]
2019-12-20 23:38 ` [PATCH v6 net-next 5/9] ipeh: Add callback to ipeh_parse_tlv to handle errors Tom Herbert
2019-12-20 23:38 ` [PATCH v6 net-next 6/9] ip6tlvs: Registration of TLV handlers and parameters Tom Herbert
2019-12-20 23:38 ` [PATCH v6 net-next 7/9] ip6tlvs: Add TX parameters Tom Herbert
2019-12-20 23:38 ` [PATCH v6 net-next 8/9] ip6tlvs: Add netlink interface Tom Herbert
2019-12-20 23:38 ` [PATCH v6 net-next 9/9] ip6tlvs: Validation of TX Destination and Hop-by-Hop options Tom Herbert
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1576885124-14576-5-git-send-email-tom@herbertland.com \
--to=tom@herbertland.com \
--cc=davem@davemloft.net \
--cc=netdev@vger.kernel.org \
--cc=simon.horman@netronome.com \
--cc=tom@quantonium.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.