]> Git Repo - linux.git/blob - net/ipv6/ila.c
route: move lwtunnel state to dst_entry
[linux.git] / net / ipv6 / ila.c
1 #include <linux/errno.h>
2 #include <linux/ip.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>
9 #include <net/ip.h>
10 #include <net/ip6_fib.h>
11 #include <net/lwtunnel.h>
12 #include <net/protocol.h>
13 #include <uapi/linux/ila.h>
14
15 struct ila_params {
16         __be64 locator;
17 };
18
19 static inline struct ila_params *ila_params_lwtunnel(
20         struct lwtunnel_state *lwstate)
21 {
22         return (struct ila_params *)lwstate->data;
23 }
24
25 static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
26 {
27         __be32 diff[] = {
28                 ~from[0], ~from[1], to[0], to[1],
29         };
30
31         return csum_partial(diff, sizeof(diff), 0);
32 }
33
34 static inline __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
35 {
36                 return compute_csum_diff8((__be32 *)&ip6h->daddr,
37                                           (__be32 *)&p->locator);
38 }
39
40 static void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
41 {
42         __wsum diff;
43         struct ipv6hdr *ip6h = ipv6_hdr(skb);
44         size_t nhoff = sizeof(struct ipv6hdr);
45
46         /* First update checksum */
47         switch (ip6h->nexthdr) {
48         case NEXTHDR_TCP:
49                 if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
50                         struct tcphdr *th = (struct tcphdr *)
51                                         (skb_network_header(skb) + nhoff);
52
53                         diff = get_csum_diff(ip6h, p);
54                         inet_proto_csum_replace_by_diff(&th->check, skb,
55                                                         diff, true);
56                 }
57                 break;
58         case NEXTHDR_UDP:
59                 if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
60                         struct udphdr *uh = (struct udphdr *)
61                                         (skb_network_header(skb) + nhoff);
62
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,
66                                                                 diff, true);
67                                 if (!uh->check)
68                                         uh->check = CSUM_MANGLED_0;
69                         }
70                 }
71                 break;
72         case NEXTHDR_ICMP:
73                 if (likely(pskb_may_pull(skb,
74                                          nhoff + sizeof(struct icmp6hdr)))) {
75                         struct icmp6hdr *ih = (struct icmp6hdr *)
76                                         (skb_network_header(skb) + nhoff);
77
78                         diff = get_csum_diff(ip6h, p);
79                         inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
80                                                         diff, true);
81                 }
82                 break;
83         }
84
85         /* Now change destination address */
86         *(__be64 *)&ip6h->daddr = p->locator;
87 }
88
89 static int ila_output(struct sock *sk, struct sk_buff *skb)
90 {
91         struct dst_entry *dst = skb_dst(skb);
92
93         if (skb->protocol != htons(ETH_P_IPV6))
94                 goto drop;
95
96         update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
97
98         return dst->lwtstate->orig_output(sk, skb);
99
100 drop:
101         kfree_skb(skb);
102         return -EINVAL;
103 }
104
105 static int ila_input(struct sk_buff *skb)
106 {
107         struct dst_entry *dst = skb_dst(skb);
108
109         if (skb->protocol != htons(ETH_P_IPV6))
110                 goto drop;
111
112         update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
113
114         return dst->lwtstate->orig_input(skb);
115
116 drop:
117         kfree_skb(skb);
118         return -EINVAL;
119 }
120
121 static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
122         [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
123 };
124
125 static int ila_build_state(struct net_device *dev, struct nlattr *nla,
126                            struct lwtunnel_state **ts)
127 {
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;
132         int ret;
133
134         ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla,
135                                ila_nl_policy);
136         if (ret < 0)
137                 return ret;
138
139         if (!tb[ILA_ATTR_LOCATOR])
140                 return -EINVAL;
141
142         newts = lwtunnel_state_alloc(encap_len);
143         if (!newts)
144                 return -ENOMEM;
145
146         newts->len = encap_len;
147         p = ila_params_lwtunnel(newts);
148
149         p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
150
151         newts->type = LWTUNNEL_ENCAP_ILA;
152         newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
153                         LWTUNNEL_STATE_INPUT_REDIRECT;
154
155         *ts = newts;
156
157         return 0;
158 }
159
160 static int ila_fill_encap_info(struct sk_buff *skb,
161                                struct lwtunnel_state *lwtstate)
162 {
163         struct ila_params *p = ila_params_lwtunnel(lwtstate);
164
165         if (nla_put_u64(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator))
166                 goto nla_put_failure;
167
168         return 0;
169
170 nla_put_failure:
171         return -EMSGSIZE;
172 }
173
174 static int ila_encap_nlsize(struct lwtunnel_state *lwtstate)
175 {
176         /* No encapsulation overhead */
177         return 0;
178 }
179
180 static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
181 {
182         struct ila_params *a_p = ila_params_lwtunnel(a);
183         struct ila_params *b_p = ila_params_lwtunnel(b);
184
185         return (a_p->locator != b_p->locator);
186 }
187
188 static const struct lwtunnel_encap_ops ila_encap_ops = {
189         .build_state = ila_build_state,
190         .output = ila_output,
191         .input = ila_input,
192         .fill_encap = ila_fill_encap_info,
193         .get_encap_size = ila_encap_nlsize,
194         .cmp_encap = ila_encap_cmp,
195 };
196
197 static int __init ila_init(void)
198 {
199         return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
200 }
201
202 static void __exit ila_fini(void)
203 {
204         lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
205 }
206
207 module_init(ila_init);
208 module_exit(ila_fini);
209 MODULE_AUTHOR("Tom Herbert <[email protected]>");
210 MODULE_LICENSE("GPL");
This page took 0.044932 seconds and 4 git commands to generate.