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