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.
9 * Authors: David Ahern <dsa@cumulusnetworks.com>
13 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <sys/mount.h>
17 #include <linux/bpf.h>
25 #include <bsd/string.h>
33 #include "ip_common.h"
36 #define CGRP_PROC_FILE "/cgroup.procs"
38 static struct link_filter vrf_filter
;
40 static void usage(void)
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");
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
54 static int vrf_identify(pid_t pid
, char *name
, size_t len
)
61 snprintf(path
, sizeof(path
), "/proc/%d/cgroup", pid
);
62 fp
= fopen(path
, "r");
68 while (fgets(buf
, sizeof(buf
), fp
)) {
69 /* want the controller-less cgroup */
70 if (strstr(buf
, "::/") == NULL
)
73 vrf
= strstr(buf
, "/vrf/");
75 vrf
+= 5; /* skip past "/vrf/" */
76 end
= strchr(vrf
, '\n');
80 strlcpy(name
, vrf
, len
);
90 static int ipvrf_identify(int argc
, char **argv
)
99 invarg("Extra arguments specified\n", argv
[1]);
100 else if (get_unsigned(&pid
, argv
[0], 10))
101 invarg("Invalid pid\n", argv
[0]);
103 rc
= vrf_identify(pid
, vrf
, sizeof(vrf
));
108 fprintf(stderr
, "Failed to lookup vrf association: %s\n",
115 /* read PATH/vrf/NAME/cgroup.procs file */
116 static void read_cgroup_pids(const char *base_path
, char *name
)
122 if (snprintf(path
, sizeof(path
), "%s/vrf/%s%s",
123 base_path
, name
, CGRP_PROC_FILE
) >= sizeof(path
))
126 fp
= fopen(path
, "r");
128 return; /* no cgroup file, nothing to show */
130 /* dump contents (pids) of cgroup.procs */
131 while (fgets(buf
, sizeof(buf
), fp
)) {
134 nl
= strchr(buf
, '\n');
138 if (get_command_name(buf
, comm
, sizeof(comm
)))
139 strcpy(comm
, "<terminated?>");
141 printf("%5s %s\n", buf
, comm
);
147 /* recurse path looking for PATH[/NETNS]/vrf/NAME */
148 static int recurse_dir(char *base_path
, char *name
, const char *netns
)
156 d
= opendir(base_path
);
160 while ((de
= readdir(d
)) != NULL
) {
161 if (!strcmp(de
->d_name
, ".") || !strcmp(de
->d_name
, ".."))
164 if (!strcmp(de
->d_name
, "vrf")) {
165 const char *pdir
= strrchr(base_path
, '/');
167 /* found a 'vrf' directory. if it is for the given
168 * namespace then dump the cgroup pids
170 if (*netns
== '\0' ||
171 (pdir
&& !strcmp(pdir
+1, netns
)))
172 read_cgroup_pids(base_path
, name
);
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
))
182 if (lstat(path
, &fstat
) < 0)
185 if (S_ISDIR(fstat
.st_mode
)) {
186 rc
= recurse_dir(path
, name
, netns
);
199 static int ipvrf_get_netns(char *netns
, int len
)
201 if (netns_identify_pid("self", netns
, len
-3)) {
202 fprintf(stderr
, "Failed to get name of network namespace: %s\n",
208 strcat(netns
, "-ns");
213 static int ipvrf_pids(int argc
, char **argv
)
220 fprintf(stderr
, "Invalid arguments\n");
225 if (!name_is_vrf(vrf
)) {
226 fprintf(stderr
, "Invalid VRF name\n");
230 mnt
= find_cgroup2_mount();
234 if (ipvrf_get_netns(netns
, sizeof(netns
)) < 0)
237 ret
= recurse_dir(mnt
, vrf
, netns
);
245 /* load BPF program to set sk_bound_dev_if for sockets */
246 static char bpf_log_buf
[256*1024];
248 static int prog_load(int idx
)
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 */
261 return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK
, prog
, sizeof(prog
),
262 "GPL", bpf_log_buf
, sizeof(bpf_log_buf
));
265 static int vrf_configure_cgroup(const char *path
, int ifindex
)
267 int rc
= -1, cg_fd
, prog_fd
= -1;
269 cg_fd
= open(path
, O_DIRECTORY
| O_RDONLY
);
272 "Failed to open cgroup path: '%s'\n",
278 * Load bpf program into kernel and attach to cgroup to affect
281 prog_fd
= prog_load(ifindex
);
283 fprintf(stderr
, "Failed to load BPF prog: '%s'\n",
286 if (errno
!= EPERM
) {
288 "Kernel compiled with CGROUP_BPF enabled?\n");
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",
307 /* get base path for controller-less cgroup for a process.
308 * path returned does not include /vrf/NAME if it exists
310 static int vrf_path(char *vpath
, size_t len
)
317 snprintf(path
, sizeof(path
), "/proc/%d/cgroup", getpid());
318 fp
= fopen(path
, "r");
324 while (fgets(buf
, sizeof(buf
), fp
)) {
327 start
= strstr(buf
, "::/");
331 /* advance past '::' */
334 nl
= strchr(start
, '\n');
338 vrf
= strstr(start
, "/vrf");
342 strlcpy(vpath
, start
, len
);
344 /* if vrf path is just / then return nothing */
345 if (!strcmp(vpath
, "/"))
356 static int vrf_switch(const char *name
)
358 char path
[PATH_MAX
], *mnt
, pid
[16];
359 char vpath
[PATH_MAX
], netns
[256];
361 int rc
= -1, len
, fd
= -1;
363 if (strcmp(name
, "default")) {
364 ifindex
= name_is_vrf(name
);
366 fprintf(stderr
, "Invalid VRF name\n");
371 mnt
= find_cgroup2_mount();
375 /* -1 on length to add '/' to the end */
376 if (ipvrf_get_netns(netns
, sizeof(netns
) - 1) < 0)
379 if (vrf_path(vpath
, sizeof(vpath
)) < 0) {
380 fprintf(stderr
, "Failed to get base cgroup path: %s\n",
385 /* if path already ends in netns then don't add it again */
386 if (*netns
!= '\0') {
387 char *pdir
= strrchr(vpath
, '/');
394 if (strcmp(pdir
, netns
) == 0)
400 /* path to cgroup; make sure buffer has room to cat "/cgroup.procs"
401 * to the end of the path
403 len
= snprintf(path
, sizeof(path
) - sizeof(CGRP_PROC_FILE
),
405 mnt
, vpath
, netns
, ifindex
? name
: "");
406 if (len
> sizeof(path
) - sizeof(CGRP_PROC_FILE
)) {
407 fprintf(stderr
, "Invalid path to cgroup2 mount\n");
411 if (make_path(path
, 0755)) {
412 fprintf(stderr
, "Failed to setup vrf cgroup2 directory\n");
416 if (ifindex
&& vrf_configure_cgroup(path
, ifindex
))
420 * write pid to cgroup.procs making process part of cgroup
422 strcat(path
, CGRP_PROC_FILE
);
423 fd
= open(path
, O_RDWR
| O_APPEND
);
425 fprintf(stderr
, "Failed to open cgroups.procs file: %s.\n",
430 snprintf(pid
, sizeof(pid
), "%d", getpid());
431 if (write(fd
, pid
, strlen(pid
)) < 0) {
432 fprintf(stderr
, "Failed to join cgroup\n");
447 static int ipvrf_exec(int argc
, char **argv
)
450 fprintf(stderr
, "No VRF name specified\n");
454 fprintf(stderr
, "No command specified\n");
458 if (vrf_switch(argv
[0]))
461 return -cmd_exec(argv
[1], argv
+ 1, !!batch_mode
);
464 /* reset VRF association of current process to default VRF;
471 if (vrf_identify(getpid(), vrf
, sizeof(vrf
)) ||
475 vrf_switch("default");
478 static int ipvrf_filter_req(struct nlmsghdr
*nlh
, int reqlen
)
480 struct rtattr
*linkinfo
;
483 if (vrf_filter
.kind
) {
484 linkinfo
= addattr_nest(nlh
, reqlen
, IFLA_LINKINFO
);
486 err
= addattr_l(nlh
, reqlen
, IFLA_INFO_KIND
, vrf_filter
.kind
,
487 strlen(vrf_filter
.kind
));
491 addattr_nest_end(nlh
, linkinfo
);
497 /* input arg is linkinfo */
498 static __u32
vrf_table_linkinfo(struct rtattr
*li
[])
500 struct rtattr
*attr
[IFLA_VRF_MAX
+ 1];
502 if (li
[IFLA_INFO_DATA
]) {
503 parse_rtattr_nested(attr
, IFLA_VRF_MAX
, li
[IFLA_INFO_DATA
]);
505 if (attr
[IFLA_VRF_TABLE
])
506 return rta_getattr_u32(attr
[IFLA_VRF_TABLE
]);
512 static int ipvrf_print(struct nlmsghdr
*n
)
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
;
521 len
-= NLMSG_LENGTH(sizeof(*ifi
));
525 if (vrf_filter
.ifindex
&& vrf_filter
.ifindex
!= ifi
->ifi_index
)
528 parse_rtattr(tb
, IFLA_MAX
, IFLA_RTA(ifi
), len
);
530 /* kernel does not support filter by master device */
531 if (tb
[IFLA_MASTER
]) {
532 int master
= *(int *)RTA_DATA(tb
[IFLA_MASTER
]);
534 if (vrf_filter
.master
&& master
!= vrf_filter
.master
)
538 if (!tb
[IFLA_IFNAME
]) {
540 "BUG: device with ifindex %d has nil ifname\n",
544 name
= rta_getattr_str(tb
[IFLA_IFNAME
]);
546 /* missing LINKINFO means not VRF. e.g., kernel does not
547 * support filtering on kind, so userspace needs to handle
549 if (!tb
[IFLA_LINKINFO
])
552 parse_rtattr_nested(li
, IFLA_INFO_MAX
, tb
[IFLA_LINKINFO
]);
554 if (!li
[IFLA_INFO_KIND
])
557 if (strcmp(RTA_DATA(li
[IFLA_INFO_KIND
]), "vrf"))
560 tb_id
= vrf_table_linkinfo(li
);
563 "BUG: VRF %s is missing table id\n", name
);
567 printf("%-16s %5u", name
, tb_id
);
573 static int ipvrf_show(int argc
, char **argv
)
575 struct nlmsg_chain linfo
= { NULL
, NULL
};
578 vrf_filter
.kind
= "vrf";
586 tb_id
= ipvrf_get_table(argv
[0]);
588 fprintf(stderr
, "Invalid VRF\n");
591 printf("%s %u\n", argv
[0], tb_id
);
595 if (ip_linkaddr_list(0, ipvrf_filter_req
, &linfo
, NULL
) == 0) {
596 struct nlmsg_list
*l
;
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
);
606 printf("No VRF has been configured\n");
610 free_nlmsg_chain(&linfo
);
615 int do_ipvrf(int argc
, char **argv
)
618 return ipvrf_show(0, NULL
);
620 if (matches(*argv
, "identify") == 0)
621 return ipvrf_identify(argc
-1, argv
+1);
623 if (matches(*argv
, "pids") == 0)
624 return ipvrf_pids(argc
-1, argv
+1);
626 if (matches(*argv
, "exec") == 0)
627 return ipvrf_exec(argc
-1, argv
+1);
629 if (matches(*argv
, "show") == 0 ||
630 matches(*argv
, "lst") == 0 ||
631 matches(*argv
, "list") == 0)
632 return ipvrf_show(argc
-1, argv
+1);
634 if (matches(*argv
, "help") == 0)
637 fprintf(stderr
, "Command \"%s\" is unknown, try \"ip vrf help\".\n",