diff --git a/ip/ip_common.h b/ip/ip_common.h index ab6a8343..e8642a18 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -59,6 +59,7 @@ int do_ipnetconf(int argc, char **argv); int do_iptoken(int argc, char **argv); int do_ipvrf(int argc, char **argv); void vrf_reset(void); +int netns_identify_pid(const char *pidstr, char *name, int len); int iplink_get(unsigned int flags, char *name, __u32 filt_mask); diff --git a/ip/ipnetns.c b/ip/ipnetns.c index 8201b94a..0b0378ab 100644 --- a/ip/ipnetns.c +++ b/ip/ipnetns.c @@ -468,28 +468,15 @@ static int netns_pids(int argc, char **argv) } -static int netns_identify(int argc, char **argv) +int netns_identify_pid(const char *pidstr, char *name, int len) { - const char *pidstr; char net_path[PATH_MAX]; int netns; struct stat netst; DIR *dir; struct dirent *entry; - if (argc < 1) { - pidstr = "self"; - } else if (argc > 1) { - fprintf(stderr, "extra arguments specified\n"); - return -1; - } else { - pidstr = argv[0]; - if (!is_pid(pidstr)) { - fprintf(stderr, "Specified string '%s' is not a pid\n", - pidstr); - return -1; - } - } + name[0] = '\0'; snprintf(net_path, sizeof(net_path), "/proc/%s/ns/net", pidstr); netns = open(net_path, O_RDONLY); @@ -531,7 +518,8 @@ static int netns_identify(int argc, char **argv) if ((st.st_dev == netst.st_dev) && (st.st_ino == netst.st_ino)) { - printf("%s\n", entry->d_name); + strncpy(name, entry->d_name, len - 1); + name[len - 1] = '\0'; } } closedir(dir); @@ -539,6 +527,33 @@ static int netns_identify(int argc, char **argv) } +static int netns_identify(int argc, char **argv) +{ + const char *pidstr; + char name[256]; + int rc; + + if (argc < 1) { + pidstr = "self"; + } else if (argc > 1) { + fprintf(stderr, "extra arguments specified\n"); + return -1; + } else { + pidstr = argv[0]; + if (!is_pid(pidstr)) { + fprintf(stderr, "Specified string '%s' is not a pid\n", + pidstr); + return -1; + } + } + + rc = netns_identify_pid(pidstr, name, sizeof(name)); + if (!rc) + printf("%s\n", name); + + return rc; +} + static int on_netns_del(char *nsname, void *arg) { char netns_path[PATH_MAX]; diff --git a/ip/ipvrf.c b/ip/ipvrf.c index 8bd99d62..5e204a9e 100644 --- a/ip/ipvrf.c +++ b/ip/ipvrf.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,10 @@ static void usage(void) exit(-1); } +/* + * parse process based cgroup file looking for PATH/vrf/NAME where + * NAME is the name of the vrf the process is associated with + */ static int vrf_identify(pid_t pid, char *name, size_t len) { char path[PATH_MAX]; @@ -55,9 +60,13 @@ static int vrf_identify(pid_t pid, char *name, size_t len) memset(name, 0, len); while (fgets(buf, sizeof(buf), fp)) { - vrf = strstr(buf, "::/vrf/"); + /* want the controller-less cgroup */ + if (strstr(buf, "::/") == NULL) + continue; + + vrf = strstr(buf, "/vrf/"); if (vrf) { - vrf += 7; /* skip past "::/vrf/" */ + vrf += 5; /* skip past "/vrf/" */ end = strchr(vrf, '\n'); if (end) *end = '\0'; @@ -97,13 +106,105 @@ static int ipvrf_identify(int argc, char **argv) return rc; } -static int ipvrf_pids(int argc, char **argv) +/* read PATH/vrf/NAME/cgroup.procs file */ +static void read_cgroup_pids(const char *base_path, char *name) { char path[PATH_MAX]; char buf[4096]; - char *mnt, *vrf; - int fd, rc = -1; ssize_t n; + int fd; + + if (snprintf(path, sizeof(path), "%s/vrf/%s%s", + base_path, name, CGRP_PROC_FILE) >= sizeof(path)) + return; + + fd = open(path, O_RDONLY); + if (fd < 0) + return; /* no cgroup file, nothing to show */ + + /* dump contents (pids) of cgroup.procs */ + while (1) { + n = read(fd, buf, sizeof(buf) - 1); + if (n <= 0) + break; + + printf("%s", buf); + } + + close(fd); +} + +/* recurse path looking for PATH[/NETNS]/vrf/NAME */ +static int recurse_dir(char *base_path, char *name, const char *netns) +{ + char path[PATH_MAX]; + struct dirent *de; + struct stat fstat; + int rc; + DIR *d; + + d = opendir(base_path); + if (!d) + return -1; + + while ((de = readdir(d)) != NULL) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (!strcmp(de->d_name, "vrf")) { + const char *pdir = strrchr(base_path, '/'); + + /* found a 'vrf' directory. if it is for the given + * namespace then dump the cgroup pids + */ + if (*netns == '\0' || + (pdir && !strcmp(pdir+1, netns))) + read_cgroup_pids(base_path, name); + + continue; + } + + /* is this a subdir that needs to be walked */ + if (snprintf(path, sizeof(path), "%s/%s", + base_path, de->d_name) >= sizeof(path)) + continue; + + if (lstat(path, &fstat) < 0) + continue; + + if (S_ISDIR(fstat.st_mode)) { + rc = recurse_dir(path, name, netns); + if (rc != 0) + goto out; + } + } + + rc = 0; +out: + closedir(d); + + return rc; +} + +static int ipvrf_get_netns(char *netns, int len) +{ + if (netns_identify_pid("self", netns, len-3)) { + fprintf(stderr, "Failed to get name of network namespace: %s\n", + strerror(errno)); + return -1; + } + + if (*netns != '\0') + strcat(netns, "-ns"); + + return 0; +} + +static int ipvrf_pids(int argc, char **argv) +{ + char *mnt, *vrf; + char netns[256]; + int ret = -1; if (argc != 1) { fprintf(stderr, "Invalid arguments\n"); @@ -111,34 +212,24 @@ static int ipvrf_pids(int argc, char **argv) } vrf = argv[0]; + if (!name_is_vrf(vrf)) { + fprintf(stderr, "Invalid VRF name\n"); + return -1; + } mnt = find_cgroup2_mount(); if (!mnt) return -1; - snprintf(path, sizeof(path), "%s/vrf/%s%s", mnt, vrf, CGRP_PROC_FILE); + if (ipvrf_get_netns(netns, sizeof(netns)) < 0) + goto out; + + ret = recurse_dir(mnt, vrf, netns); + +out: free(mnt); - fd = open(path, O_RDONLY); - if (fd < 0) - return 0; /* no cgroup file, nothing to show */ - while (1) { - n = read(fd, buf, sizeof(buf) - 1); - if (n < 0) { - fprintf(stderr, - "Failed to read cgroups file: %s\n", - strerror(errno)); - break; - } else if (n == 0) { - rc = 0; - break; - } - printf("%s", buf); - } - - close(fd); - - return rc; + return ret; } /* load BPF program to set sk_bound_dev_if for sockets */ @@ -203,9 +294,60 @@ out: return rc; } +/* get base path for controller-less cgroup for a process. + * path returned does not include /vrf/NAME if it exists + */ +static int vrf_path(char *vpath, size_t len) +{ + char path[PATH_MAX]; + char buf[4096]; + char *vrf; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/%d/cgroup", getpid()); + fp = fopen(path, "r"); + if (!fp) + return -1; + + vpath[0] = '\0'; + + while (fgets(buf, sizeof(buf), fp)) { + char *start, *nl; + + start = strstr(buf, "::/"); + if (!start) + continue; + + /* advance past '::' */ + start += 2; + + nl = strchr(start, '\n'); + if (nl) + *nl = '\0'; + + vrf = strstr(start, "/vrf"); + if (vrf) + *vrf = '\0'; + + strncpy(vpath, start, len - 1); + vpath[len - 1] = '\0'; + + /* if vrf path is just / then return nothing */ + if (!strcmp(vpath, "/")) + vpath[0] = '\0'; + + break; + } + + fclose(fp); + + return 0; +} + static int vrf_switch(const char *name) { char path[PATH_MAX], *mnt, pid[16]; + char vpath[PATH_MAX], netns[256]; int ifindex = 0; int rc = -1, len, fd = -1; @@ -221,11 +363,37 @@ static int vrf_switch(const char *name) if (!mnt) return -1; + /* -1 on length to add '/' to the end */ + if (ipvrf_get_netns(netns, sizeof(netns) - 1) < 0) + return -1; + + if (vrf_path(vpath, sizeof(vpath)) < 0) { + fprintf(stderr, "Failed to get base cgroup path: %s\n", + strerror(errno)); + return -1; + } + + /* if path already ends in netns then don't add it again */ + if (*netns != '\0') { + char *pdir = strrchr(vpath, '/'); + + if (!pdir) + pdir = vpath; + else + pdir++; + + if (strcmp(pdir, netns) == 0) + *pdir = '\0'; + + strcat(netns, "/"); + } + /* path to cgroup; make sure buffer has room to cat "/cgroup.procs" * to the end of the path */ - len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE), "%s/vrf/%s", - mnt, ifindex ? name : ""); + len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE), + "%s%s/%svrf/%s", + mnt, vpath, netns, ifindex ? name : ""); if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) { fprintf(stderr, "Invalid path to cgroup2 mount\n"); goto out; diff --git a/lib/rt_names.c b/lib/rt_names.c index 3a16d5cf..04c15ff5 100644 --- a/lib/rt_names.c +++ b/lib/rt_names.c @@ -119,7 +119,7 @@ static void rtnl_tab_initialize(const char *file, char **tab, int size) } static char *rtnl_rtprot_tab[256] = { - [RTPROT_UNSPEC] = "none", + [RTPROT_UNSPEC] = "unspec", [RTPROT_REDIRECT] = "redirect", [RTPROT_KERNEL] = "kernel", [RTPROT_BOOT] = "boot", diff --git a/tc/f_matchall.c b/tc/f_matchall.c index 04e524e3..ac486308 100644 --- a/tc/f_matchall.c +++ b/tc/f_matchall.c @@ -130,6 +130,15 @@ static int matchall_print_opt(struct filter_util *qu, FILE *f, sprint_tc_classid(rta_getattr_u32(tb[TCA_MATCHALL_CLASSID]), b1)); } + if (tb[TCA_MATCHALL_FLAGS]) { + __u32 flags = rta_getattr_u32(tb[TCA_MATCHALL_FLAGS]); + + if (flags & TCA_CLS_FLAGS_SKIP_HW) + fprintf(f, "\n skip_hw"); + if (flags & TCA_CLS_FLAGS_SKIP_SW) + fprintf(f, "\n skip_sw"); + } + if (tb[TCA_MATCHALL_ACT]) tc_print_action(f, tb[TCA_MATCHALL_ACT]); diff --git a/testsuite/Makefile b/testsuite/Makefile index 50a7bafa..055136b5 100644 --- a/testsuite/Makefile +++ b/testsuite/Makefile @@ -17,8 +17,9 @@ ifneq (,$(wildcard /proc/config.gz)) KENV := $(shell cat /proc/config.gz | gunzip | grep ^CONFIG) else KVER := $(shell uname -r) -KCPATH := /lib/modules/${KVER}/config -ifneq (,$(wildcard ${KCPATH})) +KCPATHS := /lib/modules/$(KVER)/config /boot/config-$(KVER) +KCPATH := $(firstword $(wildcard $(KCPATHS))) +ifneq (,$(KCPATH)) KENV := $(shell cat ${KCPATH} | grep ^CONFIG) endif endif