Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : * SR-IPv6 implementation
4 : : *
5 : : * Author:
6 : : * David Lebrun <david.lebrun@uclouvain.be>
7 : : */
8 : :
9 : : #include <linux/errno.h>
10 : : #include <linux/types.h>
11 : : #include <linux/socket.h>
12 : : #include <linux/net.h>
13 : : #include <linux/in6.h>
14 : : #include <linux/slab.h>
15 : : #include <linux/rhashtable.h>
16 : :
17 : : #include <net/ipv6.h>
18 : : #include <net/protocol.h>
19 : :
20 : : #include <net/seg6.h>
21 : : #include <net/genetlink.h>
22 : : #include <linux/seg6.h>
23 : : #include <linux/seg6_genl.h>
24 : : #ifdef CONFIG_IPV6_SEG6_HMAC
25 : : #include <net/seg6_hmac.h>
26 : : #endif
27 : :
28 : 0 : bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
29 : : {
30 : : int trailing;
31 : : unsigned int tlv_offset;
32 : :
33 [ # # ]: 0 : if (srh->type != IPV6_SRCRT_TYPE_4)
34 : : return false;
35 : :
36 [ # # ]: 0 : if (((srh->hdrlen + 1) << 3) != len)
37 : : return false;
38 : :
39 [ # # ]: 0 : if (srh->segments_left > srh->first_segment)
40 : : return false;
41 : :
42 : 0 : tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
43 : :
44 : 0 : trailing = len - tlv_offset;
45 [ # # ]: 0 : if (trailing < 0)
46 : : return false;
47 : :
48 [ # # ]: 0 : while (trailing) {
49 : : struct sr6_tlv *tlv;
50 : : unsigned int tlv_len;
51 : :
52 [ # # ]: 0 : if (trailing < sizeof(*tlv))
53 : : return false;
54 : :
55 : 0 : tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset);
56 : 0 : tlv_len = sizeof(*tlv) + tlv->len;
57 : :
58 : 0 : trailing -= tlv_len;
59 [ # # ]: 0 : if (trailing < 0)
60 : : return false;
61 : :
62 : 0 : tlv_offset += tlv_len;
63 : : }
64 : :
65 : : return true;
66 : : }
67 : :
68 : : static struct genl_family seg6_genl_family;
69 : :
70 : : static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
71 : : [SEG6_ATTR_DST] = { .type = NLA_BINARY,
72 : : .len = sizeof(struct in6_addr) },
73 : : [SEG6_ATTR_DSTLEN] = { .type = NLA_S32, },
74 : : [SEG6_ATTR_HMACKEYID] = { .type = NLA_U32, },
75 : : [SEG6_ATTR_SECRET] = { .type = NLA_BINARY, },
76 : : [SEG6_ATTR_SECRETLEN] = { .type = NLA_U8, },
77 : : [SEG6_ATTR_ALGID] = { .type = NLA_U8, },
78 : : [SEG6_ATTR_HMACINFO] = { .type = NLA_NESTED, },
79 : : };
80 : :
81 : : #ifdef CONFIG_IPV6_SEG6_HMAC
82 : :
83 : : static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
84 : : {
85 : : struct net *net = genl_info_net(info);
86 : : struct seg6_pernet_data *sdata;
87 : : struct seg6_hmac_info *hinfo;
88 : : u32 hmackeyid;
89 : : char *secret;
90 : : int err = 0;
91 : : u8 algid;
92 : : u8 slen;
93 : :
94 : : sdata = seg6_pernet(net);
95 : :
96 : : if (!info->attrs[SEG6_ATTR_HMACKEYID] ||
97 : : !info->attrs[SEG6_ATTR_SECRETLEN] ||
98 : : !info->attrs[SEG6_ATTR_ALGID])
99 : : return -EINVAL;
100 : :
101 : : hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]);
102 : : slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]);
103 : : algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]);
104 : :
105 : : if (hmackeyid == 0)
106 : : return -EINVAL;
107 : :
108 : : if (slen > SEG6_HMAC_SECRET_LEN)
109 : : return -EINVAL;
110 : :
111 : : mutex_lock(&sdata->lock);
112 : : hinfo = seg6_hmac_info_lookup(net, hmackeyid);
113 : :
114 : : if (!slen) {
115 : : if (!hinfo)
116 : : err = -ENOENT;
117 : :
118 : : err = seg6_hmac_info_del(net, hmackeyid);
119 : :
120 : : goto out_unlock;
121 : : }
122 : :
123 : : if (!info->attrs[SEG6_ATTR_SECRET]) {
124 : : err = -EINVAL;
125 : : goto out_unlock;
126 : : }
127 : :
128 : : if (hinfo) {
129 : : err = seg6_hmac_info_del(net, hmackeyid);
130 : : if (err)
131 : : goto out_unlock;
132 : : }
133 : :
134 : : secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]);
135 : :
136 : : hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL);
137 : : if (!hinfo) {
138 : : err = -ENOMEM;
139 : : goto out_unlock;
140 : : }
141 : :
142 : : memcpy(hinfo->secret, secret, slen);
143 : : hinfo->slen = slen;
144 : : hinfo->alg_id = algid;
145 : : hinfo->hmackeyid = hmackeyid;
146 : :
147 : : err = seg6_hmac_info_add(net, hmackeyid, hinfo);
148 : : if (err)
149 : : kfree(hinfo);
150 : :
151 : : out_unlock:
152 : : mutex_unlock(&sdata->lock);
153 : : return err;
154 : : }
155 : :
156 : : #else
157 : :
158 : 0 : static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
159 : : {
160 : 0 : return -ENOTSUPP;
161 : : }
162 : :
163 : : #endif
164 : :
165 : 0 : static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info)
166 : : {
167 : : struct net *net = genl_info_net(info);
168 : : struct in6_addr *val, *t_old, *t_new;
169 : : struct seg6_pernet_data *sdata;
170 : :
171 : : sdata = seg6_pernet(net);
172 : :
173 [ # # ]: 0 : if (!info->attrs[SEG6_ATTR_DST])
174 : : return -EINVAL;
175 : :
176 : : val = nla_data(info->attrs[SEG6_ATTR_DST]);
177 : 0 : t_new = kmemdup(val, sizeof(*val), GFP_KERNEL);
178 [ # # ]: 0 : if (!t_new)
179 : : return -ENOMEM;
180 : :
181 : 0 : mutex_lock(&sdata->lock);
182 : :
183 : 0 : t_old = sdata->tun_src;
184 : 0 : rcu_assign_pointer(sdata->tun_src, t_new);
185 : :
186 : 0 : mutex_unlock(&sdata->lock);
187 : :
188 : 0 : synchronize_net();
189 : 0 : kfree(t_old);
190 : :
191 : 0 : return 0;
192 : : }
193 : :
194 : 0 : static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info)
195 : : {
196 : : struct net *net = genl_info_net(info);
197 : : struct in6_addr *tun_src;
198 : : struct sk_buff *msg;
199 : : void *hdr;
200 : :
201 : 0 : msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
202 [ # # ]: 0 : if (!msg)
203 : : return -ENOMEM;
204 : :
205 : 0 : hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
206 : : &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC);
207 [ # # ]: 0 : if (!hdr)
208 : : goto free_msg;
209 : :
210 : : rcu_read_lock();
211 : 0 : tun_src = rcu_dereference(seg6_pernet(net)->tun_src);
212 : :
213 [ # # ]: 0 : if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src))
214 : : goto nla_put_failure;
215 : :
216 : : rcu_read_unlock();
217 : :
218 : : genlmsg_end(msg, hdr);
219 : 0 : return genlmsg_reply(msg, info);
220 : :
221 : : nla_put_failure:
222 : : rcu_read_unlock();
223 : : free_msg:
224 : : nlmsg_free(msg);
225 : 0 : return -ENOMEM;
226 : : }
227 : :
228 : : #ifdef CONFIG_IPV6_SEG6_HMAC
229 : :
230 : : static int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo,
231 : : struct sk_buff *msg)
232 : : {
233 : : if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) ||
234 : : nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) ||
235 : : nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) ||
236 : : nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id))
237 : : return -1;
238 : :
239 : : return 0;
240 : : }
241 : :
242 : : static int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo,
243 : : u32 portid, u32 seq, u32 flags,
244 : : struct sk_buff *skb, u8 cmd)
245 : : {
246 : : void *hdr;
247 : :
248 : : hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd);
249 : : if (!hdr)
250 : : return -ENOMEM;
251 : :
252 : : if (__seg6_hmac_fill_info(hinfo, skb) < 0)
253 : : goto nla_put_failure;
254 : :
255 : : genlmsg_end(skb, hdr);
256 : : return 0;
257 : :
258 : : nla_put_failure:
259 : : genlmsg_cancel(skb, hdr);
260 : : return -EMSGSIZE;
261 : : }
262 : :
263 : : static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
264 : : {
265 : : struct net *net = sock_net(cb->skb->sk);
266 : : struct seg6_pernet_data *sdata;
267 : : struct rhashtable_iter *iter;
268 : :
269 : : sdata = seg6_pernet(net);
270 : : iter = (struct rhashtable_iter *)cb->args[0];
271 : :
272 : : if (!iter) {
273 : : iter = kmalloc(sizeof(*iter), GFP_KERNEL);
274 : : if (!iter)
275 : : return -ENOMEM;
276 : :
277 : : cb->args[0] = (long)iter;
278 : : }
279 : :
280 : : rhashtable_walk_enter(&sdata->hmac_infos, iter);
281 : :
282 : : return 0;
283 : : }
284 : :
285 : : static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
286 : : {
287 : : struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
288 : :
289 : : rhashtable_walk_exit(iter);
290 : :
291 : : kfree(iter);
292 : :
293 : : return 0;
294 : : }
295 : :
296 : : static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
297 : : {
298 : : struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
299 : : struct seg6_hmac_info *hinfo;
300 : : int ret;
301 : :
302 : : rhashtable_walk_start(iter);
303 : :
304 : : for (;;) {
305 : : hinfo = rhashtable_walk_next(iter);
306 : :
307 : : if (IS_ERR(hinfo)) {
308 : : if (PTR_ERR(hinfo) == -EAGAIN)
309 : : continue;
310 : : ret = PTR_ERR(hinfo);
311 : : goto done;
312 : : } else if (!hinfo) {
313 : : break;
314 : : }
315 : :
316 : : ret = __seg6_genl_dumphmac_element(hinfo,
317 : : NETLINK_CB(cb->skb).portid,
318 : : cb->nlh->nlmsg_seq,
319 : : NLM_F_MULTI,
320 : : skb, SEG6_CMD_DUMPHMAC);
321 : : if (ret)
322 : : goto done;
323 : : }
324 : :
325 : : ret = skb->len;
326 : :
327 : : done:
328 : : rhashtable_walk_stop(iter);
329 : : return ret;
330 : : }
331 : :
332 : : #else
333 : :
334 : 0 : static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
335 : : {
336 : 0 : return 0;
337 : : }
338 : :
339 : 0 : static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
340 : : {
341 : 0 : return 0;
342 : : }
343 : :
344 : 0 : static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
345 : : {
346 : 0 : return -ENOTSUPP;
347 : : }
348 : :
349 : : #endif
350 : :
351 : 207 : static int __net_init seg6_net_init(struct net *net)
352 : : {
353 : : struct seg6_pernet_data *sdata;
354 : :
355 : 207 : sdata = kzalloc(sizeof(*sdata), GFP_KERNEL);
356 [ + - ]: 207 : if (!sdata)
357 : : return -ENOMEM;
358 : :
359 : 207 : mutex_init(&sdata->lock);
360 : :
361 : 207 : sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL);
362 [ - + ]: 207 : if (!sdata->tun_src) {
363 : 0 : kfree(sdata);
364 : 0 : return -ENOMEM;
365 : : }
366 : :
367 : 207 : net->ipv6.seg6_data = sdata;
368 : :
369 : : #ifdef CONFIG_IPV6_SEG6_HMAC
370 : : seg6_hmac_net_init(net);
371 : : #endif
372 : :
373 : 207 : return 0;
374 : : }
375 : :
376 : 0 : static void __net_exit seg6_net_exit(struct net *net)
377 : : {
378 : : struct seg6_pernet_data *sdata = seg6_pernet(net);
379 : :
380 : : #ifdef CONFIG_IPV6_SEG6_HMAC
381 : : seg6_hmac_net_exit(net);
382 : : #endif
383 : :
384 : 0 : kfree(sdata->tun_src);
385 : 0 : kfree(sdata);
386 : 0 : }
387 : :
388 : : static struct pernet_operations ip6_segments_ops = {
389 : : .init = seg6_net_init,
390 : : .exit = seg6_net_exit,
391 : : };
392 : :
393 : : static const struct genl_ops seg6_genl_ops[] = {
394 : : {
395 : : .cmd = SEG6_CMD_SETHMAC,
396 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
397 : : .doit = seg6_genl_sethmac,
398 : : .flags = GENL_ADMIN_PERM,
399 : : },
400 : : {
401 : : .cmd = SEG6_CMD_DUMPHMAC,
402 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
403 : : .start = seg6_genl_dumphmac_start,
404 : : .dumpit = seg6_genl_dumphmac,
405 : : .done = seg6_genl_dumphmac_done,
406 : : .flags = GENL_ADMIN_PERM,
407 : : },
408 : : {
409 : : .cmd = SEG6_CMD_SET_TUNSRC,
410 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
411 : : .doit = seg6_genl_set_tunsrc,
412 : : .flags = GENL_ADMIN_PERM,
413 : : },
414 : : {
415 : : .cmd = SEG6_CMD_GET_TUNSRC,
416 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
417 : : .doit = seg6_genl_get_tunsrc,
418 : : .flags = GENL_ADMIN_PERM,
419 : : },
420 : : };
421 : :
422 : : static struct genl_family seg6_genl_family __ro_after_init = {
423 : : .hdrsize = 0,
424 : : .name = SEG6_GENL_NAME,
425 : : .version = SEG6_GENL_VERSION,
426 : : .maxattr = SEG6_ATTR_MAX,
427 : : .policy = seg6_genl_policy,
428 : : .netnsok = true,
429 : : .parallel_ops = true,
430 : : .ops = seg6_genl_ops,
431 : : .n_ops = ARRAY_SIZE(seg6_genl_ops),
432 : : .module = THIS_MODULE,
433 : : };
434 : :
435 : 207 : int __init seg6_init(void)
436 : : {
437 : : int err = -ENOMEM;
438 : :
439 : 207 : err = genl_register_family(&seg6_genl_family);
440 [ + - ]: 207 : if (err)
441 : : goto out;
442 : :
443 : 207 : err = register_pernet_subsys(&ip6_segments_ops);
444 [ + - ]: 207 : if (err)
445 : : goto out_unregister_genl;
446 : :
447 : : #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
448 : : err = seg6_iptunnel_init();
449 : : if (err)
450 : : goto out_unregister_pernet;
451 : :
452 : : err = seg6_local_init();
453 : : if (err)
454 : : goto out_unregister_pernet;
455 : : #endif
456 : :
457 : : #ifdef CONFIG_IPV6_SEG6_HMAC
458 : : err = seg6_hmac_init();
459 : : if (err)
460 : : goto out_unregister_iptun;
461 : : #endif
462 : :
463 : 207 : pr_info("Segment Routing with IPv6\n");
464 : :
465 : : out:
466 : 207 : return err;
467 : : #ifdef CONFIG_IPV6_SEG6_HMAC
468 : : out_unregister_iptun:
469 : : #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
470 : : seg6_local_exit();
471 : : seg6_iptunnel_exit();
472 : : #endif
473 : : #endif
474 : : #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
475 : : out_unregister_pernet:
476 : : unregister_pernet_subsys(&ip6_segments_ops);
477 : : #endif
478 : : out_unregister_genl:
479 : 0 : genl_unregister_family(&seg6_genl_family);
480 : 0 : goto out;
481 : : }
482 : :
483 : 0 : void seg6_exit(void)
484 : : {
485 : : #ifdef CONFIG_IPV6_SEG6_HMAC
486 : : seg6_hmac_exit();
487 : : #endif
488 : : #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
489 : : seg6_iptunnel_exit();
490 : : #endif
491 : 0 : unregister_pernet_subsys(&ip6_segments_ops);
492 : 0 : genl_unregister_family(&seg6_genl_family);
493 : 0 : }
|