Blame ip/ipvrf.c

Packit Service 3880ab
/*
Packit Service 3880ab
 * ipvrf.c	"ip vrf"
Packit Service 3880ab
 *
Packit Service 3880ab
 *		This program is free software; you can redistribute it and/or
Packit Service 3880ab
 *		modify it under the terms of the GNU General Public License
Packit Service 3880ab
 *		as published by the Free Software Foundation; either version
Packit Service 3880ab
 *		2 of the License, or (at your option) any later version.
Packit Service 3880ab
 *
Packit Service 3880ab
 * Authors:	David Ahern <dsa@cumulusnetworks.com>
Packit Service 3880ab
 *
Packit Service 3880ab
 */
Packit Service 3880ab
Packit Service 3880ab
#include <sys/types.h>
Packit Service 3880ab
#include <sys/stat.h>
Packit Service 3880ab
#include <sys/socket.h>
Packit Service 3880ab
#include <sys/mount.h>
Packit Service 3880ab
#include <linux/bpf.h>
Packit Service 3880ab
#include <linux/if.h>
Packit Service 3880ab
#include <fcntl.h>
Packit Service 3880ab
#include <stdio.h>
Packit Service 3880ab
#include <stdlib.h>
Packit Service 3880ab
#include <unistd.h>
Packit Service 3880ab
#include <string.h>
Packit Service 3880ab
#include <dirent.h>
Packit Service 3880ab
#include <errno.h>
Packit Service 3880ab
#include <limits.h>
Packit Service 3880ab
Packit Service 3880ab
#include "rt_names.h"
Packit Service 3880ab
#include "utils.h"
Packit Service 3880ab
#include "ip_common.h"
Packit Service 3880ab
#include "bpf_util.h"
Packit Service 3880ab
Packit Service 3880ab
#define CGRP_PROC_FILE  "/cgroup.procs"
Packit Service 3880ab
Packit Service 3880ab
static struct link_filter vrf_filter;
Packit Service 3880ab
Packit Service 3880ab
static void usage(void)
Packit Service 3880ab
{
Packit Service 3880ab
	fprintf(stderr,
Packit Service 3880ab
		"Usage:	ip vrf show [NAME] ...\n"
Packit Service 3880ab
		"	ip vrf exec [NAME] cmd ...\n"
Packit Service 3880ab
		"	ip vrf identify [PID]\n"
Packit Service 3880ab
		"	ip vrf pids [NAME]\n");
Packit Service 3880ab
Packit Service 3880ab
	exit(-1);
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
/*
Packit Service 3880ab
 * parse process based cgroup file looking for PATH/vrf/NAME where
Packit Service 3880ab
 * NAME is the name of the vrf the process is associated with
Packit Service 3880ab
 */
Packit Service 3880ab
static int vrf_identify(pid_t pid, char *name, size_t len)
Packit Service 3880ab
{
Packit Service 3880ab
	char path[PATH_MAX];
Packit Service 3880ab
	char buf[4096];
Packit Service 3880ab
	char *vrf, *end;
Packit Service 3880ab
	FILE *fp;
Packit Service 3880ab
Packit Service 3880ab
	snprintf(path, sizeof(path), "/proc/%d/cgroup", pid);
Packit Service 3880ab
	fp = fopen(path, "r");
Packit Service 3880ab
	if (!fp)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	memset(name, 0, len);
Packit Service 3880ab
Packit Service 3880ab
	while (fgets(buf, sizeof(buf), fp)) {
Packit Service 3880ab
		/* want the controller-less cgroup */
Packit Service 3880ab
		if (strstr(buf, "::/") == NULL)
Packit Service 3880ab
			continue;
Packit Service 3880ab
Packit Service 3880ab
		vrf = strstr(buf, "/vrf/");
Packit Service 3880ab
		if (vrf) {
Packit Service 3880ab
			vrf += 5;  /* skip past "/vrf/" */
Packit Service 3880ab
			end = strchr(vrf, '\n');
Packit Service 3880ab
			if (end)
Packit Service 3880ab
				*end = '\0';
Packit Service 3880ab
Packit Service 3880ab
			strlcpy(name, vrf, len);
Packit Service 3880ab
			break;
Packit Service 3880ab
		}
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	fclose(fp);
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int ipvrf_identify(int argc, char **argv)
Packit Service 3880ab
{
Packit Service 3880ab
	char vrf[32];
Packit Service 3880ab
	int rc;
Packit Service 3880ab
	unsigned int pid;
Packit Service 3880ab
Packit Service 3880ab
	if (argc < 1)
Packit Service 3880ab
		pid = getpid();
Packit Service 3880ab
	else if (argc > 1)
Packit Service 3880ab
		invarg("Extra arguments specified\n", argv[1]);
Packit Service 3880ab
	else if (get_unsigned(&pid, argv[0], 10))
Packit Service 3880ab
		invarg("Invalid pid\n", argv[0]);
Packit Service 3880ab
Packit Service 3880ab
	rc = vrf_identify(pid, vrf, sizeof(vrf));
Packit Service 3880ab
	if (!rc) {
Packit Service 3880ab
		if (vrf[0] != '\0')
Packit Service 3880ab
			printf("%s\n", vrf);
Packit Service 3880ab
	} else {
Packit Service 3880ab
		fprintf(stderr, "Failed to lookup vrf association: %s\n",
Packit Service 3880ab
			strerror(errno));
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	return rc;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
/* read PATH/vrf/NAME/cgroup.procs file */
Packit Service 3880ab
static void read_cgroup_pids(const char *base_path, char *name)
Packit Service 3880ab
{
Packit Service 3880ab
	char path[PATH_MAX];
Packit Service 3880ab
	char buf[4096];
Packit Service 3880ab
	FILE *fp;
Packit Service 3880ab
Packit Service 3880ab
	if (snprintf(path, sizeof(path), "%s/vrf/%s%s",
Packit Service 3880ab
		     base_path, name, CGRP_PROC_FILE) >= sizeof(path))
Packit Service 3880ab
		return;
Packit Service 3880ab
Packit Service 3880ab
	fp = fopen(path, "r");
Packit Service 3880ab
	if (!fp)
Packit Service 3880ab
		return; /* no cgroup file, nothing to show */
Packit Service 3880ab
Packit Service 3880ab
	/* dump contents (pids) of cgroup.procs */
Packit Service 3880ab
	while (fgets(buf, sizeof(buf), fp)) {
Packit Service 3880ab
		char *nl, comm[32];
Packit Service 3880ab
Packit Service 3880ab
		nl = strchr(buf, '\n');
Packit Service 3880ab
		if (nl)
Packit Service 3880ab
			*nl = '\0';
Packit Service 3880ab
Packit Service 3880ab
		if (get_command_name(buf, comm, sizeof(comm)))
Packit Service 3880ab
			strcpy(comm, "<terminated?>");
Packit Service 3880ab
Packit Service 3880ab
		printf("%5s  %s\n", buf, comm);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	fclose(fp);
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
/* recurse path looking for PATH[/NETNS]/vrf/NAME */
Packit Service 3880ab
static int recurse_dir(char *base_path, char *name, const char *netns)
Packit Service 3880ab
{
Packit Service 3880ab
	char path[PATH_MAX];
Packit Service 3880ab
	struct dirent *de;
Packit Service 3880ab
	struct stat fstat;
Packit Service 3880ab
	int rc;
Packit Service 3880ab
	DIR *d;
Packit Service 3880ab
Packit Service 3880ab
	d = opendir(base_path);
Packit Service 3880ab
	if (!d)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	while ((de = readdir(d)) != NULL) {
Packit Service 3880ab
		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
Packit Service 3880ab
			continue;
Packit Service 3880ab
Packit Service 3880ab
		if (!strcmp(de->d_name, "vrf")) {
Packit Service 3880ab
			const char *pdir = strrchr(base_path, '/');
Packit Service 3880ab
Packit Service 3880ab
			/* found a 'vrf' directory. if it is for the given
Packit Service 3880ab
			 * namespace then dump the cgroup pids
Packit Service 3880ab
			 */
Packit Service 3880ab
			if (*netns == '\0' ||
Packit Service 3880ab
			    (pdir && !strcmp(pdir+1, netns)))
Packit Service 3880ab
				read_cgroup_pids(base_path, name);
Packit Service 3880ab
Packit Service 3880ab
			continue;
Packit Service 3880ab
		}
Packit Service 3880ab
Packit Service 3880ab
		/* is this a subdir that needs to be walked */
Packit Service 3880ab
		if (snprintf(path, sizeof(path), "%s/%s",
Packit Service 3880ab
			     base_path, de->d_name) >= sizeof(path))
Packit Service 3880ab
			continue;
Packit Service 3880ab
Packit Service 3880ab
		if (lstat(path, &fstat) < 0)
Packit Service 3880ab
			continue;
Packit Service 3880ab
Packit Service 3880ab
		if (S_ISDIR(fstat.st_mode)) {
Packit Service 3880ab
			rc = recurse_dir(path, name, netns);
Packit Service 3880ab
			if (rc != 0)
Packit Service 3880ab
				goto out;
Packit Service 3880ab
		}
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	rc = 0;
Packit Service 3880ab
out:
Packit Service 3880ab
	closedir(d);
Packit Service 3880ab
Packit Service 3880ab
	return rc;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int ipvrf_get_netns(char *netns, int len)
Packit Service 3880ab
{
Packit Service 3880ab
	if (netns_identify_pid("self", netns, len-3)) {
Packit Service 3880ab
		fprintf(stderr, "Failed to get name of network namespace: %s\n",
Packit Service 3880ab
			strerror(errno));
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (*netns != '\0')
Packit Service 3880ab
		strcat(netns, "-ns");
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int ipvrf_pids(int argc, char **argv)
Packit Service 3880ab
{
Packit Service 3880ab
	char *mnt, *vrf;
Packit Service 3880ab
	char netns[256];
Packit Service 3880ab
	int ret = -1;
Packit Service 3880ab
Packit Service 3880ab
	if (argc != 1) {
Packit Service 3880ab
		fprintf(stderr, "Invalid arguments\n");
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	vrf = argv[0];
Packit Service 3880ab
	if (!name_is_vrf(vrf)) {
Packit Service 3880ab
		fprintf(stderr, "Invalid VRF name\n");
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	mnt = find_cgroup2_mount(true);
Packit Service 3880ab
	if (!mnt)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	if (ipvrf_get_netns(netns, sizeof(netns)) < 0)
Packit Service 3880ab
		goto out;
Packit Service 3880ab
Packit Service 3880ab
	ret = recurse_dir(mnt, vrf, netns);
Packit Service 3880ab
Packit Service 3880ab
out:
Packit Service 3880ab
	free(mnt);
Packit Service 3880ab
Packit Service 3880ab
	return ret;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
/* load BPF program to set sk_bound_dev_if for sockets */
Packit Service 3880ab
static char bpf_log_buf[256*1024];
Packit Service 3880ab
Packit Service 3880ab
static int prog_load(int idx)
Packit Service 3880ab
{
Packit Service 3880ab
	struct bpf_insn prog[] = {
Packit Service 3880ab
		BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
Packit Service 3880ab
		BPF_MOV64_IMM(BPF_REG_3, idx),
Packit Service 3880ab
		BPF_MOV64_IMM(BPF_REG_2,
Packit Service 3880ab
			      offsetof(struct bpf_sock, bound_dev_if)),
Packit Service 3880ab
		BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
Packit Service 3880ab
			    offsetof(struct bpf_sock, bound_dev_if)),
Packit Service 3880ab
		BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
Packit Service 3880ab
		BPF_EXIT_INSN(),
Packit Service 3880ab
	};
Packit Service 3880ab
Packit Service 3880ab
	return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
Packit Service 3880ab
			     "GPL", bpf_log_buf, sizeof(bpf_log_buf));
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int vrf_configure_cgroup(const char *path, int ifindex)
Packit Service 3880ab
{
Packit Service 3880ab
	int rc = -1, cg_fd, prog_fd = -1;
Packit Service 3880ab
Packit Service 3880ab
	cg_fd = open(path, O_DIRECTORY | O_RDONLY);
Packit Service 3880ab
	if (cg_fd < 0) {
Packit Service 3880ab
		fprintf(stderr,
Packit Service 3880ab
			"Failed to open cgroup path: '%s'\n",
Packit Service 3880ab
			strerror(errno));
Packit Service 3880ab
		goto out;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	/*
Packit Service 3880ab
	 * Load bpf program into kernel and attach to cgroup to affect
Packit Service 3880ab
	 * socket creates
Packit Service 3880ab
	 */
Packit Service 3880ab
	prog_fd = prog_load(ifindex);
Packit Service 3880ab
	if (prog_fd < 0) {
Packit Service 3880ab
		fprintf(stderr, "Failed to load BPF prog: '%s'\n",
Packit Service 3880ab
			strerror(errno));
Packit Service 3880ab
Packit Service 3880ab
		if (errno != EPERM) {
Packit Service 3880ab
			fprintf(stderr,
Packit Service 3880ab
				"Kernel compiled with CGROUP_BPF enabled?\n");
Packit Service 3880ab
		}
Packit Service 3880ab
		goto out;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
Packit Service 3880ab
		fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
Packit Service 3880ab
			strerror(errno));
Packit Service 3880ab
		goto out;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	rc = 0;
Packit Service 3880ab
out:
Packit Service 3880ab
	close(cg_fd);
Packit Service 3880ab
	close(prog_fd);
Packit Service 3880ab
Packit Service 3880ab
	return rc;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
/* get base path for controller-less cgroup for a process.
Packit Service 3880ab
 * path returned does not include /vrf/NAME if it exists
Packit Service 3880ab
 */
Packit Service 3880ab
static int vrf_path(char *vpath, size_t len)
Packit Service 3880ab
{
Packit Service 3880ab
	char path[PATH_MAX];
Packit Service 3880ab
	char buf[4096];
Packit Service 3880ab
	char *vrf;
Packit Service 3880ab
	FILE *fp;
Packit Service 3880ab
Packit Service 3880ab
	snprintf(path, sizeof(path), "/proc/%d/cgroup", getpid());
Packit Service 3880ab
	fp = fopen(path, "r");
Packit Service 3880ab
	if (!fp)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	vpath[0] = '\0';
Packit Service 3880ab
Packit Service 3880ab
	while (fgets(buf, sizeof(buf), fp)) {
Packit Service 3880ab
		char *start, *nl;
Packit Service 3880ab
Packit Service 3880ab
		start = strstr(buf, "::/");
Packit Service 3880ab
		if (!start)
Packit Service 3880ab
			continue;
Packit Service 3880ab
Packit Service 3880ab
		/* advance past '::' */
Packit Service 3880ab
		start += 2;
Packit Service 3880ab
Packit Service 3880ab
		nl = strchr(start, '\n');
Packit Service 3880ab
		if (nl)
Packit Service 3880ab
			*nl = '\0';
Packit Service 3880ab
Packit Service 3880ab
		vrf = strstr(start, "/vrf");
Packit Service 3880ab
		if (vrf)
Packit Service 3880ab
			*vrf = '\0';
Packit Service 3880ab
Packit Service 3880ab
		strlcpy(vpath, start, len);
Packit Service 3880ab
Packit Service 3880ab
		/* if vrf path is just / then return nothing */
Packit Service 3880ab
		if (!strcmp(vpath, "/"))
Packit Service 3880ab
			vpath[0] = '\0';
Packit Service 3880ab
Packit Service 3880ab
		break;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	fclose(fp);
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int vrf_switch(const char *name)
Packit Service 3880ab
{
Packit Service 3880ab
	char path[PATH_MAX], *mnt, pid[16];
Packit Service 3880ab
	char vpath[PATH_MAX], netns[256];
Packit Service 3880ab
	int ifindex = 0;
Packit Service 3880ab
	int rc = -1, len, fd = -1;
Packit Service 3880ab
Packit Service 3880ab
	if (strcmp(name, "default")) {
Packit Service 3880ab
		ifindex = name_is_vrf(name);
Packit Service 3880ab
		if (!ifindex) {
Packit Service 3880ab
			fprintf(stderr, "Invalid VRF name\n");
Packit Service 3880ab
			return -1;
Packit Service 3880ab
		}
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	mnt = find_cgroup2_mount(true);
Packit Service 3880ab
	if (!mnt)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	/* -1 on length to add '/' to the end */
Packit Service 3880ab
	if (ipvrf_get_netns(netns, sizeof(netns) - 1) < 0)
Packit Service 3880ab
		goto out;
Packit Service 3880ab
Packit Service 3880ab
	if (vrf_path(vpath, sizeof(vpath)) < 0) {
Packit Service 3880ab
		fprintf(stderr, "Failed to get base cgroup path: %s\n",
Packit Service 3880ab
			strerror(errno));
Packit Service 3880ab
		goto out;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	/* if path already ends in netns then don't add it again */
Packit Service 3880ab
	if (*netns != '\0') {
Packit Service 3880ab
		char *pdir = strrchr(vpath, '/');
Packit Service 3880ab
Packit Service 3880ab
		if (!pdir)
Packit Service 3880ab
			pdir = vpath;
Packit Service 3880ab
		else
Packit Service 3880ab
			pdir++;
Packit Service 3880ab
Packit Service 3880ab
		if (strcmp(pdir, netns) == 0)
Packit Service 3880ab
			*pdir = '\0';
Packit Service 3880ab
Packit Service 3880ab
		strcat(netns, "/");
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	/* path to cgroup; make sure buffer has room to cat "/cgroup.procs"
Packit Service 3880ab
	 * to the end of the path
Packit Service 3880ab
	 */
Packit Service 3880ab
	len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE),
Packit Service 3880ab
		       "%s%s/%svrf/%s",
Packit Service 3880ab
		       mnt, vpath, netns, ifindex ? name : "");
Packit Service 3880ab
	if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) {
Packit Service 3880ab
		fprintf(stderr, "Invalid path to cgroup2 mount\n");
Packit Service 3880ab
		goto out;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (make_path(path, 0755)) {
Packit Service 3880ab
		fprintf(stderr, "Failed to setup vrf cgroup2 directory\n");
Packit Service 3880ab
		goto out;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (ifindex && vrf_configure_cgroup(path, ifindex))
Packit Service 3880ab
		goto out;
Packit Service 3880ab
Packit Service 3880ab
	/*
Packit Service 3880ab
	 * write pid to cgroup.procs making process part of cgroup
Packit Service 3880ab
	 */
Packit Service 3880ab
	strcat(path, CGRP_PROC_FILE);
Packit Service 3880ab
	fd = open(path, O_RDWR | O_APPEND);
Packit Service 3880ab
	if (fd < 0) {
Packit Service 3880ab
		fprintf(stderr, "Failed to open cgroups.procs file: %s.\n",
Packit Service 3880ab
			strerror(errno));
Packit Service 3880ab
		goto out;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	snprintf(pid, sizeof(pid), "%d", getpid());
Packit Service 3880ab
	if (write(fd, pid, strlen(pid)) < 0) {
Packit Service 3880ab
		fprintf(stderr, "Failed to join cgroup\n");
Packit Service 3880ab
		goto out2;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	rc = 0;
Packit Service 3880ab
out2:
Packit Service 3880ab
	close(fd);
Packit Service 3880ab
out:
Packit Service 3880ab
	free(mnt);
Packit Service 3880ab
Packit Service 3880ab
	drop_cap();
Packit Service 3880ab
Packit Service 3880ab
	return rc;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int do_switch(void *arg)
Packit Service 3880ab
{
Packit Service 3880ab
	char *vrf = arg;
Packit Service 3880ab
Packit Service 3880ab
	return vrf_switch(vrf);
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int ipvrf_exec(int argc, char **argv)
Packit Service 3880ab
{
Packit Service 3880ab
	if (argc < 1) {
Packit Service 3880ab
		fprintf(stderr, "No VRF name specified\n");
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
	if (argc < 2) {
Packit Service 3880ab
		fprintf(stderr, "No command specified\n");
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	return -cmd_exec(argv[1], argv + 1, !!batch_mode, do_switch, argv[0]);
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
/* reset VRF association of current process to default VRF;
Packit Service 3880ab
 * used by netns_exec
Packit Service 3880ab
 */
Packit Service 3880ab
void vrf_reset(void)
Packit Service 3880ab
{
Packit Service 3880ab
	char vrf[32];
Packit Service 3880ab
Packit Service 3880ab
	if (vrf_identify(getpid(), vrf, sizeof(vrf)) ||
Packit Service 3880ab
	    (vrf[0] == '\0'))
Packit Service 3880ab
		return;
Packit Service 3880ab
Packit Service 3880ab
	vrf_switch("default");
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int ipvrf_filter_req(struct nlmsghdr *nlh, int reqlen)
Packit Service 3880ab
{
Packit Service 3880ab
	struct rtattr *linkinfo;
Packit Service 3880ab
	int err;
Packit Service 3880ab
Packit Service 3880ab
	if (vrf_filter.kind) {
Packit Service 3880ab
		linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO);
Packit Service 3880ab
Packit Service 3880ab
		err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, vrf_filter.kind,
Packit Service 3880ab
				strlen(vrf_filter.kind));
Packit Service 3880ab
		if (err)
Packit Service 3880ab
			return err;
Packit Service 3880ab
Packit Service 3880ab
		addattr_nest_end(nlh, linkinfo);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
/* input arg is linkinfo */
Packit Service 3880ab
static __u32 vrf_table_linkinfo(struct rtattr *li[])
Packit Service 3880ab
{
Packit Service 3880ab
	struct rtattr *attr[IFLA_VRF_MAX + 1];
Packit Service 3880ab
Packit Service 3880ab
	if (li[IFLA_INFO_DATA]) {
Packit Service 3880ab
		parse_rtattr_nested(attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]);
Packit Service 3880ab
Packit Service 3880ab
		if (attr[IFLA_VRF_TABLE])
Packit Service 3880ab
			return rta_getattr_u32(attr[IFLA_VRF_TABLE]);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int ipvrf_print(struct nlmsghdr *n)
Packit Service 3880ab
{
Packit Service 3880ab
	struct ifinfomsg *ifi = NLMSG_DATA(n);
Packit Service 3880ab
	struct rtattr *tb[IFLA_MAX+1];
Packit Service 3880ab
	struct rtattr *li[IFLA_INFO_MAX+1];
Packit Service 3880ab
	int len = n->nlmsg_len;
Packit Service 3880ab
	const char *name;
Packit Service 3880ab
	__u32 tb_id;
Packit Service 3880ab
Packit Service 3880ab
	len -= NLMSG_LENGTH(sizeof(*ifi));
Packit Service 3880ab
	if (len < 0)
Packit Service 3880ab
		return 0;
Packit Service 3880ab
Packit Service 3880ab
	if (vrf_filter.ifindex && vrf_filter.ifindex != ifi->ifi_index)
Packit Service 3880ab
		return 0;
Packit Service 3880ab
Packit Service 3880ab
	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
Packit Service 3880ab
Packit Service 3880ab
	/* kernel does not support filter by master device */
Packit Service 3880ab
	if (tb[IFLA_MASTER]) {
Packit Service 3880ab
		int master = *(int *)RTA_DATA(tb[IFLA_MASTER]);
Packit Service 3880ab
Packit Service 3880ab
		if (vrf_filter.master && master != vrf_filter.master)
Packit Service 3880ab
			return 0;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (!tb[IFLA_IFNAME]) {
Packit Service 3880ab
		fprintf(stderr,
Packit Service 3880ab
			"BUG: device with ifindex %d has nil ifname\n",
Packit Service 3880ab
			ifi->ifi_index);
Packit Service 3880ab
		return 0;
Packit Service 3880ab
	}
Packit Service 3880ab
	name = rta_getattr_str(tb[IFLA_IFNAME]);
Packit Service 3880ab
Packit Service 3880ab
	/* missing LINKINFO means not VRF. e.g., kernel does not
Packit Service 3880ab
	 * support filtering on kind, so userspace needs to handle
Packit Service 3880ab
	 */
Packit Service 3880ab
	if (!tb[IFLA_LINKINFO])
Packit Service 3880ab
		return 0;
Packit Service 3880ab
Packit Service 3880ab
	parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
Packit Service 3880ab
Packit Service 3880ab
	if (!li[IFLA_INFO_KIND])
Packit Service 3880ab
		return 0;
Packit Service 3880ab
Packit Service 3880ab
	if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf"))
Packit Service 3880ab
		return 0;
Packit Service 3880ab
Packit Service 3880ab
	tb_id = vrf_table_linkinfo(li);
Packit Service 3880ab
	if (!tb_id) {
Packit Service 3880ab
		fprintf(stderr,
Packit Service 3880ab
			"BUG: VRF %s is missing table id\n", name);
Packit Service 3880ab
		return 0;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	open_json_object(NULL);
Packit Service 3880ab
	print_string(PRINT_ANY, "name", "%-16s", name);
Packit Service 3880ab
	print_uint(PRINT_ANY, "table", " %5u", tb_id);
Packit Service 3880ab
	print_string(PRINT_FP, NULL, "%s", "\n");
Packit Service 3880ab
	close_json_object();
Packit Service 3880ab
Packit Service 3880ab
	return 1;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int ipvrf_show(int argc, char **argv)
Packit Service 3880ab
{
Packit Service 3880ab
	struct nlmsg_chain linfo = { NULL, NULL};
Packit Service 3880ab
	int rc = 0;
Packit Service 3880ab
Packit Service 3880ab
	vrf_filter.kind = "vrf";
Packit Service 3880ab
Packit Service 3880ab
	if (argc > 1)
Packit Service 3880ab
		usage();
Packit Service 3880ab
Packit Service 3880ab
	if (argc == 1) {
Packit Service 3880ab
		__u32 tb_id;
Packit Service 3880ab
Packit Service 3880ab
		tb_id = ipvrf_get_table(argv[0]);
Packit Service 3880ab
		if (!tb_id) {
Packit Service 3880ab
			fprintf(stderr, "Invalid VRF\n");
Packit Service 3880ab
			return 1;
Packit Service 3880ab
		}
Packit Service 3880ab
		printf("%s %u\n", argv[0], tb_id);
Packit Service 3880ab
		return 0;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (ip_link_list(ipvrf_filter_req, &linfo) == 0) {
Packit Service 3880ab
		struct nlmsg_list *l;
Packit Service 3880ab
		unsigned nvrf = 0;
Packit Service 3880ab
Packit Service 3880ab
		new_json_obj(json);
Packit Service 3880ab
Packit Service 3880ab
		print_string(PRINT_FP, NULL, "%-16s", "Name");
Packit Service 3880ab
		print_string(PRINT_FP, NULL, "  %5s\n", "Table");
Packit Service 3880ab
		print_string(PRINT_FP, NULL, "%s\n",
Packit Service 3880ab
			     "-----------------------");
Packit Service 3880ab
Packit Service 3880ab
		for (l = linfo.head; l; l = l->next)
Packit Service 3880ab
			nvrf += ipvrf_print(&l->h);
Packit Service 3880ab
Packit Service 3880ab
		if (!nvrf)
Packit Service 3880ab
			print_string(PRINT_FP, NULL, "%s\n",
Packit Service 3880ab
				     "No VRF has been configured");
Packit Service 3880ab
		delete_json_obj();
Packit Service 3880ab
	} else
Packit Service 3880ab
		rc = 1;
Packit Service 3880ab
Packit Service 3880ab
	free_nlmsg_chain(&linfo);
Packit Service 3880ab
Packit Service 3880ab
	return rc;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
int do_ipvrf(int argc, char **argv)
Packit Service 3880ab
{
Packit Service 3880ab
	if (argc == 0)
Packit Service 3880ab
		return ipvrf_show(0, NULL);
Packit Service 3880ab
Packit Service 3880ab
	if (matches(*argv, "identify") == 0)
Packit Service 3880ab
		return ipvrf_identify(argc-1, argv+1);
Packit Service 3880ab
Packit Service 3880ab
	if (matches(*argv, "pids") == 0)
Packit Service 3880ab
		return ipvrf_pids(argc-1, argv+1);
Packit Service 3880ab
Packit Service 3880ab
	if (matches(*argv, "exec") == 0)
Packit Service 3880ab
		return ipvrf_exec(argc-1, argv+1);
Packit Service 3880ab
Packit Service 3880ab
	if (matches(*argv, "show") == 0 ||
Packit Service 3880ab
	    matches(*argv, "lst") == 0 ||
Packit Service 3880ab
	    matches(*argv, "list") == 0)
Packit Service 3880ab
		return ipvrf_show(argc-1, argv+1);
Packit Service 3880ab
Packit Service 3880ab
	if (matches(*argv, "help") == 0)
Packit Service 3880ab
		usage();
Packit Service 3880ab
Packit Service 3880ab
	fprintf(stderr, "Command \"%s\" is unknown, try \"ip vrf help\".\n",
Packit Service 3880ab
		*argv);
Packit Service 3880ab
Packit Service 3880ab
	exit(-1);
Packit Service 3880ab
}