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