mirror of
				https://git.proxmox.com/git/mirror_iproute2
				synced 2025-11-04 11:27:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			295 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * ipvrf.c	"ip vrf"
 | 
						|
 *
 | 
						|
 *		This program is free software; you can redistribute it and/or
 | 
						|
 *		modify it under the terms of the GNU General Public License
 | 
						|
 *		as published by the Free Software Foundation; either version
 | 
						|
 *		2 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * Authors:	David Ahern <dsa@cumulusnetworks.com>
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/socket.h>
 | 
						|
#include <sys/mount.h>
 | 
						|
#include <linux/bpf.h>
 | 
						|
#include <linux/if.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <string.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <limits.h>
 | 
						|
 | 
						|
#include "rt_names.h"
 | 
						|
#include "utils.h"
 | 
						|
#include "ip_common.h"
 | 
						|
#include "bpf_util.h"
 | 
						|
 | 
						|
#define CGRP_PROC_FILE  "/cgroup.procs"
 | 
						|
 | 
						|
static void usage(void)
 | 
						|
{
 | 
						|
	fprintf(stderr, "Usage: ip vrf exec [NAME] cmd ...\n");
 | 
						|
	fprintf(stderr, "       ip vrf identify [PID]\n");
 | 
						|
	fprintf(stderr, "       ip vrf pids [NAME]\n");
 | 
						|
 | 
						|
	exit(-1);
 | 
						|
}
 | 
						|
 | 
						|
static int ipvrf_identify(int argc, char **argv)
 | 
						|
{
 | 
						|
	char path[PATH_MAX];
 | 
						|
	char buf[4096];
 | 
						|
	char *vrf, *end;
 | 
						|
	int fd, rc = -1;
 | 
						|
	unsigned int pid;
 | 
						|
	ssize_t n;
 | 
						|
 | 
						|
	if (argc < 1)
 | 
						|
		pid = getpid();
 | 
						|
	else if (argc > 1)
 | 
						|
		invarg("Extra arguments specified\n", argv[1]);
 | 
						|
	else if (get_unsigned(&pid, argv[0], 10))
 | 
						|
		invarg("Invalid pid\n", argv[0]);
 | 
						|
 | 
						|
	snprintf(path, sizeof(path), "/proc/%d/cgroup", pid);
 | 
						|
	fd = open(path, O_RDONLY);
 | 
						|
	if (fd < 0) {
 | 
						|
		fprintf(stderr,
 | 
						|
			"Failed to open cgroups file: %s\n", strerror(errno));
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	n = read(fd, buf, sizeof(buf) - 1);
 | 
						|
	if (n < 0) {
 | 
						|
		fprintf(stderr,
 | 
						|
			"Failed to read cgroups file: %s\n", strerror(errno));
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	buf[n] = '\0';
 | 
						|
	vrf = strstr(buf, "::/vrf/");
 | 
						|
	if (vrf) {
 | 
						|
		vrf += 7;  /* skip past "::/vrf/" */
 | 
						|
		end = strchr(vrf, '\n');
 | 
						|
		if (end)
 | 
						|
			*end = '\0';
 | 
						|
 | 
						|
		printf("%s\n", vrf);
 | 
						|
	}
 | 
						|
 | 
						|
	rc = 0;
 | 
						|
out:
 | 
						|
	close(fd);
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static int ipvrf_pids(int argc, char **argv)
 | 
						|
{
 | 
						|
	char path[PATH_MAX];
 | 
						|
	char buf[4096];
 | 
						|
	char *mnt, *vrf;
 | 
						|
	int fd, rc = -1;
 | 
						|
	ssize_t n;
 | 
						|
 | 
						|
	if (argc != 1) {
 | 
						|
		fprintf(stderr, "Invalid arguments\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	vrf = argv[0];
 | 
						|
 | 
						|
	mnt = find_cgroup2_mount();
 | 
						|
	if (!mnt)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	snprintf(path, sizeof(path), "%s/vrf/%s%s", mnt, vrf, CGRP_PROC_FILE);
 | 
						|
	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;
 | 
						|
}
 | 
						|
 | 
						|
/* load BPF program to set sk_bound_dev_if for sockets */
 | 
						|
static char bpf_log_buf[256*1024];
 | 
						|
 | 
						|
static int prog_load(int idx)
 | 
						|
{
 | 
						|
	struct bpf_insn prog[] = {
 | 
						|
		BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
 | 
						|
		BPF_MOV64_IMM(BPF_REG_3, idx),
 | 
						|
		BPF_MOV64_IMM(BPF_REG_2,
 | 
						|
			      offsetof(struct bpf_sock, bound_dev_if)),
 | 
						|
		BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
 | 
						|
			    offsetof(struct bpf_sock, bound_dev_if)),
 | 
						|
		BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
 | 
						|
		BPF_EXIT_INSN(),
 | 
						|
	};
 | 
						|
 | 
						|
	return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 | 
						|
			     "GPL", bpf_log_buf, sizeof(bpf_log_buf));
 | 
						|
}
 | 
						|
 | 
						|
static int vrf_configure_cgroup(const char *path, int ifindex)
 | 
						|
{
 | 
						|
	int rc = -1, cg_fd, prog_fd = -1;
 | 
						|
 | 
						|
	cg_fd = open(path, O_DIRECTORY | O_RDONLY);
 | 
						|
	if (cg_fd < 0) {
 | 
						|
		fprintf(stderr,
 | 
						|
			"Failed to open cgroup path: '%s'\n",
 | 
						|
			strerror(errno));
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Load bpf program into kernel and attach to cgroup to affect
 | 
						|
	 * socket creates
 | 
						|
	 */
 | 
						|
	prog_fd = prog_load(ifindex);
 | 
						|
	if (prog_fd < 0) {
 | 
						|
		printf("Failed to load BPF prog: '%s'\n", strerror(errno));
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
 | 
						|
		fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
 | 
						|
			strerror(errno));
 | 
						|
		fprintf(stderr, "Kernel compiled with CGROUP_BPF enabled?\n");
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	rc = 0;
 | 
						|
out:
 | 
						|
	close(cg_fd);
 | 
						|
	close(prog_fd);
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static int vrf_switch(const char *name)
 | 
						|
{
 | 
						|
	char path[PATH_MAX], *mnt, pid[16];
 | 
						|
	int ifindex = name_is_vrf(name);
 | 
						|
	bool default_vrf = false;
 | 
						|
	int rc = -1, len, fd = -1;
 | 
						|
 | 
						|
	if (!ifindex) {
 | 
						|
		if (strcmp(name, "default")) {
 | 
						|
			fprintf(stderr, "Invalid VRF name\n");
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
		default_vrf = true;
 | 
						|
	}
 | 
						|
 | 
						|
	mnt = find_cgroup2_mount();
 | 
						|
	if (!mnt)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	/* 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%s/%s",
 | 
						|
		       mnt, default_vrf ? "" : "/vrf", name);
 | 
						|
	if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) {
 | 
						|
		fprintf(stderr, "Invalid path to cgroup2 mount\n");
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	if (make_path(path, 0755)) {
 | 
						|
		fprintf(stderr, "Failed to setup vrf cgroup2 directory\n");
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!default_vrf && vrf_configure_cgroup(path, ifindex))
 | 
						|
		goto out;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * write pid to cgroup.procs making process part of cgroup
 | 
						|
	 */
 | 
						|
	strcat(path, CGRP_PROC_FILE);
 | 
						|
	fd = open(path, O_RDWR | O_APPEND);
 | 
						|
	if (fd < 0) {
 | 
						|
		fprintf(stderr, "Failed to open cgroups.procs file: %s.\n",
 | 
						|
			strerror(errno));
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	snprintf(pid, sizeof(pid), "%d", getpid());
 | 
						|
	if (write(fd, pid, strlen(pid)) < 0) {
 | 
						|
		fprintf(stderr, "Failed to join cgroup\n");
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	rc = 0;
 | 
						|
out:
 | 
						|
	free(mnt);
 | 
						|
	close(fd);
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static int ipvrf_exec(int argc, char **argv)
 | 
						|
{
 | 
						|
	if (argc < 1) {
 | 
						|
		fprintf(stderr, "No VRF name specified\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	if (argc < 2) {
 | 
						|
		fprintf(stderr, "No command specified\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (vrf_switch(argv[0]))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	return -cmd_exec(argv[1], argv + 1, !!batch_mode);
 | 
						|
}
 | 
						|
 | 
						|
int do_ipvrf(int argc, char **argv)
 | 
						|
{
 | 
						|
	if (argc == 0) {
 | 
						|
		fprintf(stderr, "No command given. Try \"ip vrf help\".\n");
 | 
						|
		exit(-1);
 | 
						|
	}
 | 
						|
 | 
						|
	if (matches(*argv, "identify") == 0)
 | 
						|
		return ipvrf_identify(argc-1, argv+1);
 | 
						|
 | 
						|
	if (matches(*argv, "pids") == 0)
 | 
						|
		return ipvrf_pids(argc-1, argv+1);
 | 
						|
 | 
						|
	if (matches(*argv, "exec") == 0)
 | 
						|
		return ipvrf_exec(argc-1, argv+1);
 | 
						|
 | 
						|
	if (matches(*argv, "help") == 0)
 | 
						|
		usage();
 | 
						|
 | 
						|
	fprintf(stderr, "Command \"%s\" is unknown, try \"ip vrf help\".\n",
 | 
						|
		*argv);
 | 
						|
 | 
						|
	exit(-1);
 | 
						|
}
 |