ip vrf: Handle vrf in a cgroup hierarchy
Add support for VRF in a pre-existing hierarchy. For example, if the current process is running in CGRP/foo/bar, the 'ip vrf exec NAME CMD' should run CMD in the cgroup CGRP/foo/bar/vrf/NAME. When listing process ids in a VRF, search for the directory vrf/NAME regardless of base path (foo/bar/vrf/NAME and vrf/NAME) are still running against the same vrf NAME. Reported-by: Andy Lutomirski <luto@amacapital.net> Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
This commit is contained in:
parent
d754a64aed
commit
46afa6947b
173
ip/ipvrf.c
173
ip/ipvrf.c
|
|
@ -21,6 +21,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
|
@ -40,6 +41,10 @@ static void usage(void)
|
||||||
exit(-1);
|
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)
|
static int vrf_identify(pid_t pid, char *name, size_t len)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
|
|
@ -55,9 +60,13 @@ static int vrf_identify(pid_t pid, char *name, size_t len)
|
||||||
memset(name, 0, len);
|
memset(name, 0, len);
|
||||||
|
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
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) {
|
if (vrf) {
|
||||||
vrf += 7; /* skip past "::/vrf/" */
|
vrf += 5; /* skip past "/vrf/" */
|
||||||
end = strchr(vrf, '\n');
|
end = strchr(vrf, '\n');
|
||||||
if (end)
|
if (end)
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
|
|
@ -97,13 +106,82 @@ static int ipvrf_identify(int argc, char **argv)
|
||||||
return rc;
|
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 path[PATH_MAX];
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
char *mnt, *vrf;
|
|
||||||
int fd, rc = -1;
|
|
||||||
ssize_t n;
|
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/vrf/NAME */
|
||||||
|
static int recurse_dir(char *base_path, char *name)
|
||||||
|
{
|
||||||
|
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")) {
|
||||||
|
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);
|
||||||
|
if (rc != 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
out:
|
||||||
|
closedir(d);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ipvrf_pids(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *mnt, *vrf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (argc != 1) {
|
if (argc != 1) {
|
||||||
fprintf(stderr, "Invalid arguments\n");
|
fprintf(stderr, "Invalid arguments\n");
|
||||||
|
|
@ -116,29 +194,11 @@ static int ipvrf_pids(int argc, char **argv)
|
||||||
if (!mnt)
|
if (!mnt)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%s/vrf/%s%s", mnt, vrf, CGRP_PROC_FILE);
|
ret = recurse_dir(mnt, vrf);
|
||||||
|
|
||||||
free(mnt);
|
free(mnt);
|
||||||
fd = open(path, O_RDONLY);
|
|
||||||
if (fd < 0)
|
|
||||||
return 0; /* no cgroup file, nothing to show */
|
|
||||||
|
|
||||||
while (1) {
|
return ret;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* load BPF program to set sk_bound_dev_if for sockets */
|
/* load BPF program to set sk_bound_dev_if for sockets */
|
||||||
|
|
@ -203,9 +263,60 @@ out:
|
||||||
return rc;
|
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)
|
static int vrf_switch(const char *name)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX], *mnt, pid[16];
|
char path[PATH_MAX], *mnt, pid[16];
|
||||||
|
char vpath[PATH_MAX];
|
||||||
int ifindex = 0;
|
int ifindex = 0;
|
||||||
int rc = -1, len, fd = -1;
|
int rc = -1, len, fd = -1;
|
||||||
|
|
||||||
|
|
@ -221,11 +332,17 @@ static int vrf_switch(const char *name)
|
||||||
if (!mnt)
|
if (!mnt)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (vrf_path(vpath, sizeof(vpath)) < 0) {
|
||||||
|
fprintf(stderr, "Failed to get base cgroup path: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* path to cgroup; make sure buffer has room to cat "/cgroup.procs"
|
/* path to cgroup; make sure buffer has room to cat "/cgroup.procs"
|
||||||
* to the end of the path
|
* to the end of the path
|
||||||
*/
|
*/
|
||||||
len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE), "%s/vrf/%s",
|
len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE),
|
||||||
mnt, ifindex ? name : "");
|
"%s%s/vrf/%s", mnt, vpath, ifindex ? name : "");
|
||||||
if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) {
|
if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) {
|
||||||
fprintf(stderr, "Invalid path to cgroup2 mount\n");
|
fprintf(stderr, "Invalid path to cgroup2 mount\n");
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue