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

Reply via email to