|
@@ -25,6 +25,7 @@
|
|
|
|
|
|
#include <net/lwtunnel.h>
|
|
|
#include <net/rtnetlink.h>
|
|
|
+#include <net/ip6_fib.h>
|
|
|
|
|
|
struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
|
|
|
{
|
|
@@ -177,3 +178,58 @@ int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
|
|
|
return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL(lwtunnel_cmp_encap);
|
|
|
+
|
|
|
+int __lwtunnel_output(struct sock *sk, struct sk_buff *skb,
|
|
|
+ struct lwtunnel_state *lwtstate)
|
|
|
+{
|
|
|
+ const struct lwtunnel_encap_ops *ops;
|
|
|
+ int ret = -EINVAL;
|
|
|
+
|
|
|
+ if (!lwtstate)
|
|
|
+ goto drop;
|
|
|
+
|
|
|
+ if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
|
|
|
+ lwtstate->type > LWTUNNEL_ENCAP_MAX)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ret = -EOPNOTSUPP;
|
|
|
+ rcu_read_lock();
|
|
|
+ ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
|
|
|
+ if (likely(ops && ops->output))
|
|
|
+ ret = ops->output(sk, skb);
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ if (ret == -EOPNOTSUPP)
|
|
|
+ goto drop;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+
|
|
|
+drop:
|
|
|
+ kfree(skb);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int lwtunnel_output6(struct sock *sk, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
|
|
|
+ struct lwtunnel_state *lwtstate = NULL;
|
|
|
+
|
|
|
+ if (rt)
|
|
|
+ lwtstate = rt->rt6i_lwtstate;
|
|
|
+
|
|
|
+ return __lwtunnel_output(sk, skb, lwtstate);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(lwtunnel_output6);
|
|
|
+
|
|
|
+int lwtunnel_output(struct sock *sk, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct rtable *rt = (struct rtable *)skb_dst(skb);
|
|
|
+ struct lwtunnel_state *lwtstate = NULL;
|
|
|
+
|
|
|
+ if (rt)
|
|
|
+ lwtstate = rt->rt_lwtstate;
|
|
|
+
|
|
|
+ return __lwtunnel_output(sk, skb, lwtstate);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(lwtunnel_output);
|