]>
Commit | Line | Data |
---|---|---|
d1df6fd8 DL |
1 | /* |
2 | * SR-IPv6 implementation | |
3 | * | |
4 | * Author: | |
5 | * David Lebrun <david.lebrun@uclouvain.be> | |
6 | * | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * as published by the Free Software Foundation; either version | |
11 | * 2 of the License, or (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/types.h> | |
15 | #include <linux/skbuff.h> | |
16 | #include <linux/net.h> | |
17 | #include <linux/module.h> | |
18 | #include <net/ip.h> | |
19 | #include <net/lwtunnel.h> | |
20 | #include <net/netevent.h> | |
21 | #include <net/netns/generic.h> | |
22 | #include <net/ip6_fib.h> | |
23 | #include <net/route.h> | |
24 | #include <net/seg6.h> | |
25 | #include <linux/seg6.h> | |
26 | #include <linux/seg6_local.h> | |
27 | #include <net/addrconf.h> | |
28 | #include <net/ip6_route.h> | |
29 | #include <net/dst_cache.h> | |
30 | #ifdef CONFIG_IPV6_SEG6_HMAC | |
31 | #include <net/seg6_hmac.h> | |
32 | #endif | |
33 | ||
34 | struct seg6_local_lwt; | |
35 | ||
36 | struct seg6_action_desc { | |
37 | int action; | |
38 | unsigned long attrs; | |
39 | int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); | |
40 | int static_headroom; | |
41 | }; | |
42 | ||
43 | struct seg6_local_lwt { | |
44 | int action; | |
45 | struct ipv6_sr_hdr *srh; | |
46 | int table; | |
47 | struct in_addr nh4; | |
48 | struct in6_addr nh6; | |
49 | int iif; | |
50 | int oif; | |
51 | ||
52 | int headroom; | |
53 | struct seg6_action_desc *desc; | |
54 | }; | |
55 | ||
56 | static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) | |
57 | { | |
58 | return (struct seg6_local_lwt *)lwt->data; | |
59 | } | |
60 | ||
61 | static struct seg6_action_desc seg6_action_table[] = { | |
62 | { | |
63 | .action = SEG6_LOCAL_ACTION_END, | |
64 | .attrs = 0, | |
65 | }, | |
66 | }; | |
67 | ||
68 | static struct seg6_action_desc *__get_action_desc(int action) | |
69 | { | |
70 | struct seg6_action_desc *desc; | |
71 | int i, count; | |
72 | ||
73 | count = sizeof(seg6_action_table) / sizeof(struct seg6_action_desc); | |
74 | for (i = 0; i < count; i++) { | |
75 | desc = &seg6_action_table[i]; | |
76 | if (desc->action == action) | |
77 | return desc; | |
78 | } | |
79 | ||
80 | return NULL; | |
81 | } | |
82 | ||
83 | static int seg6_local_input(struct sk_buff *skb) | |
84 | { | |
85 | struct dst_entry *orig_dst = skb_dst(skb); | |
86 | struct seg6_action_desc *desc; | |
87 | struct seg6_local_lwt *slwt; | |
88 | ||
89 | slwt = seg6_local_lwtunnel(orig_dst->lwtstate); | |
90 | desc = slwt->desc; | |
91 | ||
92 | return desc->input(skb, slwt); | |
93 | } | |
94 | ||
95 | static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { | |
96 | [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, | |
97 | [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, | |
98 | [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, | |
99 | [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, | |
100 | .len = sizeof(struct in_addr) }, | |
101 | [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, | |
102 | .len = sizeof(struct in6_addr) }, | |
103 | [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, | |
104 | [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, | |
105 | }; | |
106 | ||
107 | struct seg6_action_param { | |
108 | int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); | |
109 | int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); | |
110 | int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); | |
111 | }; | |
112 | ||
113 | static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { | |
114 | [SEG6_LOCAL_SRH] = { .parse = NULL, | |
115 | .put = NULL, | |
116 | .cmp = NULL }, | |
117 | ||
118 | [SEG6_LOCAL_TABLE] = { .parse = NULL, | |
119 | .put = NULL, | |
120 | .cmp = NULL }, | |
121 | ||
122 | [SEG6_LOCAL_NH4] = { .parse = NULL, | |
123 | .put = NULL, | |
124 | .cmp = NULL }, | |
125 | ||
126 | [SEG6_LOCAL_NH6] = { .parse = NULL, | |
127 | .put = NULL, | |
128 | .cmp = NULL }, | |
129 | ||
130 | [SEG6_LOCAL_IIF] = { .parse = NULL, | |
131 | .put = NULL, | |
132 | .cmp = NULL }, | |
133 | ||
134 | [SEG6_LOCAL_OIF] = { .parse = NULL, | |
135 | .put = NULL, | |
136 | .cmp = NULL }, | |
137 | }; | |
138 | ||
139 | static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) | |
140 | { | |
141 | struct seg6_action_param *param; | |
142 | struct seg6_action_desc *desc; | |
143 | int i, err; | |
144 | ||
145 | desc = __get_action_desc(slwt->action); | |
146 | if (!desc) | |
147 | return -EINVAL; | |
148 | ||
149 | if (!desc->input) | |
150 | return -EOPNOTSUPP; | |
151 | ||
152 | slwt->desc = desc; | |
153 | slwt->headroom += desc->static_headroom; | |
154 | ||
155 | for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { | |
156 | if (desc->attrs & (1 << i)) { | |
157 | if (!attrs[i]) | |
158 | return -EINVAL; | |
159 | ||
160 | param = &seg6_action_params[i]; | |
161 | ||
162 | err = param->parse(attrs, slwt); | |
163 | if (err < 0) | |
164 | return err; | |
165 | } | |
166 | } | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | static int seg6_local_build_state(struct nlattr *nla, unsigned int family, | |
172 | const void *cfg, struct lwtunnel_state **ts, | |
173 | struct netlink_ext_ack *extack) | |
174 | { | |
175 | struct nlattr *tb[SEG6_LOCAL_MAX + 1]; | |
176 | struct lwtunnel_state *newts; | |
177 | struct seg6_local_lwt *slwt; | |
178 | int err; | |
179 | ||
180 | err = nla_parse_nested(tb, SEG6_LOCAL_MAX, nla, seg6_local_policy, | |
181 | extack); | |
182 | ||
183 | if (err < 0) | |
184 | return err; | |
185 | ||
186 | if (!tb[SEG6_LOCAL_ACTION]) | |
187 | return -EINVAL; | |
188 | ||
189 | newts = lwtunnel_state_alloc(sizeof(*slwt)); | |
190 | if (!newts) | |
191 | return -ENOMEM; | |
192 | ||
193 | slwt = seg6_local_lwtunnel(newts); | |
194 | slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); | |
195 | ||
196 | err = parse_nla_action(tb, slwt); | |
197 | if (err < 0) | |
198 | goto out_free; | |
199 | ||
200 | newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; | |
201 | newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; | |
202 | newts->headroom = slwt->headroom; | |
203 | ||
204 | *ts = newts; | |
205 | ||
206 | return 0; | |
207 | ||
208 | out_free: | |
209 | kfree(slwt->srh); | |
210 | kfree(newts); | |
211 | return err; | |
212 | } | |
213 | ||
214 | static void seg6_local_destroy_state(struct lwtunnel_state *lwt) | |
215 | { | |
216 | struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); | |
217 | ||
218 | kfree(slwt->srh); | |
219 | } | |
220 | ||
221 | static int seg6_local_fill_encap(struct sk_buff *skb, | |
222 | struct lwtunnel_state *lwt) | |
223 | { | |
224 | struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); | |
225 | struct seg6_action_param *param; | |
226 | int i, err; | |
227 | ||
228 | if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) | |
229 | return -EMSGSIZE; | |
230 | ||
231 | for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { | |
232 | if (slwt->desc->attrs & (1 << i)) { | |
233 | param = &seg6_action_params[i]; | |
234 | err = param->put(skb, slwt); | |
235 | if (err < 0) | |
236 | return err; | |
237 | } | |
238 | } | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
243 | static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) | |
244 | { | |
245 | struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); | |
246 | unsigned long attrs; | |
247 | int nlsize; | |
248 | ||
249 | nlsize = nla_total_size(4); /* action */ | |
250 | ||
251 | attrs = slwt->desc->attrs; | |
252 | ||
253 | if (attrs & (1 << SEG6_LOCAL_SRH)) | |
254 | nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); | |
255 | ||
256 | if (attrs & (1 << SEG6_LOCAL_TABLE)) | |
257 | nlsize += nla_total_size(4); | |
258 | ||
259 | if (attrs & (1 << SEG6_LOCAL_NH4)) | |
260 | nlsize += nla_total_size(4); | |
261 | ||
262 | if (attrs & (1 << SEG6_LOCAL_NH6)) | |
263 | nlsize += nla_total_size(16); | |
264 | ||
265 | if (attrs & (1 << SEG6_LOCAL_IIF)) | |
266 | nlsize += nla_total_size(4); | |
267 | ||
268 | if (attrs & (1 << SEG6_LOCAL_OIF)) | |
269 | nlsize += nla_total_size(4); | |
270 | ||
271 | return nlsize; | |
272 | } | |
273 | ||
274 | static int seg6_local_cmp_encap(struct lwtunnel_state *a, | |
275 | struct lwtunnel_state *b) | |
276 | { | |
277 | struct seg6_local_lwt *slwt_a, *slwt_b; | |
278 | struct seg6_action_param *param; | |
279 | int i; | |
280 | ||
281 | slwt_a = seg6_local_lwtunnel(a); | |
282 | slwt_b = seg6_local_lwtunnel(b); | |
283 | ||
284 | if (slwt_a->action != slwt_b->action) | |
285 | return 1; | |
286 | ||
287 | if (slwt_a->desc->attrs != slwt_b->desc->attrs) | |
288 | return 1; | |
289 | ||
290 | for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { | |
291 | if (slwt_a->desc->attrs & (1 << i)) { | |
292 | param = &seg6_action_params[i]; | |
293 | if (param->cmp(slwt_a, slwt_b)) | |
294 | return 1; | |
295 | } | |
296 | } | |
297 | ||
298 | return 0; | |
299 | } | |
300 | ||
301 | static const struct lwtunnel_encap_ops seg6_local_ops = { | |
302 | .build_state = seg6_local_build_state, | |
303 | .destroy_state = seg6_local_destroy_state, | |
304 | .input = seg6_local_input, | |
305 | .fill_encap = seg6_local_fill_encap, | |
306 | .get_encap_size = seg6_local_get_encap_size, | |
307 | .cmp_encap = seg6_local_cmp_encap, | |
308 | .owner = THIS_MODULE, | |
309 | }; | |
310 | ||
311 | int __init seg6_local_init(void) | |
312 | { | |
313 | return lwtunnel_encap_add_ops(&seg6_local_ops, | |
314 | LWTUNNEL_ENCAP_SEG6_LOCAL); | |
315 | } | |
316 | ||
317 | void seg6_local_exit(void) | |
318 | { | |
319 | lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); | |
320 | } |