Currently no flags are defined for segment routing in
draft-ietf-6man-segment-routing-header-19. Mark them as being
obsolete.

The HMAC flag is the only one used by the stack. This needs
additional consideration.

Rewrite sr_has_hmac in uapi/linux/seg6.h to properly parse a
segment routing header as opposed to relying on the now obsolete
code.

Implement seg6_find_hmac_tlv for internal stack use. That function
parses (via __seg6_parse_srh) a TLV list and returns the pointer to
an HMAC TLV if one exists. The parsing function also eliminates the
assumption in seg6_get_tlv_hmac that the HMAC TLV must be the first TLV.

Signed-off-by: Tom Herbert <t...@quantonium.net>
---
 include/net/seg6.h        | 12 +++++++++++-
 include/uapi/linux/seg6.h | 49 ++++++++++++++++++++++++++++++++++++++++-------
 net/ipv6/exthdrs.c        |  2 +-
 net/ipv6/seg6.c           | 12 ++++++++++--
 net/ipv6/seg6_hmac.c      |  8 +++-----
 net/ipv6/seg6_iptunnel.c  |  4 ++--
 6 files changed, 69 insertions(+), 18 deletions(-)

diff --git a/include/net/seg6.h b/include/net/seg6.h
index 563d4a6..47e7c90 100644
--- a/include/net/seg6.h
+++ b/include/net/seg6.h
@@ -17,6 +17,7 @@
 #include <linux/net.h>
 #include <linux/ipv6.h>
 #include <linux/seg6.h>
+#include <linux/seg6_hmac.h>
 #include <linux/rhashtable-types.h>
 
 static inline void update_csum_diff4(struct sk_buff *skb, __be32 from,
@@ -67,11 +68,20 @@ extern void seg6_iptunnel_exit(void);
 extern int seg6_local_init(void);
 extern void seg6_local_exit(void);
 
-extern bool __seg6_parse_srh(struct ipv6_sr_hdr *srh);
+extern bool __seg6_parse_srh(struct ipv6_sr_hdr *srh,
+                            struct sr6_tlv_hmac **hmacp);
 extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len);
 extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
                             int proto);
 extern int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh);
 extern int seg6_lookup_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,
                               u32 tbl_id);
+
+static inline struct sr6_tlv_hmac *seg6_find_hmac_tlv(struct ipv6_sr_hdr *srh)
+{
+       struct sr6_tlv_hmac *hmacp = NULL;
+
+       return __seg6_parse_srh(srh, &hmacp) ? hmacp : NULL;
+}
+
 #endif
diff --git a/include/uapi/linux/seg6.h b/include/uapi/linux/seg6.h
index 9117113..890420b0 100644
--- a/include/uapi/linux/seg6.h
+++ b/include/uapi/linux/seg6.h
@@ -33,11 +33,10 @@ struct ipv6_sr_hdr {
        struct in6_addr segments[0];
 };
 
-#define SR6_FLAG1_PROTECTED    (1 << 6)
-#define SR6_FLAG1_OAM          (1 << 5)
-#define SR6_FLAG1_ALERT                (1 << 4)
-#define SR6_FLAG1_HMAC         (1 << 3)
-
+#define SR6_FLAG1_PROTECTED    (1 << 6)        /* obsoleted */
+#define SR6_FLAG1_OAM          (1 << 5)        /* obsoleted */
+#define SR6_FLAG1_ALERT                (1 << 4)        /* obsoleted */
+#define SR6_FLAG1_HMAC         (1 << 3)        /* obsoleted */
 
 #define SR6_TLV_INGRESS                1       /* obsoleted */
 #define SR6_TLV_EGRESS         2       /* obsoleted */
@@ -47,12 +46,48 @@ struct ipv6_sr_hdr {
 #define SR6_TLV_PADDING                1
 #define SR6_TLV_HMAC           5
 
-#define sr_has_hmac(srh) ((srh)->flags & SR6_FLAG1_HMAC)
-
 struct sr6_tlv {
        __u8 type;
        __u8 len;
        __u8 data[0];
 };
 
+static inline bool __sr_has_hmac(struct ipv6_sr_hdr *srh)
+{
+       unsigned char *opt = (unsigned char *)srh;
+       int len = (srh->hdrlen + 1) << 8;
+       unsigned int off;
+
+       off = sizeof(*srh) + ((srh->first_segment + 1) << 4);
+       len -= off;
+
+       while (len > 0) {
+               struct sr6_tlv *tlv;
+               unsigned int optlen;
+
+               switch (opt[off]) {
+               case SR6_TLV_PAD1:
+                       optlen = 1;
+                       break;
+               case SR6_TLV_HMAC:
+                       return true;
+               default:
+                       if (len < sizeof(*tlv))
+                               return false;
+
+                       tlv = (struct sr6_tlv *)&opt[off];
+                       optlen = sizeof(*tlv) + tlv->len;
+
+                       break;
+               }
+
+               off += optlen;
+               len -= optlen;
+       }
+
+       return false;
+}
+
+#define sr_has_hmac(srh) __sr_has_hmac(srh)
+
 #endif
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 20291c2..112e2fd 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -922,7 +922,7 @@ static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto,
        }
 
 #ifdef CONFIG_IPV6_SEG6_HMAC
-       if (sr_has_hmac(sr_phdr)) {
+       if (seg6_find_hmac_tlv(sr_phdr)) {
                struct net *net = NULL;
 
                if (skb->dev)
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index e461357..1e782a6 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -30,7 +30,7 @@
 #include <net/seg6_hmac.h>
 #endif
 
-bool __seg6_parse_srh(struct ipv6_sr_hdr *srh)
+bool __seg6_parse_srh(struct ipv6_sr_hdr *srh, struct sr6_tlv_hmac **hmacp)
 {
        int len = ipv6_optlen((struct ipv6_opt_hdr *)srh);
        unsigned char *opt = (unsigned char *)srh;
@@ -39,6 +39,8 @@ bool __seg6_parse_srh(struct ipv6_sr_hdr *srh)
        off = seg6_tlv_offset(srh);
        len -= off;
 
+       *hmacp = NULL;
+
        while (len > 0) {
                struct sr6_tlv *tlv;
                unsigned int optlen;
@@ -47,6 +49,10 @@ bool __seg6_parse_srh(struct ipv6_sr_hdr *srh)
                case SR6_TLV_PAD1:
                        optlen = 1;
                        break;
+               case SR6_TLV_HMAC:
+                       if (!*hmacp)
+                               *hmacp = (struct sr6_tlv_hmac *)&opt[off];
+                       /* Fall through */
                default:
                        if (len < sizeof(*tlv))
                                return false;
@@ -66,6 +72,8 @@ bool __seg6_parse_srh(struct ipv6_sr_hdr *srh)
 
 bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
 {
+       struct sr6_tlv_hmac *hmacp;
+
        if (srh->type != IPV6_SRCRT_TYPE_4)
                return false;
 
@@ -75,7 +83,7 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
        if (srh->segments_left > srh->first_segment)
                return false;
 
-       return __seg6_parse_srh(srh);
+       return __seg6_parse_srh(srh, &hmacp);
 }
 
 static struct genl_family seg6_genl_family;
diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c
index 8546f94..92b398c 100644
--- a/net/ipv6/seg6_hmac.c
+++ b/net/ipv6/seg6_hmac.c
@@ -95,13 +95,11 @@ static struct sr6_tlv_hmac *seg6_get_tlv_hmac(struct 
ipv6_sr_hdr *srh)
        if (srh->hdrlen < (srh->first_segment + 1) * 2 + 5)
                return NULL;
 
-       if (!sr_has_hmac(srh))
+       tlv = seg6_find_hmac_tlv(srh);
+       if (!tlv)
                return NULL;
 
-       tlv = (struct sr6_tlv_hmac *)
-             ((char *)srh + ((srh->hdrlen + 1) << 3) - 40);
-
-       if (tlv->tlvhdr.type != SR6_TLV_HMAC || tlv->tlvhdr.len != 38)
+       if (tlv->tlvhdr.len != sizeof(*tlv) - 2)
                return NULL;
 
        return tlv;
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index 7a525fd..5344bee 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -161,7 +161,7 @@ int seg6_do_srh_encap(struct sk_buff *skb, struct 
ipv6_sr_hdr *osrh, int proto)
        set_tun_src(net, dst->dev, &hdr->daddr, &hdr->saddr);
 
 #ifdef CONFIG_IPV6_SEG6_HMAC
-       if (sr_has_hmac(isrh)) {
+       if (seg6_find_hmac_tlv(isrh)) {
                err = seg6_push_hmac(net, &hdr->saddr, isrh);
                if (unlikely(err))
                        return err;
@@ -211,7 +211,7 @@ int seg6_do_srh_inline(struct sk_buff *skb, struct 
ipv6_sr_hdr *osrh)
        hdr->daddr = isrh->segments[isrh->first_segment];
 
 #ifdef CONFIG_IPV6_SEG6_HMAC
-       if (sr_has_hmac(isrh)) {
+       if (seg6_find_hmac_tlv(isrh)) {
                struct net *net = dev_net(skb_dst(skb)->dev);
 
                err = seg6_push_hmac(net, &hdr->saddr, isrh);
-- 
2.7.4

Reply via email to