1 #include <linux/errno.h>
3 #include <linux/kernel.h>
4 #include <linux/module.h>
5 #include <linux/skbuff.h>
6 #include <linux/socket.h>
7 #include <linux/types.h>
8 #include <net/checksum.h>
10 #include <net/ip6_fib.h>
11 #include <net/lwtunnel.h>
12 #include <net/protocol.h>
13 #include <uapi/linux/ila.h>
19 static inline struct ila_params *ila_params_lwtunnel(
20 struct lwtunnel_state *lwstate)
22 return (struct ila_params *)lwstate->data;
25 static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
28 ~from[0], ~from[1], to[0], to[1],
31 return csum_partial(diff, sizeof(diff), 0);
34 static inline __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
36 return compute_csum_diff8((__be32 *)&ip6h->daddr,
37 (__be32 *)&p->locator);
40 static void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
43 struct ipv6hdr *ip6h = ipv6_hdr(skb);
44 size_t nhoff = sizeof(struct ipv6hdr);
46 /* First update checksum */
47 switch (ip6h->nexthdr) {
49 if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
50 struct tcphdr *th = (struct tcphdr *)
51 (skb_network_header(skb) + nhoff);
53 diff = get_csum_diff(ip6h, p);
54 inet_proto_csum_replace_by_diff(&th->check, skb,
59 if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
60 struct udphdr *uh = (struct udphdr *)
61 (skb_network_header(skb) + nhoff);
63 if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
64 diff = get_csum_diff(ip6h, p);
65 inet_proto_csum_replace_by_diff(&uh->check, skb,
68 uh->check = CSUM_MANGLED_0;
73 if (likely(pskb_may_pull(skb,
74 nhoff + sizeof(struct icmp6hdr)))) {
75 struct icmp6hdr *ih = (struct icmp6hdr *)
76 (skb_network_header(skb) + nhoff);
78 diff = get_csum_diff(ip6h, p);
79 inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
85 /* Now change destination address */
86 *(__be64 *)&ip6h->daddr = p->locator;
89 static int ila_output(struct sock *sk, struct sk_buff *skb)
91 struct dst_entry *dst = skb_dst(skb);
93 if (skb->protocol != htons(ETH_P_IPV6))
96 update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
98 return dst->lwtstate->orig_output(sk, skb);
105 static int ila_input(struct sk_buff *skb)
107 struct dst_entry *dst = skb_dst(skb);
109 if (skb->protocol != htons(ETH_P_IPV6))
112 update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
114 return dst->lwtstate->orig_input(skb);
121 static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
122 [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
125 static int ila_build_state(struct net_device *dev, struct nlattr *nla,
126 struct lwtunnel_state **ts)
128 struct ila_params *p;
129 struct nlattr *tb[ILA_ATTR_MAX + 1];
130 size_t encap_len = sizeof(*p);
131 struct lwtunnel_state *newts;
134 ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla,
139 if (!tb[ILA_ATTR_LOCATOR])
142 newts = lwtunnel_state_alloc(encap_len);
146 newts->len = encap_len;
147 p = ila_params_lwtunnel(newts);
149 p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
151 newts->type = LWTUNNEL_ENCAP_ILA;
152 newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
153 LWTUNNEL_STATE_INPUT_REDIRECT;
160 static int ila_fill_encap_info(struct sk_buff *skb,
161 struct lwtunnel_state *lwtstate)
163 struct ila_params *p = ila_params_lwtunnel(lwtstate);
165 if (nla_put_u64(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator))
166 goto nla_put_failure;
174 static int ila_encap_nlsize(struct lwtunnel_state *lwtstate)
176 /* No encapsulation overhead */
180 static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
182 struct ila_params *a_p = ila_params_lwtunnel(a);
183 struct ila_params *b_p = ila_params_lwtunnel(b);
185 return (a_p->locator != b_p->locator);
188 static const struct lwtunnel_encap_ops ila_encap_ops = {
189 .build_state = ila_build_state,
190 .output = ila_output,
192 .fill_encap = ila_fill_encap_info,
193 .get_encap_size = ila_encap_nlsize,
194 .cmp_encap = ila_encap_cmp,
197 static int __init ila_init(void)
199 return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
202 static void __exit ila_fini(void)
204 lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
207 module_init(ila_init);
208 module_exit(ila_fini);
210 MODULE_LICENSE("GPL");