]> git.proxmox.com Git - mirror_iproute2.git/blob - ip/ipvrf.c
Use libbsd for strlcpy if available
[mirror_iproute2.git] / ip / ipvrf.c
1 /*
2 * ipvrf.c "ip vrf"
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: David Ahern <dsa@cumulusnetworks.com>
10 *
11 */
12
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/socket.h>
16 #include <sys/mount.h>
17 #include <linux/bpf.h>
18 #include <linux/if.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 #ifdef HAVE_LIBBSD
25 #include <bsd/string.h>
26 #endif
27 #include <dirent.h>
28 #include <errno.h>
29 #include <limits.h>
30
31 #include "rt_names.h"
32 #include "utils.h"
33 #include "ip_common.h"
34 #include "bpf_util.h"
35
36 #define CGRP_PROC_FILE "/cgroup.procs"
37
38 static struct link_filter vrf_filter;
39
40 static void usage(void)
41 {
42 fprintf(stderr, "Usage: ip vrf show [NAME] ...\n");
43 fprintf(stderr, " ip vrf exec [NAME] cmd ...\n");
44 fprintf(stderr, " ip vrf identify [PID]\n");
45 fprintf(stderr, " ip vrf pids [NAME]\n");
46
47 exit(-1);
48 }
49
50 /*
51 * parse process based cgroup file looking for PATH/vrf/NAME where
52 * NAME is the name of the vrf the process is associated with
53 */
54 static int vrf_identify(pid_t pid, char *name, size_t len)
55 {
56 char path[PATH_MAX];
57 char buf[4096];
58 char *vrf, *end;
59 FILE *fp;
60
61 snprintf(path, sizeof(path), "/proc/%d/cgroup", pid);
62 fp = fopen(path, "r");
63 if (!fp)
64 return -1;
65
66 memset(name, 0, len);
67
68 while (fgets(buf, sizeof(buf), fp)) {
69 /* want the controller-less cgroup */
70 if (strstr(buf, "::/") == NULL)
71 continue;
72
73 vrf = strstr(buf, "/vrf/");
74 if (vrf) {
75 vrf += 5; /* skip past "/vrf/" */
76 end = strchr(vrf, '\n');
77 if (end)
78 *end = '\0';
79
80 strlcpy(name, vrf, len);
81 break;
82 }
83 }
84
85 fclose(fp);
86
87 return 0;
88 }
89
90 static int ipvrf_identify(int argc, char **argv)
91 {
92 char vrf[32];
93 int rc;
94 unsigned int pid;
95
96 if (argc < 1)
97 pid = getpid();
98 else if (argc > 1)
99 invarg("Extra arguments specified\n", argv[1]);
100 else if (get_unsigned(&pid, argv[0], 10))
101 invarg("Invalid pid\n", argv[0]);
102
103 rc = vrf_identify(pid, vrf, sizeof(vrf));
104 if (!rc) {
105 if (vrf[0] != '\0')
106 printf("%s\n", vrf);
107 } else {
108 fprintf(stderr, "Failed to lookup vrf association: %s\n",
109 strerror(errno));
110 }
111
112 return rc;
113 }
114
115 /* read PATH/vrf/NAME/cgroup.procs file */
116 static void read_cgroup_pids(const char *base_path, char *name)
117 {
118 char path[PATH_MAX];
119 char buf[4096];
120 FILE *fp;
121
122 if (snprintf(path, sizeof(path), "%s/vrf/%s%s",
123 base_path, name, CGRP_PROC_FILE) >= sizeof(path))
124 return;
125
126 fp = fopen(path, "r");
127 if (!fp)
128 return; /* no cgroup file, nothing to show */
129
130 /* dump contents (pids) of cgroup.procs */
131 while (fgets(buf, sizeof(buf), fp)) {
132 char *nl, comm[32];
133
134 nl = strchr(buf, '\n');
135 if (nl)
136 *nl = '\0';
137
138 if (get_command_name(buf, comm, sizeof(comm)))
139 strcpy(comm, "<terminated?>");
140
141 printf("%5s %s\n", buf, comm);
142 }
143
144 fclose(fp);
145 }
146
147 /* recurse path looking for PATH[/NETNS]/vrf/NAME */
148 static int recurse_dir(char *base_path, char *name, const char *netns)
149 {
150 char path[PATH_MAX];
151 struct dirent *de;
152 struct stat fstat;
153 int rc;
154 DIR *d;
155
156 d = opendir(base_path);
157 if (!d)
158 return -1;
159
160 while ((de = readdir(d)) != NULL) {
161 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
162 continue;
163
164 if (!strcmp(de->d_name, "vrf")) {
165 const char *pdir = strrchr(base_path, '/');
166
167 /* found a 'vrf' directory. if it is for the given
168 * namespace then dump the cgroup pids
169 */
170 if (*netns == '\0' ||
171 (pdir && !strcmp(pdir+1, netns)))
172 read_cgroup_pids(base_path, name);
173
174 continue;
175 }
176
177 /* is this a subdir that needs to be walked */
178 if (snprintf(path, sizeof(path), "%s/%s",
179 base_path, de->d_name) >= sizeof(path))
180 continue;
181
182 if (lstat(path, &fstat) < 0)
183 continue;
184
185 if (S_ISDIR(fstat.st_mode)) {
186 rc = recurse_dir(path, name, netns);
187 if (rc != 0)
188 goto out;
189 }
190 }
191
192 rc = 0;
193 out:
194 closedir(d);
195
196 return rc;
197 }
198
199 static int ipvrf_get_netns(char *netns, int len)
200 {
201 if (netns_identify_pid("self", netns, len-3)) {
202 fprintf(stderr, "Failed to get name of network namespace: %s\n",
203 strerror(errno));
204 return -1;
205 }
206
207 if (*netns != '\0')
208 strcat(netns, "-ns");
209
210 return 0;
211 }
212
213 static int ipvrf_pids(int argc, char **argv)
214 {
215 char *mnt, *vrf;
216 char netns[256];
217 int ret = -1;
218
219 if (argc != 1) {
220 fprintf(stderr, "Invalid arguments\n");
221 return -1;
222 }
223
224 vrf = argv[0];
225 if (!name_is_vrf(vrf)) {
226 fprintf(stderr, "Invalid VRF name\n");
227 return -1;
228 }
229
230 mnt = find_cgroup2_mount();
231 if (!mnt)
232 return -1;
233
234 if (ipvrf_get_netns(netns, sizeof(netns)) < 0)
235 goto out;
236
237 ret = recurse_dir(mnt, vrf, netns);
238
239 out:
240 free(mnt);
241
242 return ret;
243 }
244
245 /* load BPF program to set sk_bound_dev_if for sockets */
246 static char bpf_log_buf[256*1024];
247
248 static int prog_load(int idx)
249 {
250 struct bpf_insn prog[] = {
251 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
252 BPF_MOV64_IMM(BPF_REG_3, idx),
253 BPF_MOV64_IMM(BPF_REG_2,
254 offsetof(struct bpf_sock, bound_dev_if)),
255 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
256 offsetof(struct bpf_sock, bound_dev_if)),
257 BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
258 BPF_EXIT_INSN(),
259 };
260
261 return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
262 "GPL", bpf_log_buf, sizeof(bpf_log_buf));
263 }
264
265 static int vrf_configure_cgroup(const char *path, int ifindex)
266 {
267 int rc = -1, cg_fd, prog_fd = -1;
268
269 cg_fd = open(path, O_DIRECTORY | O_RDONLY);
270 if (cg_fd < 0) {
271 fprintf(stderr,
272 "Failed to open cgroup path: '%s'\n",
273 strerror(errno));
274 goto out;
275 }
276
277 /*
278 * Load bpf program into kernel and attach to cgroup to affect
279 * socket creates
280 */
281 prog_fd = prog_load(ifindex);
282 if (prog_fd < 0) {
283 fprintf(stderr, "Failed to load BPF prog: '%s'\n",
284 strerror(errno));
285
286 if (errno != EPERM) {
287 fprintf(stderr,
288 "Kernel compiled with CGROUP_BPF enabled?\n");
289 }
290 goto out;
291 }
292
293 if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
294 fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
295 strerror(errno));
296 goto out;
297 }
298
299 rc = 0;
300 out:
301 close(cg_fd);
302 close(prog_fd);
303
304 return rc;
305 }
306
307 /* get base path for controller-less cgroup for a process.
308 * path returned does not include /vrf/NAME if it exists
309 */
310 static int vrf_path(char *vpath, size_t len)
311 {
312 char path[PATH_MAX];
313 char buf[4096];
314 char *vrf;
315 FILE *fp;
316
317 snprintf(path, sizeof(path), "/proc/%d/cgroup", getpid());
318 fp = fopen(path, "r");
319 if (!fp)
320 return -1;
321
322 vpath[0] = '\0';
323
324 while (fgets(buf, sizeof(buf), fp)) {
325 char *start, *nl;
326
327 start = strstr(buf, "::/");
328 if (!start)
329 continue;
330
331 /* advance past '::' */
332 start += 2;
333
334 nl = strchr(start, '\n');
335 if (nl)
336 *nl = '\0';
337
338 vrf = strstr(start, "/vrf");
339 if (vrf)
340 *vrf = '\0';
341
342 strlcpy(vpath, start, len);
343
344 /* if vrf path is just / then return nothing */
345 if (!strcmp(vpath, "/"))
346 vpath[0] = '\0';
347
348 break;
349 }
350
351 fclose(fp);
352
353 return 0;
354 }
355
356 static int vrf_switch(const char *name)
357 {
358 char path[PATH_MAX], *mnt, pid[16];
359 char vpath[PATH_MAX], netns[256];
360 int ifindex = 0;
361 int rc = -1, len, fd = -1;
362
363 if (strcmp(name, "default")) {
364 ifindex = name_is_vrf(name);
365 if (!ifindex) {
366 fprintf(stderr, "Invalid VRF name\n");
367 return -1;
368 }
369 }
370
371 mnt = find_cgroup2_mount();
372 if (!mnt)
373 return -1;
374
375 /* -1 on length to add '/' to the end */
376 if (ipvrf_get_netns(netns, sizeof(netns) - 1) < 0)
377 goto out;
378
379 if (vrf_path(vpath, sizeof(vpath)) < 0) {
380 fprintf(stderr, "Failed to get base cgroup path: %s\n",
381 strerror(errno));
382 goto out;
383 }
384
385 /* if path already ends in netns then don't add it again */
386 if (*netns != '\0') {
387 char *pdir = strrchr(vpath, '/');
388
389 if (!pdir)
390 pdir = vpath;
391 else
392 pdir++;
393
394 if (strcmp(pdir, netns) == 0)
395 *pdir = '\0';
396
397 strcat(netns, "/");
398 }
399
400 /* path to cgroup; make sure buffer has room to cat "/cgroup.procs"
401 * to the end of the path
402 */
403 len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE),
404 "%s%s/%svrf/%s",
405 mnt, vpath, netns, ifindex ? name : "");
406 if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) {
407 fprintf(stderr, "Invalid path to cgroup2 mount\n");
408 goto out;
409 }
410
411 if (make_path(path, 0755)) {
412 fprintf(stderr, "Failed to setup vrf cgroup2 directory\n");
413 goto out;
414 }
415
416 if (ifindex && vrf_configure_cgroup(path, ifindex))
417 goto out;
418
419 /*
420 * write pid to cgroup.procs making process part of cgroup
421 */
422 strcat(path, CGRP_PROC_FILE);
423 fd = open(path, O_RDWR | O_APPEND);
424 if (fd < 0) {
425 fprintf(stderr, "Failed to open cgroups.procs file: %s.\n",
426 strerror(errno));
427 goto out;
428 }
429
430 snprintf(pid, sizeof(pid), "%d", getpid());
431 if (write(fd, pid, strlen(pid)) < 0) {
432 fprintf(stderr, "Failed to join cgroup\n");
433 goto out2;
434 }
435
436 rc = 0;
437 out2:
438 close(fd);
439 out:
440 free(mnt);
441
442 drop_cap();
443
444 return rc;
445 }
446
447 static int ipvrf_exec(int argc, char **argv)
448 {
449 if (argc < 1) {
450 fprintf(stderr, "No VRF name specified\n");
451 return -1;
452 }
453 if (argc < 2) {
454 fprintf(stderr, "No command specified\n");
455 return -1;
456 }
457
458 if (vrf_switch(argv[0]))
459 return -1;
460
461 return -cmd_exec(argv[1], argv + 1, !!batch_mode);
462 }
463
464 /* reset VRF association of current process to default VRF;
465 * used by netns_exec
466 */
467 void vrf_reset(void)
468 {
469 char vrf[32];
470
471 if (vrf_identify(getpid(), vrf, sizeof(vrf)) ||
472 (vrf[0] == '\0'))
473 return;
474
475 vrf_switch("default");
476 }
477
478 static int ipvrf_filter_req(struct nlmsghdr *nlh, int reqlen)
479 {
480 struct rtattr *linkinfo;
481 int err;
482
483 if (vrf_filter.kind) {
484 linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO);
485
486 err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, vrf_filter.kind,
487 strlen(vrf_filter.kind));
488 if (err)
489 return err;
490
491 addattr_nest_end(nlh, linkinfo);
492 }
493
494 return 0;
495 }
496
497 /* input arg is linkinfo */
498 static __u32 vrf_table_linkinfo(struct rtattr *li[])
499 {
500 struct rtattr *attr[IFLA_VRF_MAX + 1];
501
502 if (li[IFLA_INFO_DATA]) {
503 parse_rtattr_nested(attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]);
504
505 if (attr[IFLA_VRF_TABLE])
506 return rta_getattr_u32(attr[IFLA_VRF_TABLE]);
507 }
508
509 return 0;
510 }
511
512 static int ipvrf_print(struct nlmsghdr *n)
513 {
514 struct ifinfomsg *ifi = NLMSG_DATA(n);
515 struct rtattr *tb[IFLA_MAX+1];
516 struct rtattr *li[IFLA_INFO_MAX+1];
517 int len = n->nlmsg_len;
518 const char *name;
519 __u32 tb_id;
520
521 len -= NLMSG_LENGTH(sizeof(*ifi));
522 if (len < 0)
523 return 0;
524
525 if (vrf_filter.ifindex && vrf_filter.ifindex != ifi->ifi_index)
526 return 0;
527
528 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
529
530 /* kernel does not support filter by master device */
531 if (tb[IFLA_MASTER]) {
532 int master = *(int *)RTA_DATA(tb[IFLA_MASTER]);
533
534 if (vrf_filter.master && master != vrf_filter.master)
535 return 0;
536 }
537
538 if (!tb[IFLA_IFNAME]) {
539 fprintf(stderr,
540 "BUG: device with ifindex %d has nil ifname\n",
541 ifi->ifi_index);
542 return 0;
543 }
544 name = rta_getattr_str(tb[IFLA_IFNAME]);
545
546 /* missing LINKINFO means not VRF. e.g., kernel does not
547 * support filtering on kind, so userspace needs to handle
548 */
549 if (!tb[IFLA_LINKINFO])
550 return 0;
551
552 parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
553
554 if (!li[IFLA_INFO_KIND])
555 return 0;
556
557 if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf"))
558 return 0;
559
560 tb_id = vrf_table_linkinfo(li);
561 if (!tb_id) {
562 fprintf(stderr,
563 "BUG: VRF %s is missing table id\n", name);
564 return 0;
565 }
566
567 printf("%-16s %5u", name, tb_id);
568
569 printf("\n");
570 return 1;
571 }
572
573 static int ipvrf_show(int argc, char **argv)
574 {
575 struct nlmsg_chain linfo = { NULL, NULL};
576 int rc = 0;
577
578 vrf_filter.kind = "vrf";
579
580 if (argc > 1)
581 usage();
582
583 if (argc == 1) {
584 __u32 tb_id;
585
586 tb_id = ipvrf_get_table(argv[0]);
587 if (!tb_id) {
588 fprintf(stderr, "Invalid VRF\n");
589 return 1;
590 }
591 printf("%s %u\n", argv[0], tb_id);
592 return 0;
593 }
594
595 if (ip_linkaddr_list(0, ipvrf_filter_req, &linfo, NULL) == 0) {
596 struct nlmsg_list *l;
597 unsigned nvrf = 0;
598 int n;
599
600 n = printf("%-16s %5s\n", "Name", "Table");
601 printf("%.*s\n", n-1, "-----------------------");
602 for (l = linfo.head; l; l = l->next)
603 nvrf += ipvrf_print(&l->h);
604
605 if (!nvrf)
606 printf("No VRF has been configured\n");
607 } else
608 rc = 1;
609
610 free_nlmsg_chain(&linfo);
611
612 return rc;
613 }
614
615 int do_ipvrf(int argc, char **argv)
616 {
617 if (argc == 0)
618 return ipvrf_show(0, NULL);
619
620 if (matches(*argv, "identify") == 0)
621 return ipvrf_identify(argc-1, argv+1);
622
623 if (matches(*argv, "pids") == 0)
624 return ipvrf_pids(argc-1, argv+1);
625
626 if (matches(*argv, "exec") == 0)
627 return ipvrf_exec(argc-1, argv+1);
628
629 if (matches(*argv, "show") == 0 ||
630 matches(*argv, "lst") == 0 ||
631 matches(*argv, "list") == 0)
632 return ipvrf_show(argc-1, argv+1);
633
634 if (matches(*argv, "help") == 0)
635 usage();
636
637 fprintf(stderr, "Command \"%s\" is unknown, try \"ip vrf help\".\n",
638 *argv);
639
640 exit(-1);
641 }