diff --git a/configure b/configure index f7c2d7a7..5ef5cd4c 100755 --- a/configure +++ b/configure @@ -336,6 +336,20 @@ EOF rm -f $TMPDIR/strtest.c $TMPDIR/strtest } +check_cap() +{ + if ${PKG_CONFIG} libcap --exists + then + echo "HAVE_CAP:=y" >>$CONFIG + echo "yes" + + echo 'CFLAGS += -DHAVE_LIBCAP' `${PKG_CONFIG} libcap --cflags` >>$CONFIG + echo 'LDLIBS +=' `${PKG_CONFIG} libcap --libs` >> $CONFIG + else + echo "no" + fi +} + quiet_config() { cat <> $CONFIG echo "%.o: %.c" >> $CONFIG echo ' $(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<' >> $CONFIG diff --git a/devlink/devlink.c b/devlink/devlink.c index 69c3c5d9..ba02064b 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -2602,7 +2602,7 @@ static const char *cmd_name(uint8_t cmd) case DEVLINK_CMD_DEL: return "del"; case DEVLINK_CMD_PORT_GET: return "get"; case DEVLINK_CMD_PORT_SET: return "set"; - case DEVLINK_CMD_PORT_NEW: return "net"; + case DEVLINK_CMD_PORT_NEW: return "new"; case DEVLINK_CMD_PORT_DEL: return "del"; default: return ""; } diff --git a/include/utils.h b/include/utils.h index e4389ee4..8cb4349e 100644 --- a/include/utils.h +++ b/include/utils.h @@ -308,4 +308,6 @@ size_t strlcpy(char *dst, const char *src, size_t size); size_t strlcat(char *dst, const char *src, size_t size); #endif +void drop_cap(void); + #endif /* __UTILS_H__ */ diff --git a/ip/ip.c b/ip/ip.c index 164d7260..71d5170c 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -174,6 +174,18 @@ int main(int argc, char **argv) char *batch_file = NULL; int color = 0; + /* to run vrf exec without root, capabilities might be set, drop them + * if not needed as the first thing. + * execv will drop them for the child command. + * vrf exec requires: + * - cap_dac_override to create the cgroup subdir in /sys + * - cap_sys_admin to load the BPF program + * - cap_net_admin to set the socket into the cgroup + */ + if (argc < 3 || strcmp(argv[1], "vrf") != 0 || + strcmp(argv[2], "exec") != 0) + drop_cap(); + basename = strrchr(argv[0], '/'); if (basename == NULL) basename = argv[0]; diff --git a/ip/iproute.c b/ip/iproute.c index 1d8fd815..e8d1f898 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -190,42 +190,20 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) return 0; if ((filter.tos^r->rtm_tos)&filter.tosmask) return 0; - if (filter.rdst.family) { - if (r->rtm_family != filter.rdst.family || - filter.rdst.bitlen > r->rtm_dst_len) - return 0; - } else if (filter.rdst.flags & PREFIXLEN_SPECIFIED) { - if (filter.rdst.bitlen > r->rtm_dst_len) - return 0; - } - if (filter.mdst.family) { - if (r->rtm_family != filter.mdst.family || - (filter.mdst.bitlen >= 0 && - filter.mdst.bitlen < r->rtm_dst_len)) - return 0; - } else if (filter.mdst.flags & PREFIXLEN_SPECIFIED) { - if (filter.mdst.bitlen >= 0 && - filter.mdst.bitlen < r->rtm_dst_len) - return 0; - } - if (filter.rsrc.family) { - if (r->rtm_family != filter.rsrc.family || - filter.rsrc.bitlen > r->rtm_src_len) - return 0; - } else if (filter.rsrc.flags & PREFIXLEN_SPECIFIED) { - if (filter.rsrc.bitlen > r->rtm_src_len) - return 0; - } - if (filter.msrc.family) { - if (r->rtm_family != filter.msrc.family || - (filter.msrc.bitlen >= 0 && - filter.msrc.bitlen < r->rtm_src_len)) - return 0; - } else if (filter.msrc.flags & PREFIXLEN_SPECIFIED) { - if (filter.msrc.bitlen >= 0 && - filter.msrc.bitlen < r->rtm_src_len) - return 0; - } + if (filter.rdst.family && + (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) + return 0; + if (filter.mdst.family && + (r->rtm_family != filter.mdst.family || + (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) + return 0; + if (filter.rsrc.family && + (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) + return 0; + if (filter.msrc.family && + (r->rtm_family != filter.msrc.family || + (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) + return 0; if (filter.rvia.family) { int family = r->rtm_family; @@ -242,9 +220,7 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) if (tb[RTA_DST]) memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8); - if (filter.rsrc.family || filter.msrc.family || - filter.rsrc.flags & PREFIXLEN_SPECIFIED || - filter.msrc.flags & PREFIXLEN_SPECIFIED) { + if (filter.rsrc.family || filter.msrc.family) { if (tb[RTA_SRC]) memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8); } @@ -264,18 +240,15 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len/8); } - if ((filter.rdst.family || filter.rdst.flags & PREFIXLEN_SPECIFIED) && - inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) + if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) return 0; - if ((filter.mdst.family || filter.mdst.flags & PREFIXLEN_SPECIFIED) && + if (filter.mdst.family && filter.mdst.bitlen >= 0 && inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) return 0; - if ((filter.rsrc.family || filter.rsrc.flags & PREFIXLEN_SPECIFIED) && - inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) + if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) return 0; - if ((filter.msrc.family || filter.msrc.flags & PREFIXLEN_SPECIFIED) && - filter.msrc.bitlen >= 0 && + if (filter.msrc.family && filter.msrc.bitlen >= 0 && inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) return 0; diff --git a/ip/ipvrf.c b/ip/ipvrf.c index f9277e1e..8a6b7f97 100644 --- a/ip/ipvrf.c +++ b/ip/ipvrf.c @@ -436,6 +436,8 @@ out2: out: free(mnt); + drop_cap(); + return rc; } diff --git a/lib/utils.c b/lib/utils.c index d6c2395d..63eadc4c 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -30,6 +30,9 @@ #include #include #include +#ifdef HAVE_LIBCAP +#include +#endif #include "rt_names.h" #include "utils.h" @@ -562,14 +565,23 @@ static int __get_addr_1(inet_prefix *addr, const char *name, int family) { memset(addr, 0, sizeof(*addr)); - if (strcmp(name, "default") == 0 || - strcmp(name, "all") == 0 || - strcmp(name, "any") == 0) { + if (strcmp(name, "default") == 0) { if ((family == AF_DECnet) || (family == AF_MPLS)) return -1; addr->family = (family != AF_UNSPEC) ? family : AF_INET; addr->bytelen = af_byte_len(addr->family); addr->bitlen = -2; + addr->flags |= PREFIXLEN_SPECIFIED; + return 0; + } + + if (strcmp(name, "all") == 0 || + strcmp(name, "any") == 0) { + if ((family == AF_DECnet) || (family == AF_MPLS)) + return -1; + addr->family = AF_UNSPEC; + addr->bytelen = 0; + addr->bitlen = -2; return 0; } @@ -683,6 +695,19 @@ int get_prefix_1(inet_prefix *dst, char *arg, int family) char *slash; int err, bitlen, flags; + memset(dst, 0, sizeof(*dst)); + + if (strcmp(arg, "default") == 0 || + strcmp(arg, "any") == 0 || + strcmp(arg, "all") == 0) { + if ((family == AF_DECnet) || (family == AF_MPLS)) + return -1; + dst->family = family; + dst->bytelen = 0; + dst->bitlen = 0; + return 0; + } + slash = strchr(arg, '/'); if (slash) *slash = 0; @@ -697,7 +722,7 @@ int get_prefix_1(inet_prefix *dst, char *arg, int family) bitlen = af_bit_len(dst->family); - flags = PREFIXLEN_SPECIFIED; + flags = 0; if (slash) { unsigned int plen; @@ -708,12 +733,11 @@ int get_prefix_1(inet_prefix *dst, char *arg, int family) if (plen > bitlen) return -1; + flags |= PREFIXLEN_SPECIFIED; bitlen = plen; } else { if (dst->bitlen == -2) bitlen = 0; - else - flags = 0; } dst->flags |= flags; @@ -1594,3 +1618,22 @@ size_t strlcat(char *dst, const char *src, size_t size) return dlen + strlcpy(dst + dlen, src, size - dlen); } #endif + +void drop_cap(void) +{ +#ifdef HAVE_LIBCAP + /* don't harmstring root/sudo */ + if (getuid() != 0 && geteuid() != 0) { + cap_t capabilities; + + capabilities = cap_get_proc(); + if (!capabilities) + exit(EXIT_FAILURE); + if (cap_clear(capabilities) != 0) + exit(EXIT_FAILURE); + if (cap_set_proc(capabilities) != 0) + exit(EXIT_FAILURE); + cap_free(capabilities); + } +#endif +} diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in index 487a8748..b28f3d2c 100644 --- a/man/man8/ip-route.8.in +++ b/man/man8/ip-route.8.in @@ -125,7 +125,7 @@ replace " } " .B cwnd .IR NUMBER " ] [ " .B ssthresh -.IR REALM " ] [ " +.IR NUMBER " ] [ " .B realms .IR REALM " ] [ " .B rto_min diff --git a/man/man8/ip-vrf.8 b/man/man8/ip-vrf.8 index 18789339..1a42cebe 100644 --- a/man/man8/ip-vrf.8 +++ b/man/man8/ip-vrf.8 @@ -63,6 +63,14 @@ a VRF other than the default VRF (main table). A command can be run against the default VRF by passing the "default" as the VRF name. This is useful if the current shell is associated with another VRF (e.g, Management VRF). +This command requires the system to be booted with cgroup v2 (e.g. with systemd, +add systemd.unified_cgroup_hierarchy=1 to the kernel command line). + +This command also requires to be ran as root or with the CAP_SYS_ADMIN, +CAP_NET_ADMIN and CAP_DAC_OVERRIDE capabilities. If built with libcap and if +capabilities are added to the ip binary program via setcap, the program will +drop them as the first thing when invoked, unless the command is vrf exec. + .TP .B ip vrf identify [PID] - Report VRF association for process .sp diff --git a/man/man8/tc-vlan.8 b/man/man8/tc-vlan.8 index 59c81e86..f5ffc25f 100644 --- a/man/man8/tc-vlan.8 +++ b/man/man8/tc-vlan.8 @@ -103,7 +103,7 @@ into VLAN ID 123: #tc qdisc add dev eth0 handle ffff: ingress #tc filter add dev eth0 parent ffff: pref 11 protocol ip \\ u32 match ip protocol 1 0xff flowid 1:1 \\ - u32 match ip src 10.0.0.2 flowid 1:1 \\ + match ip src 10.0.0.2 flowid 1:1 \\ action vlan push id 123 .EE .RE diff --git a/misc/ss.c b/misc/ss.c index 1484bcab..c76ad66e 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -1106,15 +1106,33 @@ static void buf_free_all(void) buffer.head = NULL; } +/* Get current screen width, default to 80 columns if TIOCGWINSZ fails */ +static int render_screen_width(void) +{ + int width = 80; + + if (isatty(STDOUT_FILENO)) { + struct winsize w; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { + if (w.ws_col > 0) + width = w.ws_col; + } + } + + return width; +} + /* Calculate column width from contents length. If columns don't fit on one * line, break them into the least possible amount of lines and keep them * aligned across lines. Available screen space is equally spread between fields * as additional spacing. */ -static void render_calc_width(int screen_width) +static void render_calc_width(void) { - int first, len = 0, linecols = 0; + int screen_width = render_screen_width(); struct column *c, *eol = columns - 1; + int first, len = 0, linecols = 0; /* First pass: set width for each column to measured content length */ for (first = 1, c = columns; c - columns < COL_MAX; c++) { @@ -1195,7 +1213,7 @@ newline: } /* Render buffered output with spacing and delimiters, then free up buffers */ -static void render(int screen_width) +static void render(void) { struct buf_token *token; int printed, line_started = 0; @@ -1209,7 +1227,7 @@ static void render(int screen_width) /* Ensure end alignment of last token, it wasn't necessarily flushed */ buffer.tail->end += buffer.cur->len % 2; - render_calc_width(screen_width); + render_calc_width(); /* Rewind and replay */ buffer.tail = buffer.head; @@ -1245,6 +1263,7 @@ static void render(int screen_width) } buf_free_all(); + current_field = columns; } static void sock_state_print(struct sockstat *s) @@ -4267,23 +4286,33 @@ static int generic_show_sock(const struct sockaddr_nl *addr, { struct sock_diag_msg *r = NLMSG_DATA(nlh); struct inet_diag_arg inet_arg = { .f = arg, .protocol = IPPROTO_MAX }; + int ret; switch (r->sdiag_family) { case AF_INET: case AF_INET6: inet_arg.rth = inet_arg.f->rth_for_killing; - return show_one_inet_sock(addr, nlh, &inet_arg); + ret = show_one_inet_sock(addr, nlh, &inet_arg); + break; case AF_UNIX: - return unix_show_sock(addr, nlh, arg); + ret = unix_show_sock(addr, nlh, arg); + break; case AF_PACKET: - return packet_show_sock(addr, nlh, arg); + ret = packet_show_sock(addr, nlh, arg); + break; case AF_NETLINK: - return netlink_show_sock(addr, nlh, arg); + ret = netlink_show_sock(addr, nlh, arg); + break; case AF_VSOCK: - return vsock_show_sock(addr, nlh, arg); + ret = vsock_show_sock(addr, nlh, arg); + break; default: - return -1; + ret = -1; } + + render(); + + return ret; } static int handle_follow_request(struct filter *f) @@ -4650,7 +4679,6 @@ int main(int argc, char *argv[]) FILE *filter_fp = NULL; int ch; int state_filter = 0; - int screen_width = 80; while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KHS", @@ -4952,15 +4980,6 @@ int main(int argc, char *argv[]) if (!(current_filter.states & (current_filter.states - 1))) columns[COL_STATE].disabled = 1; - if (isatty(STDOUT_FILENO)) { - struct winsize w; - - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { - if (w.ws_col > 0) - screen_width = w.ws_col; - } - } - if (show_header) print_header(); @@ -4991,7 +5010,7 @@ int main(int argc, char *argv[]) if (show_users || show_proc_ctx || show_sock_ctx) user_ent_destroy(); - render(screen_width); + render(); return 0; } diff --git a/misc/ssfilter.y b/misc/ssfilter.y index 4db3c95f..88d4229a 100644 --- a/misc/ssfilter.y +++ b/misc/ssfilter.y @@ -42,7 +42,7 @@ static void yyerror(char *s) %nonassoc '!' %% -applet: null expr +applet: null exprlist { *yy_ret = $2; $$ = $2; @@ -51,6 +51,32 @@ applet: null expr ; null: /* NOTHING */ { $$ = NULL; } ; +exprlist: expr + | '!' expr + { + $$ = alloc_node(SSF_NOT, $2); + } + | '(' exprlist ')' + { + $$ = $2; + } + | exprlist '|' expr + { + $$ = alloc_node(SSF_OR, $1); + $$->post = $3; + } + | exprlist '&' expr + { + $$ = alloc_node(SSF_AND, $1); + $$->post = $3; + } + | exprlist expr + { + $$ = alloc_node(SSF_AND, $1); + $$->post = $2; + } + ; + expr: DCOND HOSTCOND { $$ = alloc_node(SSF_DCOND, $2); @@ -128,30 +154,6 @@ expr: DCOND HOSTCOND { $$ = alloc_node(SSF_S_AUTO, NULL); } - | expr '|' expr - { - $$ = alloc_node(SSF_OR, $1); - $$->post = $3; - } - | expr expr - { - $$ = alloc_node(SSF_AND, $1); - $$->post = $2; - } - | expr '&' expr - - { - $$ = alloc_node(SSF_AND, $1); - $$->post = $3; - } - | '!' expr - { - $$ = alloc_node(SSF_NOT, $2); - } - | '(' expr ')' - { - $$ = $2; - } ; %% diff --git a/tc/m_connmark.c b/tc/m_connmark.c index a6ee532d..bcce4139 100644 --- a/tc/m_connmark.c +++ b/tc/m_connmark.c @@ -120,7 +120,8 @@ static int print_connmark(struct action_util *au, FILE *f, struct rtattr *arg) ci = RTA_DATA(tb[TCA_CONNMARK_PARMS]); - fprintf(f, " connmark zone %d\n", ci->zone); + fprintf(f, " connmark zone %d", ci->zone); + print_action_control(f, " ", ci->action, "\n"); fprintf(f, "\t index %u ref %d bind %d", ci->index, ci->refcnt, ci->bindcnt); diff --git a/tc/m_sample.c b/tc/m_sample.c index f6e579f8..1e18c515 100644 --- a/tc/m_sample.c +++ b/tc/m_sample.c @@ -65,7 +65,7 @@ static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p, while (argc > 0) { if (matches(*argv, "rate") == 0) { NEXT_ARG(); - if (get_unsigned(&rate, *argv, 10) != 0) { + if (get_u32(&rate, *argv, 10) != 0) { fprintf(stderr, "Illegal rate %s\n", *argv); usage(); return -1; @@ -73,7 +73,7 @@ static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p, rate_set = true; } else if (matches(*argv, "group") == 0) { NEXT_ARG(); - if (get_unsigned(&group, *argv, 10) != 0) { + if (get_u32(&group, *argv, 10) != 0) { fprintf(stderr, "Illegal group num %s\n", *argv); usage(); @@ -82,7 +82,7 @@ static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p, group_set = true; } else if (matches(*argv, "trunc") == 0) { NEXT_ARG(); - if (get_unsigned(&trunc, *argv, 10) != 0) { + if (get_u32(&trunc, *argv, 10) != 0) { fprintf(stderr, "Illegal truncation size %s\n", *argv); usage(); @@ -162,6 +162,8 @@ static int print_sample(struct action_util *au, FILE *f, struct rtattr *arg) fprintf(f, " trunc_size %d", rta_getattr_u32(tb[TCA_SAMPLE_TRUNC_SIZE])); + print_action_control(f, " ", p->action, ""); + fprintf(f, "\n\tindex %d ref %d bind %d", p->index, p->refcnt, p->bindcnt);