+struct lwt_x {
+ struct rtattr *rta;
+ size_t len;
+};
+
+static void bpf_lwt_cb(void *lwt_ptr, int fd, const char *annotation)
+{
+ struct lwt_x *x = lwt_ptr;
+
+ rta_addattr32(x->rta, x->len, LWT_BPF_PROG_FD, fd);
+ rta_addattr_l(x->rta, x->len, LWT_BPF_PROG_NAME, annotation,
+ strlen(annotation) + 1);
+}
+
+static const struct bpf_cfg_ops bpf_cb_ops = {
+ .ebpf_cb = bpf_lwt_cb,
+};
+
+static int lwt_parse_bpf(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
+ int attr, const enum bpf_prog_type bpf_type)
+{
+ struct bpf_cfg_in cfg = {
+ .argc = *argcp,
+ .argv = *argvp,
+ };
+ struct lwt_x x = {
+ .rta = rta,
+ .len = len,
+ };
+ struct rtattr *nest;
+ int err;
+
+ nest = rta_nest(rta, len, attr);
+ err = bpf_parse_common(bpf_type, &cfg, &bpf_cb_ops, &x);
+ if (err < 0) {
+ fprintf(stderr, "Failed to parse eBPF program: %s\n", strerror(err));
+ return -1;
+ }
+ rta_nest_end(rta, nest);
+
+ *argcp = cfg.argc;
+ *argvp = cfg.argv;
+
+ return 0;
+}
+
+static void lwt_bpf_usage(void)
+{
+ fprintf(stderr, "Usage: ip route ... encap bpf [ in BPF ] [ out BPF ] [ xmit BPF ] [...]\n");
+ fprintf(stderr, "BPF := obj FILE [ section NAME ] [ verbose ]\n");
+ exit(-1);
+}
+
+static int parse_encap_bpf(struct rtattr *rta, size_t len, int *argcp,
+ char ***argvp)
+{
+ char **argv = *argvp;
+ int argc = *argcp;
+ int headroom_set = 0;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "in") == 0) {
+ NEXT_ARG();
+ if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_IN,
+ BPF_PROG_TYPE_LWT_IN) < 0)
+ return -1;
+ } else if (strcmp(*argv, "out") == 0) {
+ NEXT_ARG();
+ if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_OUT,
+ BPF_PROG_TYPE_LWT_OUT) < 0)
+ return -1;
+ } else if (strcmp(*argv, "xmit") == 0) {
+ NEXT_ARG();
+ if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_XMIT,
+ BPF_PROG_TYPE_LWT_XMIT) < 0)
+ return -1;
+ } else if (strcmp(*argv, "headroom") == 0) {
+ unsigned int headroom;
+
+ NEXT_ARG();
+ if (get_unsigned(&headroom, *argv, 0) || headroom == 0)
+ invarg("headroom is invalid\n", *argv);
+ if (!headroom_set)
+ rta_addattr32(rta, 1024, LWT_BPF_XMIT_HEADROOM,
+ headroom);
+ headroom_set = 1;
+ } else if (strcmp(*argv, "help") == 0) {
+ lwt_bpf_usage();
+ } else {
+ break;
+ }
+ NEXT_ARG_FWD();
+ }
+
+ /* argv is currently the first unparsed argument,
+ * but the lwt_parse_encap() caller will move to the next,
+ * so step back */
+ *argcp = argc + 1;
+ *argvp = argv - 1;
+
+ return 0;
+}
+