#include <linux/seg6_iptunnel.h>
#include <linux/seg6_hmac.h>
#include <linux/seg6_local.h>
-#include <net/if.h>
+#include <linux/if_tunnel.h>
static const char *format_encap_type(int type)
{
tlv = (struct sr6_tlv_hmac *)((char *)srh + offset);
print_0xhex(PRINT_ANY, "hmac",
- "hmac 0x%X ", ntohl(tlv->hmackeyid));
+ "hmac %llX ", ntohl(tlv->hmackeyid));
}
}
[SEG6_LOCAL_ACTION_END_S] = "End.S",
[SEG6_LOCAL_ACTION_END_AS] = "End.AS",
[SEG6_LOCAL_ACTION_END_AM] = "End.AM",
+ [SEG6_LOCAL_ACTION_END_BPF] = "End.BPF",
};
static const char *format_action_type(int action)
return SEG6_LOCAL_ACTION_UNSPEC;
}
+static void print_encap_bpf_prog(FILE *fp, struct rtattr *encap,
+ const char *str)
+{
+ struct rtattr *tb[LWT_BPF_PROG_MAX+1];
+ const char *progname = NULL;
+
+ parse_rtattr_nested(tb, LWT_BPF_PROG_MAX, encap);
+
+ if (tb[LWT_BPF_PROG_NAME])
+ progname = rta_getattr_str(tb[LWT_BPF_PROG_NAME]);
+
+ if (is_json_context())
+ print_string(PRINT_JSON, str, NULL,
+ progname ? : "<unknown>");
+ else {
+ fprintf(fp, "%s ", str);
+ if (progname)
+ fprintf(fp, "%s ", progname);
+ }
+}
+
static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
{
struct rtattr *tb[SEG6_LOCAL_MAX + 1];
int action;
+ SPRINT_BUF(b1);
+
parse_rtattr_nested(tb, SEG6_LOCAL_MAX, encap);
if (!tb[SEG6_LOCAL_ACTION])
}
if (tb[SEG6_LOCAL_TABLE])
- print_uint(PRINT_ANY, "table",
- "table %u ", rta_getattr_u32(tb[SEG6_LOCAL_TABLE]));
+ print_string(PRINT_ANY, "table", "table %s ",
+ rtnl_rttable_n2a(rta_getattr_u32(tb[SEG6_LOCAL_TABLE]),
+ b1, sizeof(b1)));
if (tb[SEG6_LOCAL_NH4]) {
print_string(PRINT_ANY, "nh4",
print_string(PRINT_ANY, "oif",
"oif %s ", ll_index_to_name(oif));
}
+
+ if (tb[SEG6_LOCAL_BPF])
+ print_encap_bpf_prog(fp, tb[SEG6_LOCAL_BPF], "endpoint");
}
static void print_encap_mpls(FILE *fp, struct rtattr *encap)
rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL]));
}
+static void lwtunnel_print_geneve_opts(struct rtattr *attr)
+{
+ struct rtattr *tb[LWTUNNEL_IP_OPT_GENEVE_MAX + 1];
+ struct rtattr *i = RTA_DATA(attr);
+ int rem = RTA_PAYLOAD(attr);
+ char *name = "geneve_opts";
+ int data_len, offset = 0;
+ char data[rem * 2 + 1];
+ __u16 class;
+ __u8 type;
+
+ print_nl();
+ print_string(PRINT_FP, name, "\t%s ", name);
+ open_json_array(PRINT_JSON, name);
+
+ while (rem) {
+ parse_rtattr(tb, LWTUNNEL_IP_OPT_GENEVE_MAX, i, rem);
+ class = rta_getattr_be16(tb[LWTUNNEL_IP_OPT_GENEVE_CLASS]);
+ type = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_GENEVE_TYPE]);
+ data_len = RTA_PAYLOAD(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]);
+ hexstring_n2a(RTA_DATA(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]),
+ data_len, data, sizeof(data));
+ offset += data_len + 20;
+ rem -= data_len + 20;
+ i = RTA_DATA(attr) + offset;
+
+ open_json_object(NULL);
+ print_uint(PRINT_ANY, "class", "%u", class);
+ print_uint(PRINT_ANY, "type", ":%u", type);
+ if (rem)
+ print_string(PRINT_ANY, "data", ":%s,", data);
+ else
+ print_string(PRINT_ANY, "data", ":%s ", data);
+ close_json_object();
+ }
+
+ close_json_array(PRINT_JSON, name);
+}
+
+static void lwtunnel_print_vxlan_opts(struct rtattr *attr)
+{
+ struct rtattr *tb[LWTUNNEL_IP_OPT_VXLAN_MAX + 1];
+ struct rtattr *i = RTA_DATA(attr);
+ int rem = RTA_PAYLOAD(attr);
+ char *name = "vxlan_opts";
+ __u32 gbp;
+
+ parse_rtattr(tb, LWTUNNEL_IP_OPT_VXLAN_MAX, i, rem);
+ gbp = rta_getattr_u32(tb[LWTUNNEL_IP_OPT_VXLAN_GBP]);
+
+ print_nl();
+ print_string(PRINT_FP, name, "\t%s ", name);
+ open_json_array(PRINT_JSON, name);
+ open_json_object(NULL);
+ print_uint(PRINT_ANY, "gbp", "%u ", gbp);
+ close_json_object();
+ close_json_array(PRINT_JSON, name);
+}
+
+static void lwtunnel_print_opts(struct rtattr *attr)
+{
+ struct rtattr *tb_opt[LWTUNNEL_IP_OPTS_MAX + 1];
+
+ parse_rtattr_nested(tb_opt, LWTUNNEL_IP_OPTS_MAX, attr);
+ if (tb_opt[LWTUNNEL_IP_OPTS_GENEVE])
+ lwtunnel_print_geneve_opts(tb_opt[LWTUNNEL_IP_OPTS_GENEVE]);
+ else if (tb_opt[LWTUNNEL_IP_OPTS_VXLAN])
+ lwtunnel_print_vxlan_opts(tb_opt[LWTUNNEL_IP_OPTS_VXLAN]);
+}
+
static void print_encap_ip(FILE *fp, struct rtattr *encap)
{
struct rtattr *tb[LWTUNNEL_IP_MAX+1];
+ __u16 flags;
parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap);
if (tb[LWTUNNEL_IP_TOS])
print_uint(PRINT_ANY, "tos",
"tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS]));
+
+ if (tb[LWTUNNEL_IP_FLAGS]) {
+ flags = rta_getattr_u16(tb[LWTUNNEL_IP_FLAGS]);
+ if (flags & TUNNEL_KEY)
+ print_bool(PRINT_ANY, "key", "key ", true);
+ if (flags & TUNNEL_CSUM)
+ print_bool(PRINT_ANY, "csum", "csum ", true);
+ if (flags & TUNNEL_SEQ)
+ print_bool(PRINT_ANY, "seq", "seq ", true);
+ }
+
+ if (tb[LWTUNNEL_IP_OPTS])
+ lwtunnel_print_opts(tb[LWTUNNEL_IP_OPTS]);
}
static void print_encap_ila(FILE *fp, struct rtattr *encap)
static void print_encap_ip6(FILE *fp, struct rtattr *encap)
{
struct rtattr *tb[LWTUNNEL_IP6_MAX+1];
+ __u16 flags;
parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap);
if (tb[LWTUNNEL_IP6_TC])
print_uint(PRINT_ANY, "tc",
"tc %u ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC]));
-}
-
-static void print_encap_bpf_prog(FILE *fp, struct rtattr *encap,
- const char *str)
-{
- struct rtattr *tb[LWT_BPF_PROG_MAX+1];
- const char *progname = NULL;
-
- parse_rtattr_nested(tb, LWT_BPF_PROG_MAX, encap);
- if (tb[LWT_BPF_PROG_NAME])
- progname = rta_getattr_str(tb[LWT_BPF_PROG_NAME]);
-
- if (is_json_context())
- print_string(PRINT_JSON, str, NULL,
- progname ? : "<unknown>");
- else {
- fprintf(fp, "%s ", str);
- if (progname)
- fprintf(fp, "%s ", progname);
+ if (tb[LWTUNNEL_IP6_FLAGS]) {
+ flags = rta_getattr_u16(tb[LWTUNNEL_IP6_FLAGS]);
+ if (flags & TUNNEL_KEY)
+ print_bool(PRINT_ANY, "key", "key ", true);
+ if (flags & TUNNEL_CSUM)
+ print_bool(PRINT_ANY, "csum", "csum ", true);
+ if (flags & TUNNEL_SEQ)
+ print_bool(PRINT_ANY, "seq", "seq ", true);
}
+
+ if (tb[LWTUNNEL_IP6_OPTS])
+ lwtunnel_print_opts(tb[LWTUNNEL_IP6_OPTS]);
}
static void print_encap_bpf(FILE *fp, struct rtattr *encap)
struct seg6_iptunnel_encap *tuninfo;
struct ipv6_sr_hdr *srh;
char **argv = *argvp;
- char segbuf[1024];
+ char segbuf[1024] = "";
int argc = *argcp;
int encap = -1;
__u32 hmac = 0;
+ int ret = 0;
int srhlen;
while (argc > 0) {
memcpy(tuninfo->srh, srh, srhlen);
- rta_addattr_l(rta, len, SEG6_IPTUNNEL_SRH, tuninfo,
- sizeof(*tuninfo) + srhlen);
+ if (rta_addattr_l(rta, len, SEG6_IPTUNNEL_SRH, tuninfo,
+ sizeof(*tuninfo) + srhlen)) {
+ ret = -1;
+ goto out;
+ }
+
+ *argcp = argc + 1;
+ *argvp = argv - 1;
+out:
free(tuninfo);
free(srh);
- *argcp = argc + 1;
- *argvp = argv - 1;
+ return ret;
+}
+
+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 = {
+ .type = bpf_type,
+ .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_and_load_common(&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;
}
char ***argvp)
{
int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0;
- int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0;
+ int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0, bpf_ok = 0;
__u32 action = 0, table, iif, oif;
struct ipv6_sr_hdr *srh;
char **argv = *argvp;
char segbuf[1024];
inet_prefix addr;
__u32 hmac = 0;
+ int ret = 0;
while (argc > 0) {
if (strcmp(*argv, "action") == 0) {
action = read_action_type(*argv);
if (!action)
invarg("\"action\" value is invalid\n", *argv);
- rta_addattr32(rta, len, SEG6_LOCAL_ACTION, action);
+ ret = rta_addattr32(rta, len, SEG6_LOCAL_ACTION,
+ action);
} else if (strcmp(*argv, "table") == 0) {
NEXT_ARG();
if (table_ok++)
duparg2("table", *argv);
- get_u32(&table, *argv, 0);
- rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table);
+ rtnl_rttable_a2n(&table, *argv);
+ ret = rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table);
} else if (strcmp(*argv, "nh4") == 0) {
NEXT_ARG();
if (nh4_ok++)
duparg2("nh4", *argv);
get_addr(&addr, *argv, AF_INET);
- rta_addattr_l(rta, len, SEG6_LOCAL_NH4, &addr.data,
- addr.bytelen);
+ ret = rta_addattr_l(rta, len, SEG6_LOCAL_NH4,
+ &addr.data, addr.bytelen);
} else if (strcmp(*argv, "nh6") == 0) {
NEXT_ARG();
if (nh6_ok++)
duparg2("nh6", *argv);
get_addr(&addr, *argv, AF_INET6);
- rta_addattr_l(rta, len, SEG6_LOCAL_NH6, &addr.data,
- addr.bytelen);
+ ret = rta_addattr_l(rta, len, SEG6_LOCAL_NH6,
+ &addr.data, addr.bytelen);
} else if (strcmp(*argv, "iif") == 0) {
NEXT_ARG();
if (iif_ok++)
iif = ll_name_to_index(*argv);
if (!iif)
exit(nodev(*argv));
- rta_addattr32(rta, len, SEG6_LOCAL_IIF, iif);
+ ret = rta_addattr32(rta, len, SEG6_LOCAL_IIF, iif);
} else if (strcmp(*argv, "oif") == 0) {
NEXT_ARG();
if (oif_ok++)
oif = ll_name_to_index(*argv);
if (!oif)
exit(nodev(*argv));
- rta_addattr32(rta, len, SEG6_LOCAL_OIF, oif);
+ ret = rta_addattr32(rta, len, SEG6_LOCAL_OIF, oif);
} else if (strcmp(*argv, "srh") == 0) {
NEXT_ARG();
if (srh_ok++)
} else {
continue;
}
+ } else if (strcmp(*argv, "endpoint") == 0) {
+ NEXT_ARG();
+ if (bpf_ok++)
+ duparg2("endpoint", *argv);
+
+ if (lwt_parse_bpf(rta, len, &argc, &argv, SEG6_LOCAL_BPF,
+ BPF_PROG_TYPE_LWT_SEG6LOCAL) < 0)
+ exit(-1);
} else {
break;
}
+ if (ret)
+ return ret;
argc--; argv++;
}
srh = parse_srh(segbuf, hmac,
action == SEG6_LOCAL_ACTION_END_B6_ENCAP);
srhlen = (srh->hdrlen + 1) << 3;
- rta_addattr_l(rta, len, SEG6_LOCAL_SRH, srh, srhlen);
+ ret = rta_addattr_l(rta, len, SEG6_LOCAL_SRH, srh, srhlen);
free(srh);
}
*argcp = argc + 1;
*argvp = argv - 1;
- return 0;
+ return ret;
}
static int parse_encap_mpls(struct rtattr *rta, size_t len,
exit(1);
}
- rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, &addr.data,
- addr.bytelen);
+ if (rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST,
+ &addr.data, addr.bytelen))
+ return -1;
argc--;
argv++;
duparg2("ttl", *argv);
if (get_u8(&ttl, *argv, 0))
invarg("\"ttl\" value is invalid\n", *argv);
- rta_addattr8(rta, len, MPLS_IPTUNNEL_TTL, ttl);
+ if (rta_addattr8(rta, len, MPLS_IPTUNNEL_TTL, ttl))
+ return -1;
} else {
break;
}
return 0;
}
+static int lwtunnel_parse_geneve_opt(char *str, size_t len, struct rtattr *rta)
+{
+ struct rtattr *nest;
+ char *token;
+ int i, err;
+
+ nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_GENEVE | NLA_F_NESTED);
+ i = 1;
+ token = strsep(&str, ":");
+ while (token) {
+ switch (i) {
+ case LWTUNNEL_IP_OPT_GENEVE_CLASS:
+ {
+ __be16 opt_class;
+
+ if (!strlen(token))
+ break;
+ err = get_be16(&opt_class, token, 0);
+ if (err)
+ return err;
+
+ rta_addattr16(rta, len, i, opt_class);
+ break;
+ }
+ case LWTUNNEL_IP_OPT_GENEVE_TYPE:
+ {
+ __u8 opt_type;
+
+ if (!strlen(token))
+ break;
+ err = get_u8(&opt_type, token, 0);
+ if (err)
+ return err;
+
+ rta_addattr8(rta, len, i, opt_type);
+ break;
+ }
+ case LWTUNNEL_IP_OPT_GENEVE_DATA:
+ {
+ size_t token_len = strlen(token);
+ __u8 *opts;
+
+ if (!token_len)
+ break;
+ opts = malloc(token_len / 2);
+ if (!opts)
+ return -1;
+ if (hex2mem(token, opts, token_len / 2) < 0) {
+ free(opts);
+ return -1;
+ }
+ rta_addattr_l(rta, len, i, opts, token_len / 2);
+ free(opts);
+
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown \"geneve_opts\" type\n");
+ return -1;
+ }
+
+ token = strsep(&str, ":");
+ i++;
+ }
+ rta_nest_end(rta, nest);
+
+ return 0;
+}
+
+static int lwtunnel_parse_geneve_opts(char *str, size_t len, struct rtattr *rta)
+{
+ char *token;
+ int err;
+
+ token = strsep(&str, ",");
+ while (token) {
+ err = lwtunnel_parse_geneve_opt(token, len, rta);
+ if (err)
+ return err;
+
+ token = strsep(&str, ",");
+ }
+
+ return 0;
+}
+
+static int lwtunnel_parse_vxlan_opts(char *str, size_t len, struct rtattr *rta)
+{
+ struct rtattr *nest;
+ __u32 gbp;
+ int err;
+
+ nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_VXLAN | NLA_F_NESTED);
+ err = get_u32(&gbp, str, 0);
+ if (err)
+ return err;
+ rta_addattr32(rta, len, LWTUNNEL_IP_OPT_VXLAN_GBP, gbp);
+
+ rta_nest_end(rta, nest);
+ return 0;
+}
+
static int parse_encap_ip(struct rtattr *rta, size_t len,
int *argcp, char ***argvp)
{
- int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0;
+ int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
+ int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0;
char **argv = *argvp;
int argc = *argcp;
+ int ret = 0;
+ __u16 flags = 0;
while (argc > 0) {
if (strcmp(*argv, "id") == 0) {
duparg2("id", *argv);
if (get_be64(&id, *argv, 0))
invarg("\"id\" value is invalid\n", *argv);
- rta_addattr64(rta, len, LWTUNNEL_IP_ID, id);
+ ret = rta_addattr64(rta, len, LWTUNNEL_IP_ID, id);
} else if (strcmp(*argv, "dst") == 0) {
inet_prefix addr;
if (dst_ok++)
duparg2("dst", *argv);
get_addr(&addr, *argv, AF_INET);
- rta_addattr_l(rta, len, LWTUNNEL_IP_DST,
- &addr.data, addr.bytelen);
+ ret = rta_addattr_l(rta, len, LWTUNNEL_IP_DST,
+ &addr.data, addr.bytelen);
+ } else if (strcmp(*argv, "src") == 0) {
+ inet_prefix addr;
+
+ NEXT_ARG();
+ if (src_ok++)
+ duparg2("src", *argv);
+ get_addr(&addr, *argv, AF_INET);
+ ret = rta_addattr_l(rta, len, LWTUNNEL_IP_SRC,
+ &addr.data, addr.bytelen);
} else if (strcmp(*argv, "tos") == 0) {
__u32 tos;
duparg2("tos", *argv);
if (rtnl_dsfield_a2n(&tos, *argv))
invarg("\"tos\" value is invalid\n", *argv);
- rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos);
+ ret = rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos);
} else if (strcmp(*argv, "ttl") == 0) {
__u8 ttl;
duparg2("ttl", *argv);
if (get_u8(&ttl, *argv, 0))
invarg("\"ttl\" value is invalid\n", *argv);
- rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
+ ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
+ } else if (strcmp(*argv, "geneve_opts") == 0) {
+ struct rtattr *nest;
+
+ if (opts_ok++)
+ duparg2("opts", *argv);
+
+ NEXT_ARG();
+
+ nest = rta_nest(rta, len,
+ LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+ ret = lwtunnel_parse_geneve_opts(*argv, len, rta);
+ if (ret)
+ invarg("\"geneve_opts\" value is invalid\n",
+ *argv);
+ rta_nest_end(rta, nest);
+ } else if (strcmp(*argv, "vxlan_opts") == 0) {
+ struct rtattr *nest;
+
+ if (opts_ok++)
+ duparg2("opts", *argv);
+
+ NEXT_ARG();
+
+ nest = rta_nest(rta, len,
+ LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+ ret = lwtunnel_parse_vxlan_opts(*argv, len, rta);
+ if (ret)
+ invarg("\"vxlan_opts\" value is invalid\n",
+ *argv);
+ rta_nest_end(rta, nest);
+ } else if (strcmp(*argv, "key") == 0) {
+ if (key_ok++)
+ duparg2("key", *argv);
+ flags |= TUNNEL_KEY;
+ } else if (strcmp(*argv, "csum") == 0) {
+ if (csum_ok++)
+ duparg2("csum", *argv);
+ flags |= TUNNEL_CSUM;
+ } else if (strcmp(*argv, "seq") == 0) {
+ if (seq_ok++)
+ duparg2("seq", *argv);
+ flags |= TUNNEL_SEQ;
} else {
break;
}
+ if (ret)
+ break;
argc--; argv++;
}
+ if (flags)
+ ret = rta_addattr16(rta, len, LWTUNNEL_IP_FLAGS, flags);
+
/* 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;
+ return ret;
}
static int parse_encap_ila(struct rtattr *rta, size_t len,
__u64 locator;
int argc = *argcp;
char **argv = *argvp;
+ int ret = 0;
if (get_addr64(&locator, *argv) < 0) {
fprintf(stderr, "Bad locator: %s\n", *argv);
argc--; argv++;
- rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator);
+ if (rta_addattr64(rta, len, ILA_ATTR_LOCATOR, locator))
+ return -1;
while (argc > 0) {
if (strcmp(*argv, "csum-mode") == 0) {
invarg("\"csum-mode\" value is invalid\n",
*argv);
- rta_addattr8(rta, 1024, ILA_ATTR_CSUM_MODE,
- (__u8)csum_mode);
+ ret = rta_addattr8(rta, len, ILA_ATTR_CSUM_MODE,
+ (__u8)csum_mode);
argc--; argv++;
} else if (strcmp(*argv, "ident-type") == 0) {
invarg("\"ident-type\" value is invalid\n",
*argv);
- rta_addattr8(rta, 1024, ILA_ATTR_IDENT_TYPE,
- (__u8)ident_type);
+ ret = rta_addattr8(rta, len, ILA_ATTR_IDENT_TYPE,
+ (__u8)ident_type);
argc--; argv++;
} else if (strcmp(*argv, "hook-type") == 0) {
invarg("\"hook-type\" value is invalid\n",
*argv);
- rta_addattr8(rta, 1024, ILA_ATTR_HOOK_TYPE,
- (__u8)hook_type);
+ ret = rta_addattr8(rta, len, ILA_ATTR_HOOK_TYPE,
+ (__u8)hook_type);
argc--; argv++;
} else {
break;
}
+ if (ret)
+ break;
}
/* argv is currently the first unparsed argument,
*argcp = argc + 1;
*argvp = argv - 1;
- return 0;
+ return ret;
}
static int parse_encap_ip6(struct rtattr *rta, size_t len,
int *argcp, char ***argvp)
{
- int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0;
+ int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
+ int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0;
char **argv = *argvp;
int argc = *argcp;
+ int ret = 0;
+ __u16 flags = 0;
while (argc > 0) {
if (strcmp(*argv, "id") == 0) {
duparg2("id", *argv);
if (get_be64(&id, *argv, 0))
invarg("\"id\" value is invalid\n", *argv);
- rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id);
+ ret = rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id);
} else if (strcmp(*argv, "dst") == 0) {
inet_prefix addr;
if (dst_ok++)
duparg2("dst", *argv);
get_addr(&addr, *argv, AF_INET6);
- rta_addattr_l(rta, len, LWTUNNEL_IP6_DST,
- &addr.data, addr.bytelen);
+ ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_DST,
+ &addr.data, addr.bytelen);
+ } else if (strcmp(*argv, "src") == 0) {
+ inet_prefix addr;
+
+ NEXT_ARG();
+ if (src_ok++)
+ duparg2("src", *argv);
+ get_addr(&addr, *argv, AF_INET6);
+ ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_SRC,
+ &addr.data, addr.bytelen);
} else if (strcmp(*argv, "tc") == 0) {
__u32 tc;
duparg2("tc", *argv);
if (rtnl_dsfield_a2n(&tc, *argv))
invarg("\"tc\" value is invalid\n", *argv);
- rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc);
+ ret = rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc);
} else if (strcmp(*argv, "hoplimit") == 0) {
__u8 hoplimit;
if (get_u8(&hoplimit, *argv, 0))
invarg("\"hoplimit\" value is invalid\n",
*argv);
- rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, hoplimit);
+ ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT,
+ hoplimit);
+ } else if (strcmp(*argv, "geneve_opts") == 0) {
+ struct rtattr *nest;
+
+ if (opts_ok++)
+ duparg2("opts", *argv);
+
+ NEXT_ARG();
+
+ nest = rta_nest(rta, len,
+ LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+ ret = lwtunnel_parse_geneve_opts(*argv, len, rta);
+ if (ret)
+ invarg("\"geneve_opts\" value is invalid\n",
+ *argv);
+ rta_nest_end(rta, nest);
+ } else if (strcmp(*argv, "vxlan_opts") == 0) {
+ struct rtattr *nest;
+
+ if (opts_ok++)
+ duparg2("opts", *argv);
+
+ NEXT_ARG();
+
+ nest = rta_nest(rta, len,
+ LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+ ret = lwtunnel_parse_vxlan_opts(*argv, len, rta);
+ if (ret)
+ invarg("\"vxlan_opts\" value is invalid\n",
+ *argv);
+ rta_nest_end(rta, nest);
+ } else if (strcmp(*argv, "key") == 0) {
+ if (key_ok++)
+ duparg2("key", *argv);
+ flags |= TUNNEL_KEY;
+ } else if (strcmp(*argv, "csum") == 0) {
+ if (csum_ok++)
+ duparg2("csum", *argv);
+ flags |= TUNNEL_CSUM;
+ } else if (strcmp(*argv, "seq") == 0) {
+ if (seq_ok++)
+ duparg2("seq", *argv);
+ flags |= TUNNEL_SEQ;
} else {
break;
}
+ if (ret)
+ break;
argc--; argv++;
}
+ if (flags)
+ ret = rta_addattr16(rta, len, LWTUNNEL_IP6_FLAGS, flags);
+
/* 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;
-}
-
-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 = {
- .type = bpf_type,
- .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_and_load_common(&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;
+ return ret;
}
static void lwt_bpf_usage(void)
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,
+ rta_addattr32(rta, len, LWT_BPF_XMIT_HEADROOM,
headroom);
headroom_set = 1;
} else if (strcmp(*argv, "help") == 0) {
return 0;
}
-int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
+int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
+ int encap_attr, int encap_type_attr)
{
struct rtattr *nest;
int argc = *argcp;
char **argv = *argvp;
__u16 type;
+ int ret = 0;
NEXT_ARG();
type = read_encap_type(*argv);
exit(-1);
}
- nest = rta_nest(rta, 1024, RTA_ENCAP);
+ nest = rta_nest(rta, len, encap_attr);
switch (type) {
case LWTUNNEL_ENCAP_MPLS:
- parse_encap_mpls(rta, len, &argc, &argv);
+ ret = parse_encap_mpls(rta, len, &argc, &argv);
break;
case LWTUNNEL_ENCAP_IP:
- parse_encap_ip(rta, len, &argc, &argv);
+ ret = parse_encap_ip(rta, len, &argc, &argv);
break;
case LWTUNNEL_ENCAP_ILA:
- parse_encap_ila(rta, len, &argc, &argv);
+ ret = parse_encap_ila(rta, len, &argc, &argv);
break;
case LWTUNNEL_ENCAP_IP6:
- parse_encap_ip6(rta, len, &argc, &argv);
+ ret = parse_encap_ip6(rta, len, &argc, &argv);
break;
case LWTUNNEL_ENCAP_BPF:
if (parse_encap_bpf(rta, len, &argc, &argv) < 0)
exit(-1);
break;
case LWTUNNEL_ENCAP_SEG6:
- parse_encap_seg6(rta, len, &argc, &argv);
+ ret = parse_encap_seg6(rta, len, &argc, &argv);
break;
case LWTUNNEL_ENCAP_SEG6_LOCAL:
- parse_encap_seg6local(rta, len, &argc, &argv);
+ ret = parse_encap_seg6local(rta, len, &argc, &argv);
break;
default:
fprintf(stderr, "Error: unsupported encap type\n");
break;
}
+ if (ret)
+ return ret;
+
rta_nest_end(rta, nest);
- rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type);
+ ret = rta_addattr16(rta, len, encap_type_attr, type);
*argcp = argc;
*argvp = argv;
- return 0;
+ return ret;
}