net: UDP tunnel encapsulation module for tunnelling different protocols like MPLS...
[platform/kernel/linux-starfive.git] / net / ipv6 / ip6_output.c
index 0873044..8a8c2d0 100644 (file)
@@ -54,6 +54,7 @@
 #include <linux/mroute6.h>
 #include <net/l3mdev.h>
 #include <net/lwtunnel.h>
+#include <net/ip_tunnels.h>
 
 static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
@@ -1196,6 +1197,75 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
 }
 EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow);
 
+/**
+ *      ip6_dst_lookup_tunnel - perform route lookup on tunnel
+ *      @skb: Packet for which lookup is done
+ *      @dev: Tunnel device
+ *      @net: Network namespace of tunnel device
+ *      @sk: Socket which provides route info
+ *      @saddr: Memory to store the src ip address
+ *      @info: Tunnel information
+ *      @protocol: IP protocol
+ *      @use_cahce: Flag to enable cache usage
+ *      This function performs a route lookup on a tunnel
+ *
+ *      It returns a valid dst pointer and stores src address to be used in
+ *      tunnel in param saddr on success, else a pointer encoded error code.
+ */
+
+struct dst_entry *ip6_dst_lookup_tunnel(struct sk_buff *skb,
+                                       struct net_device *dev,
+                                       struct net *net,
+                                       struct socket *sock,
+                                       struct in6_addr *saddr,
+                                       const struct ip_tunnel_info *info,
+                                       u8 protocol,
+                                       bool use_cache)
+{
+       struct dst_entry *dst = NULL;
+#ifdef CONFIG_DST_CACHE
+       struct dst_cache *dst_cache;
+#endif
+       struct flowi6 fl6;
+       __u8 prio;
+
+#ifdef CONFIG_DST_CACHE
+       dst_cache = (struct dst_cache *)&info->dst_cache;
+       if (use_cache) {
+               dst = dst_cache_get_ip6(dst_cache, saddr);
+               if (dst)
+                       return dst;
+       }
+#endif
+       memset(&fl6, 0, sizeof(fl6));
+       fl6.flowi6_mark = skb->mark;
+       fl6.flowi6_proto = protocol;
+       fl6.daddr = info->key.u.ipv6.dst;
+       fl6.saddr = info->key.u.ipv6.src;
+       prio = info->key.tos;
+       fl6.flowlabel = ip6_make_flowinfo(RT_TOS(prio),
+                                         info->key.label);
+
+       dst = ipv6_stub->ipv6_dst_lookup_flow(net, sock->sk, &fl6,
+                                             NULL);
+       if (IS_ERR(dst)) {
+               netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
+               return ERR_PTR(-ENETUNREACH);
+       }
+       if (dst->dev == dev) { /* is this necessary? */
+               netdev_dbg(dev, "circular route to %pI6\n", &fl6.daddr);
+               dst_release(dst);
+               return ERR_PTR(-ELOOP);
+       }
+#ifdef CONFIG_DST_CACHE
+       if (use_cache)
+               dst_cache_set_ip6(dst_cache, dst, &fl6.saddr);
+#endif
+       *saddr = fl6.saddr;
+       return dst;
+}
+EXPORT_SYMBOL_GPL(ip6_dst_lookup_tunnel);
+
 static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src,
                                               gfp_t gfp)
 {