Home | History | Annotate | Download | only in tc
      1 /*
      2  * e_bpf.c	BPF exec proxy
      3  *
      4  *		This program is free software; you can distribute it and/or
      5  *		modify it under the terms of the GNU General Public License
      6  *		as published by the Free Software Foundation; either version
      7  *		2 of the License, or (at your option) any later version.
      8  *
      9  * Authors:	Daniel Borkmann <daniel (at) iogearbox.net>
     10  */
     11 
     12 #include <stdio.h>
     13 #include <unistd.h>
     14 
     15 #include "utils.h"
     16 
     17 #include "tc_util.h"
     18 #include "tc_bpf.h"
     19 
     20 #include "bpf_elf.h"
     21 #include "bpf_scm.h"
     22 
     23 #define BPF_DEFAULT_CMD	"/bin/sh"
     24 
     25 static char *argv_default[] = { BPF_DEFAULT_CMD, NULL };
     26 
     27 static void explain(void)
     28 {
     29 	fprintf(stderr, "Usage: ... bpf [ import UDS_FILE ] [ run CMD ]\n");
     30 	fprintf(stderr, "       ... bpf [ debug ]\n");
     31 	fprintf(stderr, "       ... bpf [ graft MAP_FILE ] [ key KEY ]\n");
     32 	fprintf(stderr, "          `... [ object-file OBJ_FILE ] [ type TYPE ] [ section NAME ] [ verbose ]\n");
     33 	fprintf(stderr, "          `... [ object-pinned PROG_FILE ]\n");
     34 	fprintf(stderr, "\n");
     35 	fprintf(stderr, "Where UDS_FILE provides the name of a unix domain socket file\n");
     36 	fprintf(stderr, "to import eBPF maps and the optional CMD denotes the command\n");
     37 	fprintf(stderr, "to be executed (default: \'%s\').\n", BPF_DEFAULT_CMD);
     38 	fprintf(stderr, "Where MAP_FILE points to a pinned map, OBJ_FILE to an object file\n");
     39 	fprintf(stderr, "and PROG_FILE to a pinned program. TYPE can be {cls, act}, where\n");
     40 	fprintf(stderr, "\'cls\' is default. KEY is optional and can be inferred from the\n");
     41 	fprintf(stderr, "section name, otherwise it needs to be provided.\n");
     42 }
     43 
     44 static int bpf_num_env_entries(void)
     45 {
     46 	char **envp;
     47 	int num;
     48 
     49 	for (num = 0, envp = environ; *envp != NULL; envp++)
     50 		num++;
     51 	return num;
     52 }
     53 
     54 static int parse_bpf(struct exec_util *eu, int argc, char **argv)
     55 {
     56 	char **argv_run = argv_default, **envp_run, *tmp;
     57 	int ret, i, env_old, env_num, env_map;
     58 	const char *bpf_uds_name = NULL;
     59 	int fds[BPF_SCM_MAX_FDS];
     60 	struct bpf_map_aux aux;
     61 
     62 	if (argc == 0)
     63 		return 0;
     64 
     65 	while (argc > 0) {
     66 		if (matches(*argv, "run") == 0) {
     67 			NEXT_ARG();
     68 			argv_run = argv;
     69 			break;
     70 		} else if (matches(*argv, "import") == 0) {
     71 			NEXT_ARG();
     72 			bpf_uds_name = *argv;
     73 		} else if (matches(*argv, "debug") == 0 ||
     74 			   matches(*argv, "dbg") == 0) {
     75 			if (bpf_trace_pipe())
     76 				fprintf(stderr,
     77 					"No trace pipe, tracefs not mounted?\n");
     78 			return -1;
     79 		} else if (matches(*argv, "graft") == 0) {
     80 			const char *bpf_map_path;
     81 			bool has_key = false;
     82 			uint32_t key;
     83 
     84 			NEXT_ARG();
     85 			bpf_map_path = *argv;
     86 			NEXT_ARG();
     87 			if (matches(*argv, "key") == 0) {
     88 				NEXT_ARG();
     89 				if (get_unsigned(&key, *argv, 0)) {
     90 					fprintf(stderr, "Illegal \"key\"\n");
     91 					return -1;
     92 				}
     93 				has_key = true;
     94 				NEXT_ARG();
     95 			}
     96 			return bpf_graft_map(bpf_map_path, has_key ?
     97 					     &key : NULL, argc, argv);
     98 		} else {
     99 			explain();
    100 			return -1;
    101 		}
    102 
    103 		NEXT_ARG_FWD();
    104 	}
    105 
    106 	if (!bpf_uds_name) {
    107 		fprintf(stderr, "bpf: No import parameter provided!\n");
    108 		explain();
    109 		return -1;
    110 	}
    111 
    112 	if (argv_run != argv_default && argc == 0) {
    113 		fprintf(stderr, "bpf: No run command provided!\n");
    114 		explain();
    115 		return -1;
    116 	}
    117 
    118 	memset(fds, 0, sizeof(fds));
    119 	memset(&aux, 0, sizeof(aux));
    120 
    121 	ret = bpf_recv_map_fds(bpf_uds_name, fds, &aux, ARRAY_SIZE(fds));
    122 	if (ret < 0) {
    123 		fprintf(stderr, "bpf: Could not receive fds!\n");
    124 		return -1;
    125 	}
    126 
    127 	if (aux.num_ent == 0) {
    128 		envp_run = environ;
    129 		goto out;
    130 	}
    131 
    132 	env_old = bpf_num_env_entries();
    133 	env_num = env_old + aux.num_ent + 2;
    134 	env_map = env_old + 1;
    135 
    136 	envp_run = malloc(sizeof(*envp_run) * env_num);
    137 	if (!envp_run) {
    138 		fprintf(stderr, "bpf: No memory left to allocate env!\n");
    139 		goto err;
    140 	}
    141 
    142 	for (i = 0; i < env_old; i++)
    143 		envp_run[i] = environ[i];
    144 
    145 	ret = asprintf(&tmp, "BPF_NUM_MAPS=%u", aux.num_ent);
    146 	if (ret < 0)
    147 		goto err_free;
    148 
    149 	envp_run[env_old] = tmp;
    150 
    151 	for (i = env_map; i < env_num - 1; i++) {
    152 		ret = asprintf(&tmp, "BPF_MAP%u=%u",
    153 			       aux.ent[i - env_map].id,
    154 			       fds[i - env_map]);
    155 		if (ret < 0)
    156 			goto err_free_env;
    157 
    158 		envp_run[i] = tmp;
    159 	}
    160 
    161 	envp_run[env_num - 1] = NULL;
    162 out:
    163 	return execvpe(argv_run[0], argv_run, envp_run);
    164 
    165 err_free_env:
    166 	for (--i; i >= env_old; i--)
    167 		free(envp_run[i]);
    168 err_free:
    169 	free(envp_run);
    170 err:
    171 	for (i = 0; i < aux.num_ent; i++)
    172 		close(fds[i]);
    173 	return -1;
    174 }
    175 
    176 struct exec_util bpf_exec_util = {
    177 	.id		= "bpf",
    178 	.parse_eopt	= parse_bpf,
    179 };
    180