Implement a TLV parsing loop for segment routing. The code is uniform
with other instances of TLV parsing loops in the stack (e.g. parsing of
Hop-by-Hop and Destination Options).

seg_validate_srh calls this function. Note, this fixes a bug in the
original parsing code that PAD1 was not supported.

Signed-off-by: Tom Herbert <t...@quantonium.net>
---
 include/net/seg6.h |  6 ++++++
 net/ipv6/seg6.c    | 60 +++++++++++++++++++++++++++++++-----------------------
 2 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/include/net/seg6.h b/include/net/seg6.h
index 8b2dc68..563d4a6 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;
@@ -62,6 +67,7 @@ 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_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);
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index 0c5479e..e461357 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -30,44 +30,52 @@
 #include <net/seg6_hmac.h>
 #endif
 
-bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
+bool __seg6_parse_srh(struct ipv6_sr_hdr *srh)
 {
-       int trailing;
-       unsigned int tlv_offset;
+       int len = ipv6_optlen((struct ipv6_opt_hdr *)srh);
+       unsigned char *opt = (unsigned char *)srh;
+       unsigned int off;
 
-       if (srh->type != IPV6_SRCRT_TYPE_4)
-               return false;
+       off = seg6_tlv_offset(srh);
+       len -= off;
 
-       if (((srh->hdrlen + 1) << 3) != len)
-               return false;
+       while (len > 0) {
+               struct sr6_tlv *tlv;
+               unsigned int optlen;
 
-       if (srh->segments_left > srh->first_segment)
-               return false;
+               switch (opt[off]) {
+               case SR6_TLV_PAD1:
+                       optlen = 1;
+                       break;
+               default:
+                       if (len < sizeof(*tlv))
+                               return false;
 
-       tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
+                       tlv = (struct sr6_tlv *)&opt[off];
+                       optlen = sizeof(*tlv) + tlv->len;
 
-       trailing = len - tlv_offset;
-       if (trailing < 0)
-               return false;
+                       break;
+               }
 
-       while (trailing) {
-               struct sr6_tlv *tlv;
-               unsigned int tlv_len;
+               off += optlen;
+               len -= optlen;
+       }
 
-               if (trailing < sizeof(*tlv))
-                       return false;
+       return !len;
+}
 
-               tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset);
-               tlv_len = sizeof(*tlv) + tlv->len;
+bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
+{
+       if (srh->type != IPV6_SRCRT_TYPE_4)
+               return false;
 
-               trailing -= tlv_len;
-               if (trailing < 0)
-                       return false;
+       if (ipv6_optlen((struct ipv6_opt_hdr *)srh) != len)
+               return false;
 
-               tlv_offset += tlv_len;
-       }
+       if (srh->segments_left > srh->first_segment)
+               return false;
 
-       return true;
+       return __seg6_parse_srh(srh);
 }
 
 static struct genl_family seg6_genl_family;
-- 
2.7.4

Reply via email to