#define LWTUNNEL_HASH_SIZE (1 << LWTUNNEL_HASH_BITS)
/* lw tunnel state flags */
-#define LWTUNNEL_STATE_OUTPUT_REDIRECT 0x1
+#define LWTUNNEL_STATE_OUTPUT_REDIRECT BIT(0)
+#define LWTUNNEL_STATE_INPUT_REDIRECT BIT(1)
struct lwtunnel_state {
__u16 type;
__u16 flags;
atomic_t refcnt;
+ int (*orig_output)(struct sock *sk, struct sk_buff *skb);
+ int (*orig_input)(struct sk_buff *);
int len;
__u8 data[0];
};
int (*build_state)(struct net_device *dev, struct nlattr *encap,
struct lwtunnel_state **ts);
int (*output)(struct sock *sk, struct sk_buff *skb);
+ int (*input)(struct sk_buff *skb);
int (*fill_encap)(struct sk_buff *skb,
struct lwtunnel_state *lwtstate);
int (*get_encap_size)(struct lwtunnel_state *lwtstate);
return false;
}
+static inline bool lwtunnel_input_redirect(struct lwtunnel_state *lwtstate)
+{
+ if (lwtstate && (lwtstate->flags & LWTUNNEL_STATE_INPUT_REDIRECT))
+ return true;
+
+ return false;
+}
int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *op,
unsigned int num);
int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op,
int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b);
int lwtunnel_output(struct sock *sk, struct sk_buff *skb);
int lwtunnel_output6(struct sock *sk, struct sk_buff *skb);
+int lwtunnel_input(struct sk_buff *skb);
+int lwtunnel_input6(struct sk_buff *skb);
#else
return false;
}
+static inline bool lwtunnel_input_redirect(struct lwtunnel_state *lwtstate)
+{
+ return false;
+}
+
static inline int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *op,
unsigned int num)
{
return -EOPNOTSUPP;
}
+static inline int lwtunnel_input(struct sk_buff *skb)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int lwtunnel_input6(struct sk_buff *skb)
+{
+ return -EOPNOTSUPP;
+}
+
#endif
#endif /* __NET_LWTUNNEL_H */
return __lwtunnel_output(sk, skb, lwtstate);
}
EXPORT_SYMBOL(lwtunnel_output);
+
+int __lwtunnel_input(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->input))
+ ret = ops->input(skb);
+ rcu_read_unlock();
+
+ if (ret == -EOPNOTSUPP)
+ goto drop;
+
+ return ret;
+
+drop:
+ kfree_skb(skb);
+
+ return ret;
+}
+
+int lwtunnel_input6(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_input(skb, lwtstate);
+}
+EXPORT_SYMBOL(lwtunnel_input6);
+
+int lwtunnel_input(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_input(skb, lwtstate);
+}
+EXPORT_SYMBOL(lwtunnel_input);
rth->dst.output = ip_output;
rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag);
- if (lwtunnel_output_redirect(rth->rt_lwtstate))
+ if (lwtunnel_output_redirect(rth->rt_lwtstate)) {
+ rth->rt_lwtstate->orig_output = rth->dst.output;
rth->dst.output = lwtunnel_output;
+ }
+ if (lwtunnel_input_redirect(rth->rt_lwtstate)) {
+ rth->rt_lwtstate->orig_input = rth->dst.input;
+ rth->dst.input = lwtunnel_input;
+ }
skb_dst_set(skb, &rth->dst);
out:
err = 0;
if (err)
goto out;
rt->rt6i_lwtstate = lwtstate_get(lwtstate);
- if (lwtunnel_output_redirect(rt->rt6i_lwtstate))
+ if (lwtunnel_output_redirect(rt->rt6i_lwtstate)) {
+ rt->rt6i_lwtstate->orig_output = rt->dst.output;
rt->dst.output = lwtunnel_output6;
+ }
+ if (lwtunnel_input_redirect(rt->rt6i_lwtstate)) {
+ rt->rt6i_lwtstate->orig_input = rt->dst.input;
+ rt->dst.input = lwtunnel_input6;
+ }
}
ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);