1 /* SPDX-License-Identifier: LGPL-2.1-only */
2
3 /**
4 * @ingroup link
5 * @defgroup ip6vti IP6VTI
6 * ip6vti link module
7 *
8 * @details
9 * \b Link Type Name: "vti6"
10 *
11 * @route_doc{link_ip6vti, IP6VTI Documentation}
12 *
13 * @{
14 */
15
16 #include "nl-default.h"
17
18 #include <linux/if_tunnel.h>
19
20 #include <netlink/netlink.h>
21 #include <netlink/attr.h>
22 #include <netlink/utils.h>
23 #include <netlink/object.h>
24 #include <netlink/route/rtnl.h>
25 #include <netlink/route/link/ip6vti.h>
26
27 #include "nl-route.h"
28 #include "link-api.h"
29
30 #define IP6VTI_ATTR_LINK (1 << 0)
31 #define IP6VTI_ATTR_IKEY (1 << 1)
32 #define IP6VTI_ATTR_OKEY (1 << 2)
33 #define IP6VTI_ATTR_LOCAL (1 << 3)
34 #define IP6VTI_ATTR_REMOTE (1 << 4)
35 #define IP6VTI_ATTR_FWMARK (1 << 5)
36
37 struct ip6vti_info
38 {
39 uint32_t link;
40 uint32_t ikey;
41 uint32_t okey;
42 struct in6_addr local;
43 struct in6_addr remote;
44 uint32_t fwmark;
45 uint32_t ip6vti_mask;
46 };
47
48 static struct nla_policy ip6vti_policy[IFLA_VTI_MAX + 1] = {
49 [IFLA_VTI_LINK] = { .type = NLA_U32 },
50 [IFLA_VTI_IKEY] = { .type = NLA_U32 },
51 [IFLA_VTI_OKEY] = { .type = NLA_U32 },
52 [IFLA_VTI_LOCAL] = { .minlen = sizeof(struct in6_addr) },
53 [IFLA_VTI_REMOTE] = { .minlen = sizeof(struct in6_addr) },
54 [IFLA_VTI_FWMARK] = { .type = NLA_U32 },
55 };
56
ip6vti_alloc(struct rtnl_link * link)57 static int ip6vti_alloc(struct rtnl_link *link)
58 {
59 struct ip6vti_info *ip6vti;
60
61 if (link->l_info)
62 memset(link->l_info, 0, sizeof(*ip6vti));
63 else {
64 ip6vti = calloc(1, sizeof(*ip6vti));
65 if (!ip6vti)
66 return -NLE_NOMEM;
67
68 link->l_info = ip6vti;
69 }
70
71 return 0;
72 }
73
ip6vti_parse(struct rtnl_link * link,struct nlattr * data,struct nlattr * xstats)74 static int ip6vti_parse(struct rtnl_link *link, struct nlattr *data,
75 struct nlattr *xstats)
76 {
77 struct nlattr *tb[IFLA_VTI_MAX + 1];
78 struct ip6vti_info *ip6vti;
79 int err;
80
81 NL_DBG(3, "Parsing IP6VTI link info\n");
82
83 err = nla_parse_nested(tb, IFLA_VTI_MAX, data, ip6vti_policy);
84 if (err < 0)
85 goto errout;
86
87 err = ip6vti_alloc(link);
88 if (err < 0)
89 goto errout;
90
91 ip6vti = link->l_info;
92
93 if (tb[IFLA_VTI_LINK]) {
94 ip6vti->link = nla_get_u32(tb[IFLA_VTI_LINK]);
95 ip6vti->ip6vti_mask |= IP6VTI_ATTR_LINK;
96 }
97
98 if (tb[IFLA_VTI_IKEY]) {
99 ip6vti->ikey = nla_get_u32(tb[IFLA_VTI_IKEY]);
100 ip6vti->ip6vti_mask |= IP6VTI_ATTR_IKEY;
101 }
102
103 if (tb[IFLA_VTI_OKEY]) {
104 ip6vti->okey = nla_get_u32(tb[IFLA_VTI_OKEY]);
105 ip6vti->ip6vti_mask |= IP6VTI_ATTR_OKEY;
106 }
107
108 if (tb[IFLA_VTI_LOCAL]) {
109 nla_memcpy(&ip6vti->local, tb[IFLA_VTI_LOCAL], sizeof(struct in6_addr));
110 ip6vti->ip6vti_mask |= IP6VTI_ATTR_LOCAL;
111 }
112
113 if (tb[IFLA_VTI_REMOTE]) {
114 nla_memcpy(&ip6vti->remote, tb[IFLA_VTI_REMOTE], sizeof(struct in6_addr));
115 ip6vti->ip6vti_mask |= IP6VTI_ATTR_REMOTE;
116 }
117
118 if (tb[IFLA_VTI_FWMARK]) {
119 ip6vti->fwmark = nla_get_u32(tb[IFLA_VTI_FWMARK]);
120 ip6vti->ip6vti_mask |= IP6VTI_ATTR_FWMARK;
121 }
122
123 err = 0;
124
125 errout:
126 return err;
127 }
128
ip6vti_put_attrs(struct nl_msg * msg,struct rtnl_link * link)129 static int ip6vti_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
130 {
131 struct ip6vti_info *ip6vti = link->l_info;
132 struct nlattr *data;
133
134 data = nla_nest_start(msg, IFLA_INFO_DATA);
135 if (!data)
136 return -NLE_MSGSIZE;
137
138 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_LINK)
139 NLA_PUT_U32(msg, IFLA_VTI_LINK, ip6vti->link);
140
141 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_IKEY)
142 NLA_PUT_U32(msg, IFLA_VTI_IKEY, ip6vti->ikey);
143
144 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_OKEY)
145 NLA_PUT_U32(msg, IFLA_VTI_OKEY, ip6vti->okey);
146
147 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_LOCAL)
148 NLA_PUT(msg, IFLA_VTI_LOCAL, sizeof(struct in6_addr), &ip6vti->local);
149
150 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_REMOTE)
151 NLA_PUT(msg, IFLA_VTI_REMOTE, sizeof(struct in6_addr), &ip6vti->remote);
152
153 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_FWMARK)
154 NLA_PUT_U32(msg, IFLA_VTI_FWMARK, ip6vti->fwmark);
155
156 nla_nest_end(msg, data);
157
158 nla_put_failure:
159
160 return 0;
161 }
162
ip6vti_free(struct rtnl_link * link)163 static void ip6vti_free(struct rtnl_link *link)
164 {
165 struct ip6vti_info *ip6vti = link->l_info;
166
167 free(ip6vti);
168 link->l_info = NULL;
169 }
170
ip6vti_dump_line(struct rtnl_link * link,struct nl_dump_params * p)171 static void ip6vti_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
172 {
173 nl_dump(p, "ip6vti : %s", link->l_name);
174 }
175
ip6vti_dump_details(struct rtnl_link * link,struct nl_dump_params * p)176 static void ip6vti_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
177 {
178 struct ip6vti_info *ip6vti = link->l_info;
179 char *name;
180 char addr[INET6_ADDRSTRLEN];
181
182 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_LINK) {
183 nl_dump(p, " link ");
184 name = rtnl_link_get_name(link);
185 if (name)
186 nl_dump_line(p, "%s\n", name);
187 else
188 nl_dump_line(p, "%u\n", ip6vti->link);
189 }
190
191 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_IKEY) {
192 nl_dump(p, " ikey ");
193 nl_dump_line(p, "%x\n",ip6vti->ikey);
194 }
195
196 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_OKEY) {
197 nl_dump(p, " okey ");
198 nl_dump_line(p, "%x\n", ip6vti->okey);
199 }
200
201 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_LOCAL) {
202 nl_dump(p, " local ");
203 nl_dump_line(p, "%s\n",
204 _nl_inet_ntop(AF_INET6, &ip6vti->local, addr));
205 }
206
207 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_REMOTE) {
208 nl_dump(p, " remote ");
209 nl_dump_line(p, "%s\n",
210 _nl_inet_ntop(AF_INET6, &ip6vti->remote, addr));
211 }
212
213 if (ip6vti->ip6vti_mask & IP6VTI_ATTR_FWMARK) {
214 nl_dump(p, " fwmark ");
215 nl_dump_line(p, "%x\n", ip6vti->fwmark);
216 }
217 }
218
ip6vti_clone(struct rtnl_link * dst,struct rtnl_link * src)219 static int ip6vti_clone(struct rtnl_link *dst, struct rtnl_link *src)
220 {
221 struct ip6vti_info *ip6vti_dst, *ip6vti_src = src->l_info;
222 int err;
223
224 dst->l_info = NULL;
225
226 err = rtnl_link_set_type(dst, "vti6");
227 if (err < 0)
228 return err;
229
230 ip6vti_dst = dst->l_info;
231
232 if (!ip6vti_dst || !ip6vti_src)
233 BUG();
234
235 memcpy(ip6vti_dst, ip6vti_src, sizeof(struct ip6vti_info));
236
237 return 0;
238 }
239
240 static struct rtnl_link_info_ops ip6vti_info_ops = {
241 .io_name = "vti6",
242 .io_alloc = ip6vti_alloc,
243 .io_parse = ip6vti_parse,
244 .io_dump = {
245 [NL_DUMP_LINE] = ip6vti_dump_line,
246 [NL_DUMP_DETAILS] = ip6vti_dump_details,
247 },
248 .io_clone = ip6vti_clone,
249 .io_put_attrs = ip6vti_put_attrs,
250 .io_free = ip6vti_free,
251 };
252
253 #define IS_IP6VTI_LINK_ASSERT(link) \
254 if ((link)->l_info_ops != &ip6vti_info_ops) { \
255 APPBUG("Link is not a ip6vti link. set type \"vti6\" first."); \
256 return -NLE_OPNOTSUPP; \
257 }
258
259 #define HAS_IP6VTI_ATTR_ASSERT(ip6vti,attr) \
260 if (!((ip6vti)->ip6vti_mask & (attr))) \
261 return -NLE_NOATTR;
262
rtnl_link_ip6vti_alloc(void)263 struct rtnl_link *rtnl_link_ip6vti_alloc(void)
264 {
265 struct rtnl_link *link;
266 int err;
267
268 link = rtnl_link_alloc();
269 if (!link)
270 return NULL;
271
272 err = rtnl_link_set_type(link, "vti6");
273 if (err < 0) {
274 rtnl_link_put(link);
275 return NULL;
276 }
277
278 return link;
279 }
280
281 /**
282 * Check if link is a IP6VTI link
283 * @arg link Link object
284 *
285 * @return True if link is a IP6VTI link, otherwise 0 is returned.
286 */
rtnl_link_is_ip6vti(struct rtnl_link * link)287 int rtnl_link_is_ip6vti(struct rtnl_link *link)
288 {
289 return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vti6");
290 }
291 /**
292 * Create a new vti6 tunnel device
293 * @arg sock netlink socket
294 * @arg name name of the tunnel deviceL
295 *
296 * Creates a new vti6 tunnel device in the kernel
297 * @return 0 on success or a negative error code
298 */
rtnl_link_ip6vti_add(struct nl_sock * sk,const char * name)299 int rtnl_link_ip6vti_add(struct nl_sock *sk, const char *name)
300 {
301 struct rtnl_link *link;
302 int err;
303
304 link = rtnl_link_ip6vti_alloc();
305 if (!link)
306 return -NLE_NOMEM;
307
308 if(name)
309 rtnl_link_set_name(link, name);
310
311 err = rtnl_link_add(sk, link, NLM_F_CREATE);
312 rtnl_link_put(link);
313
314 return err;
315 }
316 /**
317 * Set IP6VTI tunnel interface index
318 * @arg link Link object
319 * @arg index interface index
320 *
321 * @return 0 on success or a negative error code
322 */
rtnl_link_ip6vti_set_link(struct rtnl_link * link,uint32_t index)323 int rtnl_link_ip6vti_set_link(struct rtnl_link *link, uint32_t index)
324 {
325 struct ip6vti_info *ip6vti = link->l_info;
326
327 IS_IP6VTI_LINK_ASSERT(link);
328
329 ip6vti->link = index;
330 ip6vti->ip6vti_mask |= IP6VTI_ATTR_LINK;
331
332 return 0;
333 }
334
335 /**
336 * Get IP6VTI tunnel interface index
337 * @arg link Link object
338 * @arg index addr to fill in with the interface index
339 *
340 * @return 0 on success or a negative error code
341 */
rtnl_link_ip6vti_get_link(struct rtnl_link * link,uint32_t * index)342 int rtnl_link_ip6vti_get_link(struct rtnl_link *link, uint32_t *index)
343 {
344 struct ip6vti_info *ip6vti = link->l_info;
345
346 IS_IP6VTI_LINK_ASSERT(link);
347
348 HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_LINK);
349
350 *index = ip6vti->link;
351
352 return 0;
353 }
354
355 /**
356 * Set IP6VTI tunnel set ikey
357 * @arg link Link object
358 * @arg ikey gre ikey
359 *
360 * @return 0 on success or a negative error code
361 */
rtnl_link_ip6vti_set_ikey(struct rtnl_link * link,uint32_t ikey)362 int rtnl_link_ip6vti_set_ikey(struct rtnl_link *link, uint32_t ikey)
363 {
364 struct ip6vti_info *ip6vti = link->l_info;
365
366 IS_IP6VTI_LINK_ASSERT(link);
367
368 ip6vti->ikey = ikey;
369 ip6vti->ip6vti_mask |= IP6VTI_ATTR_IKEY;
370
371 return 0;
372 }
373
374 /**
375 * Get IP6VTI tunnel ikey
376 * @arg link Link object
377 * @arg ikey addr to fill in with the ikey
378 *
379 * @return 0 on success or a negative error code
380 */
rtnl_link_ip6vti_get_ikey(struct rtnl_link * link,uint32_t * ikey)381 int rtnl_link_ip6vti_get_ikey(struct rtnl_link *link, uint32_t *ikey)
382 {
383 struct ip6vti_info *ip6vti = link->l_info;
384
385 IS_IP6VTI_LINK_ASSERT(link);
386
387 HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_IKEY);
388
389 *ikey = ip6vti->ikey;
390
391 return 0;
392 }
393
394 /**
395 * Set IP6VTI tunnel set okey
396 * @arg link Link object
397 * @arg okey gre okey
398 *
399 * @return 0 on success or a negative error code
400 */
rtnl_link_ip6vti_set_okey(struct rtnl_link * link,uint32_t okey)401 int rtnl_link_ip6vti_set_okey(struct rtnl_link *link, uint32_t okey)
402 {
403 struct ip6vti_info *ip6vti = link->l_info;
404
405 IS_IP6VTI_LINK_ASSERT(link);
406
407 ip6vti->okey = okey;
408 ip6vti->ip6vti_mask |= IP6VTI_ATTR_OKEY;
409
410 return 0;
411 }
412
413 /**
414 * Get IP6VTI tunnel okey
415 * @arg link Link object
416 * @arg okey addr to fill in with the okey
417 *
418 * @return 0 on success or a negative error code
419 */
rtnl_link_ip6vti_get_okey(struct rtnl_link * link,uint32_t * okey)420 int rtnl_link_ip6vti_get_okey(struct rtnl_link *link, uint32_t *okey)
421 {
422 struct ip6vti_info *ip6vti = link->l_info;
423
424 IS_IP6VTI_LINK_ASSERT(link);
425
426 HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_OKEY);
427
428 *okey = ip6vti->okey;
429
430 return 0;
431 }
432
433 /**
434 * Set IP6VTI tunnel local address
435 * @arg link Link object
436 * @arg local local address
437 *
438 * @return 0 on success or a negative error code
439 */
rtnl_link_ip6vti_set_local(struct rtnl_link * link,struct in6_addr * local)440 int rtnl_link_ip6vti_set_local(struct rtnl_link *link, struct in6_addr *local)
441 {
442 struct ip6vti_info *ip6vti = link->l_info;
443
444 IS_IP6VTI_LINK_ASSERT(link);
445
446 memcpy(&ip6vti->local, local, sizeof(struct in6_addr));
447 ip6vti->ip6vti_mask |= IP6VTI_ATTR_LOCAL;
448
449 return 0;
450 }
451
452 /**
453 * Get IP6VTI tunnel local address
454 * @arg link Link object
455 * @arg local addr to fill in with remote address
456 *
457 * @return 0 on success or a negative error code
458 */
rtnl_link_ip6vti_get_local(struct rtnl_link * link,struct in6_addr * local)459 int rtnl_link_ip6vti_get_local(struct rtnl_link *link, struct in6_addr *local)
460 {
461 struct ip6vti_info *ip6vti = link->l_info;
462
463 IS_IP6VTI_LINK_ASSERT(link);
464
465 HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_LOCAL);
466
467 memcpy(local, &ip6vti->local, sizeof(struct in6_addr));
468
469 return 0;
470 }
471
472 /**
473 * Set IP6VTI tunnel remote address
474 * @arg link Link object
475 * @arg remote remote address
476 *
477 * @return 0 on success or a negative error code
478 */
rtnl_link_ip6vti_set_remote(struct rtnl_link * link,struct in6_addr * remote)479 int rtnl_link_ip6vti_set_remote(struct rtnl_link *link, struct in6_addr *remote)
480 {
481 struct ip6vti_info *ip6vti = link->l_info;
482
483 IS_IP6VTI_LINK_ASSERT(link);
484
485 memcpy(&ip6vti->remote, remote, sizeof(struct in6_addr));
486 ip6vti->ip6vti_mask |= IP6VTI_ATTR_REMOTE;
487
488 return 0;
489 }
490
491 /**
492 * Get IP6VTI tunnel remote address
493 * @arg link Link object
494 * @arg remote addr to fill in with remote address
495 *
496 * @return 0 on success or a negative error code
497 */
rtnl_link_ip6vti_get_remote(struct rtnl_link * link,struct in6_addr * remote)498 int rtnl_link_ip6vti_get_remote(struct rtnl_link *link, struct in6_addr *remote)
499 {
500 struct ip6vti_info *ip6vti = link->l_info;
501
502 IS_IP6VTI_LINK_ASSERT(link);
503
504 HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_REMOTE);
505
506 memcpy(remote, &ip6vti->remote, sizeof(struct in6_addr));
507
508 return 0;
509 }
510
511 /**
512 * Set IP6VTI tunnel fwmark
513 * @arg link Link object
514 * @arg fwmark fwmark
515 *
516 * @return 0 on success or a negative error code
517 */
rtnl_link_ip6vti_set_fwmark(struct rtnl_link * link,uint32_t fwmark)518 int rtnl_link_ip6vti_set_fwmark(struct rtnl_link *link, uint32_t fwmark)
519 {
520 struct ip6vti_info *ip6vti = link->l_info;
521
522 IS_IP6VTI_LINK_ASSERT(link);
523
524 ip6vti->fwmark = fwmark;
525 ip6vti->ip6vti_mask |= IP6VTI_ATTR_FWMARK;
526
527 return 0;
528 }
529
530 /**
531 * Get IP6VTI tunnel fwmark
532 * @arg link Link object
533 * @arg fwmark addr to fill in with the fwmark
534 *
535 * @return 0 on success or a negative error code
536 */
rtnl_link_ip6vti_get_fwmark(struct rtnl_link * link,uint32_t * fwmark)537 int rtnl_link_ip6vti_get_fwmark(struct rtnl_link *link, uint32_t *fwmark)
538 {
539 struct ip6vti_info *ip6vti = link->l_info;
540
541 IS_IP6VTI_LINK_ASSERT(link);
542
543 HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_FWMARK);
544
545 *fwmark = ip6vti->fwmark;
546
547 return 0;
548 }
549
ip6vti_init(void)550 static void _nl_init ip6vti_init(void)
551 {
552 rtnl_link_register_info(&ip6vti_info_ops);
553 }
554
ip6vti_exit(void)555 static void _nl_exit ip6vti_exit(void)
556 {
557 rtnl_link_unregister_info(&ip6vti_info_ops);
558 }
559