18c2ecf20Sopenharmony_ci/* eBPF example program:
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * - Loads eBPF program
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   The eBPF program sets the sk_bound_dev_if index in new AF_INET{6}
68c2ecf20Sopenharmony_ci *   sockets opened by processes in the cgroup.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define _GNU_SOURCE
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <stdio.h>
148c2ecf20Sopenharmony_ci#include <stdlib.h>
158c2ecf20Sopenharmony_ci#include <stddef.h>
168c2ecf20Sopenharmony_ci#include <string.h>
178c2ecf20Sopenharmony_ci#include <unistd.h>
188c2ecf20Sopenharmony_ci#include <assert.h>
198c2ecf20Sopenharmony_ci#include <errno.h>
208c2ecf20Sopenharmony_ci#include <fcntl.h>
218c2ecf20Sopenharmony_ci#include <net/if.h>
228c2ecf20Sopenharmony_ci#include <inttypes.h>
238c2ecf20Sopenharmony_ci#include <linux/bpf.h>
248c2ecf20Sopenharmony_ci#include <bpf/bpf.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "bpf_insn.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cichar bpf_log_buf[BPF_LOG_BUF_SIZE];
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int prog_load(__u32 idx, __u32 mark, __u32 prio)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	/* save pointer to context */
338c2ecf20Sopenharmony_ci	struct bpf_insn prog_start[] = {
348c2ecf20Sopenharmony_ci		BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
358c2ecf20Sopenharmony_ci	};
368c2ecf20Sopenharmony_ci	struct bpf_insn prog_end[] = {
378c2ecf20Sopenharmony_ci		BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
388c2ecf20Sopenharmony_ci		BPF_EXIT_INSN(),
398c2ecf20Sopenharmony_ci	};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* set sk_bound_dev_if on socket */
428c2ecf20Sopenharmony_ci	struct bpf_insn prog_dev[] = {
438c2ecf20Sopenharmony_ci		BPF_MOV64_IMM(BPF_REG_3, idx),
448c2ecf20Sopenharmony_ci		BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)),
458c2ecf20Sopenharmony_ci		BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)),
468c2ecf20Sopenharmony_ci	};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/* set mark on socket */
498c2ecf20Sopenharmony_ci	struct bpf_insn prog_mark[] = {
508c2ecf20Sopenharmony_ci		/* get uid of process */
518c2ecf20Sopenharmony_ci		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
528c2ecf20Sopenharmony_ci			     BPF_FUNC_get_current_uid_gid),
538c2ecf20Sopenharmony_ci		BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff),
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		/* if uid is 0, use given mark, else use the uid as the mark */
568c2ecf20Sopenharmony_ci		BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
578c2ecf20Sopenharmony_ci		BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
588c2ecf20Sopenharmony_ci		BPF_MOV64_IMM(BPF_REG_3, mark),
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		/* set the mark on the new socket */
618c2ecf20Sopenharmony_ci		BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
628c2ecf20Sopenharmony_ci		BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, mark)),
638c2ecf20Sopenharmony_ci		BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, mark)),
648c2ecf20Sopenharmony_ci	};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* set priority on socket */
678c2ecf20Sopenharmony_ci	struct bpf_insn prog_prio[] = {
688c2ecf20Sopenharmony_ci		BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
698c2ecf20Sopenharmony_ci		BPF_MOV64_IMM(BPF_REG_3, prio),
708c2ecf20Sopenharmony_ci		BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, priority)),
718c2ecf20Sopenharmony_ci		BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, priority)),
728c2ecf20Sopenharmony_ci	};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	struct bpf_insn *prog;
758c2ecf20Sopenharmony_ci	size_t insns_cnt;
768c2ecf20Sopenharmony_ci	void *p;
778c2ecf20Sopenharmony_ci	int ret;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	insns_cnt = sizeof(prog_start) + sizeof(prog_end);
808c2ecf20Sopenharmony_ci	if (idx)
818c2ecf20Sopenharmony_ci		insns_cnt += sizeof(prog_dev);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (mark)
848c2ecf20Sopenharmony_ci		insns_cnt += sizeof(prog_mark);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (prio)
878c2ecf20Sopenharmony_ci		insns_cnt += sizeof(prog_prio);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	p = prog = malloc(insns_cnt);
908c2ecf20Sopenharmony_ci	if (!prog) {
918c2ecf20Sopenharmony_ci		fprintf(stderr, "Failed to allocate memory for instructions\n");
928c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	memcpy(p, prog_start, sizeof(prog_start));
968c2ecf20Sopenharmony_ci	p += sizeof(prog_start);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (idx) {
998c2ecf20Sopenharmony_ci		memcpy(p, prog_dev, sizeof(prog_dev));
1008c2ecf20Sopenharmony_ci		p += sizeof(prog_dev);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (mark) {
1048c2ecf20Sopenharmony_ci		memcpy(p, prog_mark, sizeof(prog_mark));
1058c2ecf20Sopenharmony_ci		p += sizeof(prog_mark);
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (prio) {
1098c2ecf20Sopenharmony_ci		memcpy(p, prog_prio, sizeof(prog_prio));
1108c2ecf20Sopenharmony_ci		p += sizeof(prog_prio);
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	memcpy(p, prog_end, sizeof(prog_end));
1148c2ecf20Sopenharmony_ci	p += sizeof(prog_end);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	insns_cnt /= sizeof(struct bpf_insn);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, insns_cnt,
1198c2ecf20Sopenharmony_ci				"GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	free(prog);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return ret;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int get_bind_to_device(int sd, char *name, size_t len)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	socklen_t optlen = len;
1298c2ecf20Sopenharmony_ci	int rc;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	name[0] = '\0';
1328c2ecf20Sopenharmony_ci	rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen);
1338c2ecf20Sopenharmony_ci	if (rc < 0)
1348c2ecf20Sopenharmony_ci		perror("setsockopt(SO_BINDTODEVICE)");
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return rc;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic unsigned int get_somark(int sd)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	unsigned int mark = 0;
1428c2ecf20Sopenharmony_ci	socklen_t optlen = sizeof(mark);
1438c2ecf20Sopenharmony_ci	int rc;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen);
1468c2ecf20Sopenharmony_ci	if (rc < 0)
1478c2ecf20Sopenharmony_ci		perror("getsockopt(SO_MARK)");
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return mark;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic unsigned int get_priority(int sd)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	unsigned int prio = 0;
1558c2ecf20Sopenharmony_ci	socklen_t optlen = sizeof(prio);
1568c2ecf20Sopenharmony_ci	int rc;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen);
1598c2ecf20Sopenharmony_ci	if (rc < 0)
1608c2ecf20Sopenharmony_ci		perror("getsockopt(SO_PRIORITY)");
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return prio;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int show_sockopts(int family)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	unsigned int mark, prio;
1688c2ecf20Sopenharmony_ci	char name[16];
1698c2ecf20Sopenharmony_ci	int sd;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	sd = socket(family, SOCK_DGRAM, 17);
1728c2ecf20Sopenharmony_ci	if (sd < 0) {
1738c2ecf20Sopenharmony_ci		perror("socket");
1748c2ecf20Sopenharmony_ci		return 1;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (get_bind_to_device(sd, name, sizeof(name)) < 0)
1788c2ecf20Sopenharmony_ci		return 1;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	mark = get_somark(sd);
1818c2ecf20Sopenharmony_ci	prio = get_priority(sd);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	close(sd);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic int usage(const char *argv0)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	printf("Usage:\n");
1938c2ecf20Sopenharmony_ci	printf("  Attach a program\n");
1948c2ecf20Sopenharmony_ci	printf("  %s -b bind-to-dev -m mark -p prio cg-path\n", argv0);
1958c2ecf20Sopenharmony_ci	printf("\n");
1968c2ecf20Sopenharmony_ci	printf("  Detach a program\n");
1978c2ecf20Sopenharmony_ci	printf("  %s -d cg-path\n", argv0);
1988c2ecf20Sopenharmony_ci	printf("\n");
1998c2ecf20Sopenharmony_ci	printf("  Show inherited socket settings (mark, priority, and device)\n");
2008c2ecf20Sopenharmony_ci	printf("  %s [-6]\n", argv0);
2018c2ecf20Sopenharmony_ci	return EXIT_FAILURE;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ciint main(int argc, char **argv)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	__u32 idx = 0, mark = 0, prio = 0;
2078c2ecf20Sopenharmony_ci	const char *cgrp_path = NULL;
2088c2ecf20Sopenharmony_ci	int cg_fd, prog_fd, ret;
2098c2ecf20Sopenharmony_ci	int family = PF_INET;
2108c2ecf20Sopenharmony_ci	int do_attach = 1;
2118c2ecf20Sopenharmony_ci	int rc;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) {
2148c2ecf20Sopenharmony_ci		switch (rc) {
2158c2ecf20Sopenharmony_ci		case 'd':
2168c2ecf20Sopenharmony_ci			do_attach = 0;
2178c2ecf20Sopenharmony_ci			break;
2188c2ecf20Sopenharmony_ci		case 'b':
2198c2ecf20Sopenharmony_ci			idx = if_nametoindex(optarg);
2208c2ecf20Sopenharmony_ci			if (!idx) {
2218c2ecf20Sopenharmony_ci				idx = strtoumax(optarg, NULL, 0);
2228c2ecf20Sopenharmony_ci				if (!idx) {
2238c2ecf20Sopenharmony_ci					printf("Invalid device name\n");
2248c2ecf20Sopenharmony_ci					return EXIT_FAILURE;
2258c2ecf20Sopenharmony_ci				}
2268c2ecf20Sopenharmony_ci			}
2278c2ecf20Sopenharmony_ci			break;
2288c2ecf20Sopenharmony_ci		case 'm':
2298c2ecf20Sopenharmony_ci			mark = strtoumax(optarg, NULL, 0);
2308c2ecf20Sopenharmony_ci			break;
2318c2ecf20Sopenharmony_ci		case 'p':
2328c2ecf20Sopenharmony_ci			prio = strtoumax(optarg, NULL, 0);
2338c2ecf20Sopenharmony_ci			break;
2348c2ecf20Sopenharmony_ci		case '6':
2358c2ecf20Sopenharmony_ci			family = PF_INET6;
2368c2ecf20Sopenharmony_ci			break;
2378c2ecf20Sopenharmony_ci		default:
2388c2ecf20Sopenharmony_ci			return usage(argv[0]);
2398c2ecf20Sopenharmony_ci		}
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (optind == argc)
2438c2ecf20Sopenharmony_ci		return show_sockopts(family);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	cgrp_path = argv[optind];
2468c2ecf20Sopenharmony_ci	if (!cgrp_path) {
2478c2ecf20Sopenharmony_ci		fprintf(stderr, "cgroup path not given\n");
2488c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (do_attach && !idx && !mark && !prio) {
2528c2ecf20Sopenharmony_ci		fprintf(stderr,
2538c2ecf20Sopenharmony_ci			"One of device, mark or priority must be given\n");
2548c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY);
2588c2ecf20Sopenharmony_ci	if (cg_fd < 0) {
2598c2ecf20Sopenharmony_ci		printf("Failed to open cgroup path: '%s'\n", strerror(errno));
2608c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (do_attach) {
2648c2ecf20Sopenharmony_ci		prog_fd = prog_load(idx, mark, prio);
2658c2ecf20Sopenharmony_ci		if (prog_fd < 0) {
2668c2ecf20Sopenharmony_ci			printf("Failed to load prog: '%s'\n", strerror(errno));
2678c2ecf20Sopenharmony_ci			printf("Output from kernel verifier:\n%s\n-------\n",
2688c2ecf20Sopenharmony_ci			       bpf_log_buf);
2698c2ecf20Sopenharmony_ci			return EXIT_FAILURE;
2708c2ecf20Sopenharmony_ci		}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		ret = bpf_prog_attach(prog_fd, cg_fd,
2738c2ecf20Sopenharmony_ci				      BPF_CGROUP_INET_SOCK_CREATE, 0);
2748c2ecf20Sopenharmony_ci		if (ret < 0) {
2758c2ecf20Sopenharmony_ci			printf("Failed to attach prog to cgroup: '%s'\n",
2768c2ecf20Sopenharmony_ci			       strerror(errno));
2778c2ecf20Sopenharmony_ci			return EXIT_FAILURE;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci	} else {
2808c2ecf20Sopenharmony_ci		ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE);
2818c2ecf20Sopenharmony_ci		if (ret < 0) {
2828c2ecf20Sopenharmony_ci			printf("Failed to detach prog from cgroup: '%s'\n",
2838c2ecf20Sopenharmony_ci			       strerror(errno));
2848c2ecf20Sopenharmony_ci			return EXIT_FAILURE;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	close(cg_fd);
2898c2ecf20Sopenharmony_ci	return EXIT_SUCCESS;
2908c2ecf20Sopenharmony_ci}
291