Add parameters to ip6_parse_tlv that will allow leveraging the function for parsing segment routing TLVs. The new parameters are offset of TLVs, length of the TLV block, and a function that is called in the case of an unrecognized option.
Signed-off-by: Tom Herbert <t...@quantonium.net> --- net/ipv6/exthdrs.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 20291c2..a394d20 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -112,15 +112,26 @@ 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 or destination) + * + * Arguments: + * procs - TLV proc structure + * skb - skbuff containing TLVs + * max_count - absolute value is maximum nuber of TLVs. If less than zero + * then unknown TLVs are disallowed regardless of disposition + * indicated by TLV type + * off - offset of first TLV relative to the first byte of the extension + * header which is transport header of the skb + * len - length of TLV block + * unknown_opt - function called when unknown option is encountered + */ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, - struct sk_buff *skb, - int max_count) + struct sk_buff *skb, int max_count, int off, int len, + bool (*unknown_opt)(struct sk_buff *skb, int optoff, + bool disallow_unknowns)) { - int len = (skb_transport_header(skb)[1] + 1) << 3; const unsigned char *nh = skb_network_header(skb); - int off = skb_network_header_len(skb); const struct tlvtype_proc *curr; bool disallow_unknowns = false; int tlv_count = 0; @@ -131,11 +142,11 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, max_count = -max_count; } - if (skb_transport_offset(skb) + len > skb_headlen(skb)) + if (skb_transport_offset(skb) + off + len > skb_headlen(skb)) goto bad; - off += 2; - len -= 2; + /* Offset relative to network header for parse loop */ + off += skb_network_header_len(skb); while (len > 0) { int optlen = nh[off + 1] + 2; @@ -187,7 +198,7 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, } } if (curr->type < 0 && - !ip6_tlvopt_unknown(skb, off, disallow_unknowns)) + !unknown_opt(skb, off, disallow_unknowns)) return false; padlen = 0; @@ -309,7 +320,8 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) #endif if (ip6_parse_tlv(tlvprocdestopt_lst, skb, - init_net.ipv6.sysctl.max_dst_opts_cnt)) { + init_net.ipv6.sysctl.max_dst_opts_cnt, + 2, extlen - 2, ip6_tlvopt_unknown)) { skb->transport_header += extlen; opt = IP6CB(skb); #if IS_ENABLED(CONFIG_IPV6_MIP6) @@ -848,7 +860,8 @@ int ipv6_parse_hopopts(struct sk_buff *skb) opt->flags |= IP6SKB_HOPBYHOP; if (ip6_parse_tlv(tlvprochopopt_lst, skb, - init_net.ipv6.sysctl.max_hbh_opts_cnt)) { + init_net.ipv6.sysctl.max_hbh_opts_cnt, + 2, extlen - 2, ip6_tlvopt_unknown)) { skb->transport_header += extlen; opt = IP6CB(skb); opt->nhoff = sizeof(struct ipv6hdr); -- 2.7.4