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