Back to home page

OSCL-LXR

 
 

    


0001 /* eBPF example program:
0002  *
0003  * - Loads eBPF program
0004  *
0005  *   The eBPF program sets the sk_bound_dev_if index in new AF_INET{6}
0006  *   sockets opened by processes in the cgroup.
0007  *
0008  * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
0009  */
0010 
0011 #define _GNU_SOURCE
0012 
0013 #include <stdio.h>
0014 #include <stdlib.h>
0015 #include <stddef.h>
0016 #include <string.h>
0017 #include <unistd.h>
0018 #include <assert.h>
0019 #include <errno.h>
0020 #include <fcntl.h>
0021 #include <net/if.h>
0022 #include <inttypes.h>
0023 #include <linux/bpf.h>
0024 #include <bpf/bpf.h>
0025 
0026 #include "bpf_insn.h"
0027 
0028 char bpf_log_buf[BPF_LOG_BUF_SIZE];
0029 
0030 static int prog_load(__u32 idx, __u32 mark, __u32 prio)
0031 {
0032     /* save pointer to context */
0033     struct bpf_insn prog_start[] = {
0034         BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0035     };
0036     struct bpf_insn prog_end[] = {
0037         BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
0038         BPF_EXIT_INSN(),
0039     };
0040 
0041     /* set sk_bound_dev_if on socket */
0042     struct bpf_insn prog_dev[] = {
0043         BPF_MOV64_IMM(BPF_REG_3, idx),
0044         BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)),
0045         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)),
0046     };
0047 
0048     /* set mark on socket */
0049     struct bpf_insn prog_mark[] = {
0050         /* get uid of process */
0051         BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
0052                  BPF_FUNC_get_current_uid_gid),
0053         BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff),
0054 
0055         /* if uid is 0, use given mark, else use the uid as the mark */
0056         BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
0057         BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
0058         BPF_MOV64_IMM(BPF_REG_3, mark),
0059 
0060         /* set the mark on the new socket */
0061         BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
0062         BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, mark)),
0063         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, mark)),
0064     };
0065 
0066     /* set priority on socket */
0067     struct bpf_insn prog_prio[] = {
0068         BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
0069         BPF_MOV64_IMM(BPF_REG_3, prio),
0070         BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, priority)),
0071         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, priority)),
0072     };
0073     LIBBPF_OPTS(bpf_prog_load_opts, opts,
0074         .log_buf = bpf_log_buf,
0075         .log_size = BPF_LOG_BUF_SIZE,
0076     );
0077 
0078     struct bpf_insn *prog;
0079     size_t insns_cnt;
0080     void *p;
0081     int ret;
0082 
0083     insns_cnt = sizeof(prog_start) + sizeof(prog_end);
0084     if (idx)
0085         insns_cnt += sizeof(prog_dev);
0086 
0087     if (mark)
0088         insns_cnt += sizeof(prog_mark);
0089 
0090     if (prio)
0091         insns_cnt += sizeof(prog_prio);
0092 
0093     p = prog = malloc(insns_cnt);
0094     if (!prog) {
0095         fprintf(stderr, "Failed to allocate memory for instructions\n");
0096         return EXIT_FAILURE;
0097     }
0098 
0099     memcpy(p, prog_start, sizeof(prog_start));
0100     p += sizeof(prog_start);
0101 
0102     if (idx) {
0103         memcpy(p, prog_dev, sizeof(prog_dev));
0104         p += sizeof(prog_dev);
0105     }
0106 
0107     if (mark) {
0108         memcpy(p, prog_mark, sizeof(prog_mark));
0109         p += sizeof(prog_mark);
0110     }
0111 
0112     if (prio) {
0113         memcpy(p, prog_prio, sizeof(prog_prio));
0114         p += sizeof(prog_prio);
0115     }
0116 
0117     memcpy(p, prog_end, sizeof(prog_end));
0118     p += sizeof(prog_end);
0119 
0120     insns_cnt /= sizeof(struct bpf_insn);
0121 
0122     ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL",
0123                 prog, insns_cnt, &opts);
0124 
0125     free(prog);
0126 
0127     return ret;
0128 }
0129 
0130 static int get_bind_to_device(int sd, char *name, size_t len)
0131 {
0132     socklen_t optlen = len;
0133     int rc;
0134 
0135     name[0] = '\0';
0136     rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen);
0137     if (rc < 0)
0138         perror("setsockopt(SO_BINDTODEVICE)");
0139 
0140     return rc;
0141 }
0142 
0143 static unsigned int get_somark(int sd)
0144 {
0145     unsigned int mark = 0;
0146     socklen_t optlen = sizeof(mark);
0147     int rc;
0148 
0149     rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen);
0150     if (rc < 0)
0151         perror("getsockopt(SO_MARK)");
0152 
0153     return mark;
0154 }
0155 
0156 static unsigned int get_priority(int sd)
0157 {
0158     unsigned int prio = 0;
0159     socklen_t optlen = sizeof(prio);
0160     int rc;
0161 
0162     rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen);
0163     if (rc < 0)
0164         perror("getsockopt(SO_PRIORITY)");
0165 
0166     return prio;
0167 }
0168 
0169 static int show_sockopts(int family)
0170 {
0171     unsigned int mark, prio;
0172     char name[16];
0173     int sd;
0174 
0175     sd = socket(family, SOCK_DGRAM, 17);
0176     if (sd < 0) {
0177         perror("socket");
0178         return 1;
0179     }
0180 
0181     if (get_bind_to_device(sd, name, sizeof(name)) < 0)
0182         return 1;
0183 
0184     mark = get_somark(sd);
0185     prio = get_priority(sd);
0186 
0187     close(sd);
0188 
0189     printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio);
0190 
0191     return 0;
0192 }
0193 
0194 static int usage(const char *argv0)
0195 {
0196     printf("Usage:\n");
0197     printf("  Attach a program\n");
0198     printf("  %s -b bind-to-dev -m mark -p prio cg-path\n", argv0);
0199     printf("\n");
0200     printf("  Detach a program\n");
0201     printf("  %s -d cg-path\n", argv0);
0202     printf("\n");
0203     printf("  Show inherited socket settings (mark, priority, and device)\n");
0204     printf("  %s [-6]\n", argv0);
0205     return EXIT_FAILURE;
0206 }
0207 
0208 int main(int argc, char **argv)
0209 {
0210     __u32 idx = 0, mark = 0, prio = 0;
0211     const char *cgrp_path = NULL;
0212     int cg_fd, prog_fd, ret;
0213     int family = PF_INET;
0214     int do_attach = 1;
0215     int rc;
0216 
0217     while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) {
0218         switch (rc) {
0219         case 'd':
0220             do_attach = 0;
0221             break;
0222         case 'b':
0223             idx = if_nametoindex(optarg);
0224             if (!idx) {
0225                 idx = strtoumax(optarg, NULL, 0);
0226                 if (!idx) {
0227                     printf("Invalid device name\n");
0228                     return EXIT_FAILURE;
0229                 }
0230             }
0231             break;
0232         case 'm':
0233             mark = strtoumax(optarg, NULL, 0);
0234             break;
0235         case 'p':
0236             prio = strtoumax(optarg, NULL, 0);
0237             break;
0238         case '6':
0239             family = PF_INET6;
0240             break;
0241         default:
0242             return usage(argv[0]);
0243         }
0244     }
0245 
0246     if (optind == argc)
0247         return show_sockopts(family);
0248 
0249     cgrp_path = argv[optind];
0250     if (!cgrp_path) {
0251         fprintf(stderr, "cgroup path not given\n");
0252         return EXIT_FAILURE;
0253     }
0254 
0255     if (do_attach && !idx && !mark && !prio) {
0256         fprintf(stderr,
0257             "One of device, mark or priority must be given\n");
0258         return EXIT_FAILURE;
0259     }
0260 
0261     cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY);
0262     if (cg_fd < 0) {
0263         printf("Failed to open cgroup path: '%s'\n", strerror(errno));
0264         return EXIT_FAILURE;
0265     }
0266 
0267     if (do_attach) {
0268         prog_fd = prog_load(idx, mark, prio);
0269         if (prog_fd < 0) {
0270             printf("Failed to load prog: '%s'\n", strerror(errno));
0271             printf("Output from kernel verifier:\n%s\n-------\n",
0272                    bpf_log_buf);
0273             return EXIT_FAILURE;
0274         }
0275 
0276         ret = bpf_prog_attach(prog_fd, cg_fd,
0277                       BPF_CGROUP_INET_SOCK_CREATE, 0);
0278         if (ret < 0) {
0279             printf("Failed to attach prog to cgroup: '%s'\n",
0280                    strerror(errno));
0281             return EXIT_FAILURE;
0282         }
0283     } else {
0284         ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE);
0285         if (ret < 0) {
0286             printf("Failed to detach prog from cgroup: '%s'\n",
0287                    strerror(errno));
0288             return EXIT_FAILURE;
0289         }
0290     }
0291 
0292     close(cg_fd);
0293     return EXIT_SUCCESS;
0294 }