diff --git a/include/libnetlink.h b/include/libnetlink.h index 6bff6bae..9e4cc101 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -109,6 +109,27 @@ struct rtnl_ctrl_data { typedef int (*rtnl_filter_t)(struct nlmsghdr *n, void *); +/** + * rtnl error handler called from + * rtnl_dump_done() + * rtnl_dump_error() + * + * Return value is a bitmask of the following values: + * RTNL_LET_NLERR + * error handled as usual + * RTNL_SUPPRESS_NLMSG_DONE_NLERR + * error in nlmsg_type == NLMSG_DONE will be suppressed + * RTNL_SUPPRESS_NLMSG_ERROR_NLERR + * error in nlmsg_type == NLMSG_ERROR will be suppressed + * and nlmsg will be skipped + * RTNL_SUPPRESS_NLERR - suppress error in both previous cases + */ +#define RTNL_LET_NLERR 0x01 +#define RTNL_SUPPRESS_NLMSG_DONE_NLERR 0x02 +#define RTNL_SUPPRESS_NLMSG_ERROR_NLERR 0x04 +#define RTNL_SUPPRESS_NLERR 0x06 +typedef int (*rtnl_err_hndlr_t)(struct nlmsghdr *n, void *); + typedef int (*rtnl_listen_filter_t)(struct rtnl_ctrl_data *, struct nlmsghdr *n, void *); @@ -118,6 +139,8 @@ typedef int (*nl_ext_ack_fn_t)(const char *errmsg, uint32_t off, struct rtnl_dump_filter_arg { rtnl_filter_t filter; void *arg1; + rtnl_err_hndlr_t errhndlr; + void *arg2; __u16 nc_flags; }; @@ -126,6 +149,15 @@ int rtnl_dump_filter_nc(struct rtnl_handle *rth, void *arg, __u16 nc_flags); #define rtnl_dump_filter(rth, filter, arg) \ rtnl_dump_filter_nc(rth, filter, arg, 0) +int rtnl_dump_filter_errhndlr_nc(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, + rtnl_err_hndlr_t errhndlr, + void *arg2, + __u16 nc_flags); +#define rtnl_dump_filter_errhndlr(rth, filter, farg, errhndlr, earg) \ + rtnl_dump_filter_errhndlr_nc(rth, filter, farg, errhndlr, earg, 0) + int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, struct nlmsghdr **answer) __attribute__((warn_unused_result)); diff --git a/ip/iproute.c b/ip/iproute.c index 6b3ddc92..1e5e2002 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -1735,6 +1735,18 @@ static int iproute_flush(int family, rtnl_filter_t filter_fn) } } +static int save_route_errhndlr(struct nlmsghdr *n, void *arg) +{ + int err = -*(int *)NLMSG_DATA(n); + + if (n->nlmsg_type == NLMSG_DONE && + filter.tb == RT_TABLE_MAIN && + err == ENOENT) + return RTNL_SUPPRESS_NLMSG_DONE_NLERR; + + return RTNL_LET_NLERR; +} + static int iproute_list_flush_or_save(int argc, char **argv, int action) { int dump_family = preferred_family; @@ -1947,7 +1959,8 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) new_json_obj(json); - if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { + if (rtnl_dump_filter_errhndlr(&rth, filter_fn, stdout, + save_route_errhndlr, NULL) < 0) { fprintf(stderr, "Dump terminated\n"); return -2; } diff --git a/lib/bpf_libbpf.c b/lib/bpf_libbpf.c index 864f8c35..dbec2cb5 100644 --- a/lib/bpf_libbpf.c +++ b/lib/bpf_libbpf.c @@ -268,10 +268,12 @@ static int load_bpf_object(struct bpf_cfg_in *cfg) } bpf_object__for_each_program(p, obj) { + bool prog_to_attach = !prog && cfg->section && + !strcmp(get_bpf_program__section_name(p), cfg->section); + /* Only load the programs that will either be subsequently * attached or inserted into a tail call map */ - if (find_legacy_tail_calls(p, obj) < 0 && cfg->section && - strcmp(get_bpf_program__section_name(p), cfg->section)) { + if (find_legacy_tail_calls(p, obj) < 0 && !prog_to_attach) { ret = bpf_program__set_autoload(p, false); if (ret) return -EINVAL; @@ -280,7 +282,8 @@ static int load_bpf_object(struct bpf_cfg_in *cfg) bpf_program__set_type(p, cfg->type); bpf_program__set_ifindex(p, cfg->ifindex); - if (!prog) + + if (prog_to_attach) prog = p; } diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 2f2cc1fe..7e977a67 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -718,7 +718,8 @@ int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) return sendmsg(rth->fd, &msg, 0); } -static int rtnl_dump_done(struct nlmsghdr *h) +static int rtnl_dump_done(struct nlmsghdr *h, + const struct rtnl_dump_filter_arg *a) { int len = *(int *)NLMSG_DATA(h); @@ -728,11 +729,15 @@ static int rtnl_dump_done(struct nlmsghdr *h) } if (len < 0) { + errno = -len; + + if (a->errhndlr && (a->errhndlr(h, a->arg2) & RTNL_SUPPRESS_NLMSG_DONE_NLERR)) + return 0; + /* check for any messages returned from kernel */ if (nl_dump_ext_ack_done(h, len)) return len; - errno = -len; switch (errno) { case ENOENT: case EOPNOTSUPP: @@ -753,8 +758,9 @@ static int rtnl_dump_done(struct nlmsghdr *h) return 0; } -static void rtnl_dump_error(const struct rtnl_handle *rth, - struct nlmsghdr *h) +static int rtnl_dump_error(const struct rtnl_handle *rth, + struct nlmsghdr *h, + const struct rtnl_dump_filter_arg *a) { if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { @@ -766,11 +772,16 @@ static void rtnl_dump_error(const struct rtnl_handle *rth, if (rth->proto == NETLINK_SOCK_DIAG && (errno == ENOENT || errno == EOPNOTSUPP)) - return; + return -1; + + if (a->errhndlr && (a->errhndlr(h, a->arg2) & RTNL_SUPPRESS_NLMSG_ERROR_NLERR)) + return 0; if (!(rth->flags & RTNL_HANDLE_F_SUPPRESS_NLERR)) perror("RTNETLINK answers"); } + + return -1; } static int __rtnl_recvmsg(int fd, struct msghdr *msg, int flags) @@ -879,7 +890,7 @@ static int rtnl_dump_filter_l(struct rtnl_handle *rth, dump_intr = 1; if (h->nlmsg_type == NLMSG_DONE) { - err = rtnl_dump_done(h); + err = rtnl_dump_done(h, a); if (err < 0) { free(buf); return -1; @@ -890,9 +901,13 @@ static int rtnl_dump_filter_l(struct rtnl_handle *rth, } if (h->nlmsg_type == NLMSG_ERROR) { - rtnl_dump_error(rth, h); - free(buf); - return -1; + err = rtnl_dump_error(rth, h, a); + if (err < 0) { + free(buf); + return -1; + } + + goto skip_it; } if (!rth->dump_fp) { @@ -928,12 +943,34 @@ skip_it: } int rtnl_dump_filter_nc(struct rtnl_handle *rth, - rtnl_filter_t filter, - void *arg1, __u16 nc_flags) + rtnl_filter_t filter, + void *arg1, __u16 nc_flags) { - const struct rtnl_dump_filter_arg a[2] = { - { .filter = filter, .arg1 = arg1, .nc_flags = nc_flags, }, - { .filter = NULL, .arg1 = NULL, .nc_flags = 0, }, + const struct rtnl_dump_filter_arg a[] = { + { + .filter = filter, .arg1 = arg1, + .nc_flags = nc_flags, + }, + { }, + }; + + return rtnl_dump_filter_l(rth, a); +} + +int rtnl_dump_filter_errhndlr_nc(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, + rtnl_err_hndlr_t errhndlr, + void *arg2, + __u16 nc_flags) +{ + const struct rtnl_dump_filter_arg a[] = { + { + .filter = filter, .arg1 = arg1, + .errhndlr = errhndlr, .arg2 = arg2, + .nc_flags = nc_flags, + }, + { }, }; return rtnl_dump_filter_l(rth, a); @@ -1138,16 +1175,16 @@ int rtnl_listen(struct rtnl_handle *rtnl, char buf[16384]; char cmsgbuf[BUFSIZ]; - if (rtnl->flags & RTNL_HANDLE_F_LISTEN_ALL_NSID) { - msg.msg_control = &cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - } - iov.iov_base = buf; while (1) { struct rtnl_ctrl_data ctrl; struct cmsghdr *cmsg; + if (rtnl->flags & RTNL_HANDLE_F_LISTEN_ALL_NSID) { + msg.msg_control = &cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + } + iov.iov_len = sizeof(buf); status = recvmsg(rtnl->fd, &msg, 0); diff --git a/man/man8/ip-tunnel.8 b/man/man8/ip-tunnel.8 index 9a510af0..57e030dd 100644 --- a/man/man8/ip-tunnel.8 +++ b/man/man8/ip-tunnel.8 @@ -235,7 +235,7 @@ flag is equivalent to the combination .B It doesn't work. Don't use it. .TP -.BI encaplim " ELIM" +.BI encaplimit " ELIM" .RB ( " only IPv6 tunnels " ) set a fixed encapsulation limit. Default is 4. diff --git a/man/man8/tc-skbmod.8 b/man/man8/tc-skbmod.8 index eb3c38fa..76512311 100644 --- a/man/man8/tc-skbmod.8 +++ b/man/man8/tc-skbmod.8 @@ -5,12 +5,12 @@ skbmod - user-friendly packet editor action .SH SYNOPSIS .in +8 .ti -8 -.BR tc " ... " "action skbmod " "{ [ " "set " -.IR SETTABLE " ] [ " +.BR tc " ... " "action skbmod " "{ " "set " +.IR SETTABLE " | " .BI swap " SWAPPABLE" -.RI " ] [ " CONTROL " ] [ " +.RI " } [ " CONTROL " ] [ " .BI index " INDEX " -] } +] .ti -8 .IR SETTABLE " := " @@ -25,6 +25,7 @@ skbmod - user-friendly packet editor action .IR SWAPPABLE " := " .B mac .ti -8 + .IR CONTROL " := {" .BR reclassify " | " pipe " | " drop " | " shot " | " continue " | " pass " }" .SH DESCRIPTION @@ -48,10 +49,7 @@ Change the source mac to the specified address. Change the ethertype to the specified value. .TP .BI mac -Used to swap mac addresses. The -.B swap mac -directive is performed -after any outstanding D/SMAC changes. +Used to swap mac addresses. .TP .I CONTROL The following keywords allow to control how the tree of qdisc, classes, @@ -128,9 +126,13 @@ tc filter add dev eth3 parent 1: protocol ip prio 10 \\ .EE .RE -As mentioned above, the swap action will occur after any -.B " smac/dmac " -substitutions are executed, if they are present. +However, trying to +.B set +and +.B swap +in a single +.B skbmod +command will cause undefined behavior. .SH SEE ALSO .BR tc (8), diff --git a/tc/m_police.c b/tc/m_police.c index 2594c089..f38ab90a 100644 --- a/tc/m_police.c +++ b/tc/m_police.c @@ -278,7 +278,7 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg) __u64 rate64, prate64; __u64 pps64, ppsburst64; - print_string(PRINT_ANY, "kind", "%s", "police"); + print_string(PRINT_JSON, "kind", "%s", "police"); if (arg == NULL) return 0; @@ -301,7 +301,8 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg) RTA_PAYLOAD(tb[TCA_POLICE_RATE64]) >= sizeof(rate64)) rate64 = rta_getattr_u64(tb[TCA_POLICE_RATE64]); - print_uint(PRINT_ANY, "index", "\t index %u ", p->index); + print_hex(PRINT_FP, NULL, " police 0x%x ", p->index); + print_uint(PRINT_JSON, "index", NULL, p->index); tc_print_rate(PRINT_FP, NULL, "rate %s ", rate64); buffer = tc_calc_xmitsize(rate64, p->burst); print_size(PRINT_FP, NULL, "burst %s ", buffer); @@ -342,12 +343,13 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg) print_string(PRINT_FP, NULL, " ", NULL); } - print_uint(PRINT_ANY, "overhead", "overhead %u ", p->rate.overhead); + print_size(PRINT_ANY, "overhead", "overhead %s ", p->rate.overhead); linklayer = (p->rate.linklayer & TC_LINKLAYER_MASK); if (linklayer > TC_LINKLAYER_ETHERNET || show_details) print_string(PRINT_ANY, "linklayer", "linklayer %s ", sprint_linklayer(linklayer, b2)); - print_int(PRINT_ANY, "ref", "ref %d ", p->refcnt); + print_nl(); + print_int(PRINT_ANY, "ref", "\tref %d ", p->refcnt); print_int(PRINT_ANY, "bind", "bind %d ", p->bindcnt); if (show_stats) { if (tb[TCA_POLICE_TM]) { diff --git a/tc/m_skbmod.c b/tc/m_skbmod.c index e13d3f16..3fe30651 100644 --- a/tc/m_skbmod.c +++ b/tc/m_skbmod.c @@ -28,10 +28,9 @@ static void skbmod_explain(void) { fprintf(stderr, - "Usage:... skbmod {[set ] [swap ]} [CONTROL] [index INDEX]\n" + "Usage:... skbmod { set | swap } [CONTROL] [index INDEX]\n" "where SETTABLE is: [dmac DMAC] [smac SMAC] [etype ETYPE]\n" - "where SWAPABLE is: \"mac\" to swap mac addresses\n" - "note: \"swap mac\" is done after any outstanding D/SMAC change\n" + "where SWAPPABLE is: \"mac\" to swap mac addresses\n" "\tDMAC := 6 byte Destination MAC address\n" "\tSMAC := optional 6 byte Source MAC address\n" "\tETYPE := optional 16 bit ethertype\n"