Create exthdrs_options.c to hold code related to Hop-by-Hop and
Destination extension header options. Move related functions in
exthdrs.c to the new file.
---
 include/net/ipv6.h         |   8 ++
 net/ipv6/Makefile          |   2 +-
 net/ipv6/exthdrs.c         | 342 --------------------------------------------
 net/ipv6/exthdrs_options.c | 346 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 355 insertions(+), 343 deletions(-)
 create mode 100644 net/ipv6/exthdrs_options.c

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index daf8086..8abdcdb 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -379,6 +379,14 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
 struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
                                          struct ipv6_txoptions *opt);
 
+struct tlvtype_proc {
+       int     type;
+       bool    (*func)(struct sk_buff *skb, int offset);
+};
+
+extern const struct tlvtype_proc tlvprocdestopt_lst[];
+extern const struct tlvtype_proc tlvprochopopt_lst[];
+
 bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb,
                       const struct inet6_skb_parm *opt);
 struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index e0026fa..72bd775 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -10,7 +10,7 @@ ipv6-objs :=  af_inet6.o anycast.o ip6_output.o ip6_input.o 
addrconf.o \
                route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
                raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
                exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
-               udp_offload.o seg6.o fib6_notifier.o
+               udp_offload.o seg6.o fib6_notifier.o exthdrs_options.o
 
 ipv6-offload :=        ip6_offload.o tcpv6_offload.o exthdrs_offload.o
 
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 20291c2..6dbacf1 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -43,7 +43,6 @@
 #include <net/ndisc.h>
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
-#include <net/calipso.h>
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
 #include <net/xfrm.h>
 #endif
@@ -55,19 +54,6 @@
 
 #include <linux/uaccess.h>
 
-/*
- *     Parsing tlv encoded headers.
- *
- *     Parsing function "func" returns true, if parsing succeed
- *     and false, if it failed.
- *     It MUST NOT touch skb->h.
- */
-
-struct tlvtype_proc {
-       int     type;
-       bool    (*func)(struct sk_buff *skb, int offset);
-};
-
 /*********************
   Generic functions
  *********************/
@@ -204,80 +190,6 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
        return false;
 }
 
-/*****************************
-  Destination options header.
- *****************************/
-
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
-static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
-{
-       struct ipv6_destopt_hao *hao;
-       struct inet6_skb_parm *opt = IP6CB(skb);
-       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
-       int ret;
-
-       if (opt->dsthao) {
-               net_dbg_ratelimited("hao duplicated\n");
-               goto discard;
-       }
-       opt->dsthao = opt->dst1;
-       opt->dst1 = 0;
-
-       hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
-
-       if (hao->length != 16) {
-               net_dbg_ratelimited("hao invalid option length = %d\n",
-                                   hao->length);
-               goto discard;
-       }
-
-       if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
-               net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
-                                   &hao->addr);
-               goto discard;
-       }
-
-       ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
-                              (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
-       if (unlikely(ret < 0))
-               goto discard;
-
-       if (skb_cloned(skb)) {
-               if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
-                       goto discard;
-
-               /* update all variable using below by copied skbuff */
-               hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
-                                                 optoff);
-               ipv6h = ipv6_hdr(skb);
-       }
-
-       if (skb->ip_summed == CHECKSUM_COMPLETE)
-               skb->ip_summed = CHECKSUM_NONE;
-
-       swap(ipv6h->saddr, hao->addr);
-
-       if (skb->tstamp == 0)
-               __net_timestamp(skb);
-
-       return true;
-
- discard:
-       kfree_skb(skb);
-       return false;
-}
-#endif
-
-static const struct tlvtype_proc tlvprocdestopt_lst[] = {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
-       {
-               .type   = IPV6_TLV_HAO,
-               .func   = ipv6_dest_hao,
-       },
-#endif
-       {-1,                    NULL}
-};
-
 static int ipv6_destopt_rcv(struct sk_buff *skb)
 {
        struct inet6_dev *idev = __in6_dev_get(skb->dev);
@@ -706,122 +618,6 @@ void ipv6_exthdrs_exit(void)
        inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
 }
 
-/**********************************
-  Hop-by-hop options.
- **********************************/
-
-/*
- * Note: we cannot rely on skb_dst(skb) before we assign it in 
ip6_route_input().
- */
-static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
-{
-       return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : 
__in6_dev_get(skb->dev);
-}
-
-static inline struct net *ipv6_skb_net(struct sk_buff *skb)
-{
-       return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
-}
-
-/* Router Alert as of RFC 2711 */
-
-static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
-{
-       const unsigned char *nh = skb_network_header(skb);
-
-       if (nh[optoff + 1] == 2) {
-               IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
-               memcpy(&IP6CB(skb)->ra, nh + optoff + 2, 
sizeof(IP6CB(skb)->ra));
-               return true;
-       }
-       net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n",
-                           nh[optoff + 1]);
-       kfree_skb(skb);
-       return false;
-}
-
-/* Jumbo payload */
-
-static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
-{
-       const unsigned char *nh = skb_network_header(skb);
-       struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
-       struct net *net = ipv6_skb_net(skb);
-       u32 pkt_len;
-
-       if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
-               net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt 
length/alignment %d\n",
-                                   nh[optoff+1]);
-               __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
-               goto drop;
-       }
-
-       pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
-       if (pkt_len <= IPV6_MAXPLEN) {
-               __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
-               icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
-               return false;
-       }
-       if (ipv6_hdr(skb)->payload_len) {
-               __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
-               icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
-               return false;
-       }
-
-       if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
-               __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS);
-               goto drop;
-       }
-
-       if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
-               goto drop;
-
-       IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM;
-       return true;
-
-drop:
-       kfree_skb(skb);
-       return false;
-}
-
-/* CALIPSO RFC 5570 */
-
-static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
-{
-       const unsigned char *nh = skb_network_header(skb);
-
-       if (nh[optoff + 1] < 8)
-               goto drop;
-
-       if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
-               goto drop;
-
-       if (!calipso_validate(skb, nh + optoff))
-               goto drop;
-
-       return true;
-
-drop:
-       kfree_skb(skb);
-       return false;
-}
-
-static const struct tlvtype_proc tlvprochopopt_lst[] = {
-       {
-               .type   = IPV6_TLV_ROUTERALERT,
-               .func   = ipv6_hop_ra,
-       },
-       {
-               .type   = IPV6_TLV_JUMBO,
-               .func   = ipv6_hop_jumbo,
-       },
-       {
-               .type   = IPV6_TLV_CALIPSO,
-               .func   = ipv6_hop_calipso,
-       },
-       { -1, }
-};
-
 int ipv6_parse_hopopts(struct sk_buff *skb)
 {
        struct inet6_skb_parm *opt = IP6CB(skb);
@@ -992,144 +788,6 @@ void ipv6_push_frag_opts(struct sk_buff *skb, struct 
ipv6_txoptions *opt, u8 *pr
 }
 EXPORT_SYMBOL(ipv6_push_frag_opts);
 
-struct ipv6_txoptions *
-ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
-{
-       struct ipv6_txoptions *opt2;
-
-       opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
-       if (opt2) {
-               long dif = (char *)opt2 - (char *)opt;
-               memcpy(opt2, opt, opt->tot_len);
-               if (opt2->hopopt)
-                       *((char **)&opt2->hopopt) += dif;
-               if (opt2->dst0opt)
-                       *((char **)&opt2->dst0opt) += dif;
-               if (opt2->dst1opt)
-                       *((char **)&opt2->dst1opt) += dif;
-               if (opt2->srcrt)
-                       *((char **)&opt2->srcrt) += dif;
-               refcount_set(&opt2->refcnt, 1);
-       }
-       return opt2;
-}
-EXPORT_SYMBOL_GPL(ipv6_dup_options);
-
-static void ipv6_renew_option(int renewtype,
-                             struct ipv6_opt_hdr **dest,
-                             struct ipv6_opt_hdr *old,
-                             struct ipv6_opt_hdr *new,
-                             int newtype, char **p)
-{
-       struct ipv6_opt_hdr *src;
-
-       src = (renewtype == newtype ? new : old);
-       if (!src)
-               return;
-
-       memcpy(*p, src, ipv6_optlen(src));
-       *dest = (struct ipv6_opt_hdr *)*p;
-       *p += CMSG_ALIGN(ipv6_optlen(*dest));
-}
-
-/**
- * ipv6_renew_options - replace a specific ext hdr with a new one.
- *
- * @sk: sock from which to allocate memory
- * @opt: original options
- * @newtype: option type to replace in @opt
- * @newopt: new option of type @newtype to replace (user-mem)
- * @newoptlen: length of @newopt
- *
- * Returns a new set of options which is a copy of @opt with the
- * option type @newtype replaced with @newopt.
- *
- * @opt may be NULL, in which case a new set of options is returned
- * containing just @newopt.
- *
- * @newopt may be NULL, in which case the specified option type is
- * not copied into the new set of options.
- *
- * The new set of options is allocated from the socket option memory
- * buffer of @sk.
- */
-struct ipv6_txoptions *
-ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
-                  int newtype, struct ipv6_opt_hdr *newopt)
-{
-       int tot_len = 0;
-       char *p;
-       struct ipv6_txoptions *opt2;
-
-       if (opt) {
-               if (newtype != IPV6_HOPOPTS && opt->hopopt)
-                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
-               if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
-                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
-               if (newtype != IPV6_RTHDR && opt->srcrt)
-                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
-               if (newtype != IPV6_DSTOPTS && opt->dst1opt)
-                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
-       }
-
-       if (newopt)
-               tot_len += CMSG_ALIGN(ipv6_optlen(newopt));
-
-       if (!tot_len)
-               return NULL;
-
-       tot_len += sizeof(*opt2);
-       opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
-       if (!opt2)
-               return ERR_PTR(-ENOBUFS);
-
-       memset(opt2, 0, tot_len);
-       refcount_set(&opt2->refcnt, 1);
-       opt2->tot_len = tot_len;
-       p = (char *)(opt2 + 1);
-
-       ipv6_renew_option(IPV6_HOPOPTS, &opt2->hopopt,
-                         (opt ? opt->hopopt : NULL),
-                         newopt, newtype, &p);
-       ipv6_renew_option(IPV6_RTHDRDSTOPTS, &opt2->dst0opt,
-                         (opt ? opt->dst0opt : NULL),
-                         newopt, newtype, &p);
-       ipv6_renew_option(IPV6_RTHDR,
-                         (struct ipv6_opt_hdr **)&opt2->srcrt,
-                         (opt ? (struct ipv6_opt_hdr *)opt->srcrt : NULL),
-                         newopt, newtype, &p);
-       ipv6_renew_option(IPV6_DSTOPTS, &opt2->dst1opt,
-                         (opt ? opt->dst1opt : NULL),
-                         newopt, newtype, &p);
-
-       opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
-                         (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
-                         (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
-       opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
-
-       return opt2;
-}
-
-struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
-                                         struct ipv6_txoptions *opt)
-{
-       /*
-        * ignore the dest before srcrt unless srcrt is being included.
-        * --yoshfuji
-        */
-       if (opt && opt->dst0opt && !opt->srcrt) {
-               if (opt_space != opt) {
-                       memcpy(opt_space, opt, sizeof(*opt_space));
-                       opt = opt_space;
-               }
-               opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
-               opt->dst0opt = NULL;
-       }
-
-       return opt;
-}
-EXPORT_SYMBOL_GPL(ipv6_fixup_options);
-
 /**
  * fl6_update_dst - update flowi destination address with info given
  *                  by srcrt option, if any.
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
new file mode 100644
index 0000000..70266a6
--- /dev/null
+++ b/net/ipv6/exthdrs_options.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/errno.h>
+#include <linux/in6.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+#include <net/calipso.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+#include <net/xfrm.h>
+#endif
+
+/*     Parsing tlv encoded headers.
+ *
+ *     Parsing function "func" returns true, if parsing succeed
+ *     and false, if it failed.
+ *     It MUST NOT touch skb->h.
+ */
+
+struct ipv6_txoptions *
+ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
+{
+       struct ipv6_txoptions *opt2;
+
+       opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
+       if (opt2) {
+               long dif = (char *)opt2 - (char *)opt;
+
+               memcpy(opt2, opt, opt->tot_len);
+               if (opt2->hopopt)
+                       *((char **)&opt2->hopopt) += dif;
+               if (opt2->dst0opt)
+                       *((char **)&opt2->dst0opt) += dif;
+               if (opt2->dst1opt)
+                       *((char **)&opt2->dst1opt) += dif;
+               if (opt2->srcrt)
+                       *((char **)&opt2->srcrt) += dif;
+               refcount_set(&opt2->refcnt, 1);
+       }
+       return opt2;
+}
+EXPORT_SYMBOL_GPL(ipv6_dup_options);
+
+static void ipv6_renew_option(int renewtype,
+                             struct ipv6_opt_hdr **dest,
+                             struct ipv6_opt_hdr *old,
+                             struct ipv6_opt_hdr *new,
+                             int newtype, char **p)
+{
+       struct ipv6_opt_hdr *src;
+
+       src = (renewtype == newtype ? new : old);
+       if (!src)
+               return;
+
+       memcpy(*p, src, ipv6_optlen(src));
+       *dest = (struct ipv6_opt_hdr *)*p;
+       *p += CMSG_ALIGN(ipv6_optlen(*dest));
+}
+
+/**
+ * ipv6_renew_options - replace a specific ext hdr with a new one.
+ *
+ * @sk: sock from which to allocate memory
+ * @opt: original options
+ * @newtype: option type to replace in @opt
+ * @newopt: new option of type @newtype to replace (user-mem)
+ * @newoptlen: length of @newopt
+ *
+ * Returns a new set of options which is a copy of @opt with the
+ * option type @newtype replaced with @newopt.
+ *
+ * @opt may be NULL, in which case a new set of options is returned
+ * containing just @newopt.
+ *
+ * @newopt may be NULL, in which case the specified option type is
+ * not copied into the new set of options.
+ *
+ * The new set of options is allocated from the socket option memory
+ * buffer of @sk.
+ */
+struct ipv6_txoptions *
+ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
+                  int newtype, struct ipv6_opt_hdr *newopt)
+{
+       int tot_len = 0;
+       char *p;
+       struct ipv6_txoptions *opt2;
+
+       if (opt) {
+               if (newtype != IPV6_HOPOPTS && opt->hopopt)
+                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
+               if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
+                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
+               if (newtype != IPV6_RTHDR && opt->srcrt)
+                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
+               if (newtype != IPV6_DSTOPTS && opt->dst1opt)
+                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
+       }
+
+       if (newopt)
+               tot_len += CMSG_ALIGN(ipv6_optlen(newopt));
+
+       if (!tot_len)
+               return NULL;
+
+       tot_len += sizeof(*opt2);
+       opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
+       if (!opt2)
+               return ERR_PTR(-ENOBUFS);
+
+       memset(opt2, 0, tot_len);
+       refcount_set(&opt2->refcnt, 1);
+       opt2->tot_len = tot_len;
+       p = (char *)(opt2 + 1);
+
+       ipv6_renew_option(IPV6_HOPOPTS, &opt2->hopopt,
+                         (opt ? opt->hopopt : NULL),
+                         newopt, newtype, &p);
+       ipv6_renew_option(IPV6_RTHDRDSTOPTS, &opt2->dst0opt,
+                         (opt ? opt->dst0opt : NULL),
+                         newopt, newtype, &p);
+       ipv6_renew_option(IPV6_RTHDR,
+                         (struct ipv6_opt_hdr **)&opt2->srcrt,
+                         (opt ? (struct ipv6_opt_hdr *)opt->srcrt : NULL),
+                         newopt, newtype, &p);
+       ipv6_renew_option(IPV6_DSTOPTS, &opt2->dst1opt,
+                         (opt ? opt->dst1opt : NULL),
+                         newopt, newtype, &p);
+
+       opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
+                         (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
+                         (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
+       opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
+
+       return opt2;
+}
+
+struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
+                                         struct ipv6_txoptions *opt)
+{
+       /* ignore the dest before srcrt unless srcrt is being included.
+        * --yoshfuji
+        */
+       if (opt && opt->dst0opt && !opt->srcrt) {
+               if (opt_space != opt) {
+                       memcpy(opt_space, opt, sizeof(*opt_space));
+                       opt = opt_space;
+               }
+               opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
+               opt->dst0opt = NULL;
+       }
+
+       return opt;
+}
+EXPORT_SYMBOL_GPL(ipv6_fixup_options);
+
+/* Destination options header */
+
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
+{
+       struct ipv6_destopt_hao *hao;
+       struct inet6_skb_parm *opt = IP6CB(skb);
+       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+       int ret;
+
+       if (opt->dsthao) {
+               net_dbg_ratelimited("hao duplicated\n");
+               goto discard;
+       }
+       opt->dsthao = opt->dst1;
+       opt->dst1 = 0;
+
+       hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
+
+       if (hao->length != 16) {
+               net_dbg_ratelimited("hao invalid option length = %d\n",
+                                   hao->length);
+               goto discard;
+       }
+
+       if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
+               net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
+                                   &hao->addr);
+               goto discard;
+       }
+
+       ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
+                              (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
+       if (unlikely(ret < 0))
+               goto discard;
+
+       if (skb_cloned(skb)) {
+               if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+                       goto discard;
+
+               /* update all variable using below by copied skbuff */
+               hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
+                                                 optoff);
+               ipv6h = ipv6_hdr(skb);
+       }
+
+       if (skb->ip_summed == CHECKSUM_COMPLETE)
+               skb->ip_summed = CHECKSUM_NONE;
+
+       swap(ipv6h->saddr, hao->addr);
+
+       if (skb->tstamp == 0)
+               __net_timestamp(skb);
+
+       return true;
+
+ discard:
+       kfree_skb(skb);
+       return false;
+}
+#endif
+
+const struct tlvtype_proc tlvprocdestopt_lst[] = {
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+       {
+               .type   = IPV6_TLV_HAO,
+               .func   = ipv6_dest_hao,
+       },
+#endif
+       {-1,                    NULL}
+};
+
+/* Hop-by-hop options */
+
+/* Note: we cannot rely on skb_dst(skb) before we assign it in
+ * ip6_route_input().
+ */
+static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
+{
+       return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) :
+           __in6_dev_get(skb->dev);
+}
+
+static inline struct net *ipv6_skb_net(struct sk_buff *skb)
+{
+       return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
+}
+
+/* Router Alert as of RFC 2711 */
+
+static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
+{
+       const unsigned char *nh = skb_network_header(skb);
+
+       if (nh[optoff + 1] == 2) {
+               IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
+               memcpy(&IP6CB(skb)->ra, nh + optoff + 2,
+                      sizeof(IP6CB(skb)->ra));
+               return true;
+       }
+       net_dbg_ratelimited("%s: wrong RA length %d\n",
+                           __func__, nh[optoff + 1]);
+       kfree_skb(skb);
+       return false;
+}
+
+/* Jumbo payload */
+
+static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
+{
+       const unsigned char *nh = skb_network_header(skb);
+       struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
+       struct net *net = ipv6_skb_net(skb);
+       u32 pkt_len;
+
+       if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
+               net_dbg_ratelimited("%s: wrong jumbo opt length/alignment %d\n",
+                                   __func__, nh[optoff + 1]);
+               __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+               goto drop;
+       }
+
+       pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
+       if (pkt_len <= IPV6_MAXPLEN) {
+               __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+               icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff + 2);
+               return false;
+       }
+       if (ipv6_hdr(skb)->payload_len) {
+               __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+               icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
+               return false;
+       }
+
+       if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
+               __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS);
+               goto drop;
+       }
+
+       if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
+               goto drop;
+
+       IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM;
+       return true;
+
+drop:
+       kfree_skb(skb);
+       return false;
+}
+
+/* CALIPSO RFC 5570 */
+
+static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
+{
+       const unsigned char *nh = skb_network_header(skb);
+
+       if (nh[optoff + 1] < 8)
+               goto drop;
+
+       if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
+               goto drop;
+
+       if (!calipso_validate(skb, nh + optoff))
+               goto drop;
+
+       return true;
+
+drop:
+       kfree_skb(skb);
+       return false;
+}
+
+const struct tlvtype_proc tlvprochopopt_lst[] = {
+       {
+               .type   = IPV6_TLV_ROUTERALERT,
+               .func   = ipv6_hop_ra,
+       },
+       {
+               .type   = IPV6_TLV_JUMBO,
+               .func   = ipv6_hop_jumbo,
+       },
+       {
+               .type   = IPV6_TLV_CALIPSO,
+               .func   = ipv6_hop_calipso,
+       },
+       { -1, }
+};
-- 
2.7.4

Reply via email to