[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1559933708-13947-6-git-send-email-tom@quantonium.net>
Date: Fri, 7 Jun 2019 11:55:08 -0700
From: Tom Herbert <tom@...bertland.com>
To: davem@...emloft.net, netdev@...r.kernel.org, dlebrun@...gle.com
Cc: Tom Herbert <tom@...ntonium.net>
Subject: [RFC v2 PATCH 5/5] seg6: Leverage ip6_parse_tlv
Call ip6_parse_tlv from segment routing receive function to properly
parse TLVs and to leverage to existing implementation that already
parses Destination and Hop-by-Hop Options. This includes applying
the denial of service mitigations and limits to processing segment
routing TLVs.
Signed-off-by: Tom Herbert <tom@...ntonium.net>
---
include/net/seg6.h | 5 +++++
include/net/seg6_hmac.h | 2 +-
net/ipv6/exthdrs.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++---
net/ipv6/seg6_hmac.c | 16 +++-------------
net/ipv6/seg6_local.c | 21 ++++++++++++++------
5 files changed, 72 insertions(+), 23 deletions(-)
diff --git a/include/net/seg6.h b/include/net/seg6.h
index 8b2dc68..b7d8a94 100644
--- a/include/net/seg6.h
+++ b/include/net/seg6.h
@@ -38,6 +38,11 @@ static inline void update_csum_diff16(struct sk_buff *skb, __be32 *from,
skb->csum = ~csum_partial((char *)diff, sizeof(diff), ~skb->csum);
}
+static inline unsigned int seg6_tlv_offset(struct ipv6_sr_hdr *srh)
+{
+ return sizeof(*srh) + ((srh->first_segment + 1) << 4);
+}
+
struct seg6_pernet_data {
struct mutex lock;
struct in6_addr __rcu *tun_src;
diff --git a/include/net/seg6_hmac.h b/include/net/seg6_hmac.h
index 7fda469..6ad33e2 100644
--- a/include/net/seg6_hmac.h
+++ b/include/net/seg6_hmac.h
@@ -53,7 +53,7 @@ extern int seg6_hmac_info_add(struct net *net, u32 key,
extern int seg6_hmac_info_del(struct net *net, u32 key);
extern int seg6_push_hmac(struct net *net, struct in6_addr *saddr,
struct ipv6_sr_hdr *srh);
-extern bool seg6_hmac_validate_skb(struct sk_buff *skb);
+extern bool seg6_hmac_validate_skb(struct sk_buff *skb, int optoff);
extern int seg6_hmac_init(void);
extern void seg6_hmac_exit(void);
extern int seg6_hmac_net_init(struct net *net);
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index a394d20..7d14c0d 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -112,7 +112,8 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff,
return false;
}
-/* Parse tlv encoded option header (hop-by-hop or destination)
+/* Parse tlv encoded option header (hop-by-hop, destination, or
+ * segment routing header)
*
* Arguments:
* procs - TLV proc structure
@@ -365,14 +366,42 @@ static void seg6_update_csum(struct sk_buff *skb)
(__be32 *)addr);
}
+static bool seg6_recv_hmac(struct sk_buff *skb, int optoff)
+{
+ if (!seg6_hmac_validate_skb(skb, optoff)) {
+ kfree_skb(skb);
+ return false;
+ }
+
+ return true;
+}
+
+static const struct tlvtype_proc tlvprocsrhopt_lst[] = {
+#ifdef CONFIG_IPV6_SEG6_HMAC
+ {
+ .type = SR6_TLV_HMAC,
+ .func = seg6_recv_hmac,
+ },
+#endif
+ {-1, NULL}
+};
+
+static bool seg6_srhopt_unknown(struct sk_buff *skb, int optoff,
+ bool disallow_unknowns)
+{
+ /* Unknown segment routing header options are ignored */
+
+ return !disallow_unknowns;
+}
+
static int ipv6_srh_rcv(struct sk_buff *skb)
{
struct inet6_skb_parm *opt = IP6CB(skb);
struct net *net = dev_net(skb->dev);
+ int accept_seg6, tlvoff, tlvlen;
struct ipv6_sr_hdr *hdr;
struct inet6_dev *idev;
struct in6_addr *addr;
- int accept_seg6;
hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
@@ -387,8 +416,24 @@ static int ipv6_srh_rcv(struct sk_buff *skb)
return -1;
}
+ tlvoff = seg6_tlv_offset(hdr);
+ tlvlen = ipv6_optlen((struct ipv6_opt_hdr *)hdr) - tlvoff;
+
+ if (tlvlen) {
+ if (tlvlen > net->ipv6.sysctl.max_srh_opts_len) {
+ kfree_skb(skb);
+ return -1;
+ }
+
+ if (!ip6_parse_tlv(tlvprocsrhopt_lst, skb,
+ init_net.ipv6.sysctl.max_srh_opts_cnt,
+ tlvoff, tlvlen, seg6_srhopt_unknown))
+ return -1;
+ }
+
#ifdef CONFIG_IPV6_SEG6_HMAC
- if (!seg6_hmac_validate_skb(skb)) {
+ if (idev->cnf.seg6_require_hmac > 0 && !sr_has_hmac(hdr)) {
+ /* mandatory check but no HMAC tlv */
kfree_skb(skb);
return -1;
}
diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c
index 8546f94..18f82f2 100644
--- a/net/ipv6/seg6_hmac.c
+++ b/net/ipv6/seg6_hmac.c
@@ -240,7 +240,7 @@ EXPORT_SYMBOL(seg6_hmac_compute);
*
* called with rcu_read_lock()
*/
-bool seg6_hmac_validate_skb(struct sk_buff *skb)
+bool seg6_hmac_validate_skb(struct sk_buff *skb, int optoff)
{
u8 hmac_output[SEG6_HMAC_FIELD_LEN];
struct net *net = dev_net(skb->dev);
@@ -251,23 +251,13 @@ bool seg6_hmac_validate_skb(struct sk_buff *skb)
idev = __in6_dev_get(skb->dev);
- srh = (struct ipv6_sr_hdr *)skb_transport_header(skb);
-
- tlv = seg6_get_tlv_hmac(srh);
-
- /* mandatory check but no tlv */
- if (idev->cnf.seg6_require_hmac > 0 && !tlv)
- return false;
-
/* no check */
if (idev->cnf.seg6_require_hmac < 0)
return true;
- /* check only if present */
- if (idev->cnf.seg6_require_hmac == 0 && !tlv)
- return true;
+ srh = (struct ipv6_sr_hdr *)skb_transport_header(skb);
- /* now, seg6_require_hmac >= 0 && tlv */
+ tlv = (struct sr6_tlv_hmac *)(skb_network_header(skb) + optoff);
hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid));
if (!hinfo)
diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c
index 78155fd..d486ed8 100644
--- a/net/ipv6/seg6_local.c
+++ b/net/ipv6/seg6_local.c
@@ -92,6 +92,19 @@ static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb)
return srh;
}
+static bool seg6_local_hmac_validate_skb(struct sk_buff *skb,
+ struct ipv6_sr_hdr *srh)
+{
+#ifdef CONFIG_IPV6_SEG6_HMAC
+ int off = sr_hmac_offset(srh);
+
+ return off ? seg6_hmac_validate_skb(skb, off) :
+ (__in6_dev_get(skb->dev)->cnf.seg6_require_hmac <= 0);
+#else
+ return true;
+#endif
+}
+
static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb)
{
struct ipv6_sr_hdr *srh;
@@ -103,10 +116,8 @@ static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb)
if (srh->segments_left == 0)
return NULL;
-#ifdef CONFIG_IPV6_SEG6_HMAC
- if (!seg6_hmac_validate_skb(skb))
+ if (!seg6_local_hmac_validate_skb(skb, srh))
return NULL;
-#endif
return srh;
}
@@ -120,10 +131,8 @@ static bool decap_and_validate(struct sk_buff *skb, int proto)
if (srh && srh->segments_left > 0)
return false;
-#ifdef CONFIG_IPV6_SEG6_HMAC
- if (srh && !seg6_hmac_validate_skb(skb))
+ if (srh && !seg6_local_hmac_validate_skb(skb, srh))
return false;
-#endif
if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0)
return false;
--
2.7.4
Powered by blists - more mailing lists