diff --git a/bridge/fdb.c b/bridge/fdb.c index f75e953a..c4bf4039 100644 --- a/bridge/fdb.c +++ b/bridge/fdb.c @@ -346,8 +346,7 @@ static int fdb_show(int argc, char **argv) if (rth.flags & RTNL_HANDLE_F_STRICT_CHK) rc = rtnl_neighdump_req(&rth, PF_BRIDGE, fdb_dump_filter); else - rc = rtnl_linkdump_req_filter_fn(&rth, PF_BRIDGE, - fdb_linkdump_filter); + rc = rtnl_fdb_linkdump_req_filter_fn(&rth, fdb_linkdump_filter); if (rc < 0) { perror("Cannot send dump request"); exit(1); diff --git a/devlink/devlink.c b/devlink/devlink.c index d823512a..960cdda9 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -4525,7 +4525,8 @@ static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl) size = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE]); counters_enabled = !!mnl_attr_get_u8(nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]); - resource_valid = !!nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID]; + resource_valid = nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID] && + ctx->resources; if (resource_valid) { table->resource_id = mnl_attr_get_u64(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID]); table->resource_valid = true; @@ -4641,12 +4642,9 @@ static int cmd_dpipe_table_show(struct dl *dl) dl_opts_put(nlh, dl); err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, &resource_ctx); - if (err) { - pr_err("error get resources %s\n", strerror(resource_ctx.err)); - goto err_resource_dump; - } + if (!err) + dpipe_ctx.resources = resource_ctx.resources; - dpipe_ctx.resources = resource_ctx.resources; flags = NLM_F_REQUEST | NLM_F_ACK; nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, flags); dl_opts_put(nlh, dl); @@ -4659,8 +4657,6 @@ static int cmd_dpipe_table_show(struct dl *dl) dpipe_ctx_fini(&dpipe_ctx); return 0; -err_resource_dump: - resource_ctx_fini(&resource_ctx); err_resource_ctx_init: err_headers_get: dpipe_ctx_fini(&dpipe_ctx); diff --git a/include/libnetlink.h b/include/libnetlink.h index 0854d6ad..503b3ec1 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -81,6 +81,9 @@ int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask) int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int fam, req_filter_fn_t fn) __attribute__((warn_unused_result)); +int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth, + req_filter_fn_t filter_fn) + __attribute__((warn_unused_result)); int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask) __attribute__((warn_unused_result)); int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h index f98d82d4..f3bcd7ee 100644 --- a/include/uapi/linux/inet_diag.h +++ b/include/uapi/linux/inet_diag.h @@ -137,15 +137,21 @@ enum { INET_DIAG_TCLASS, INET_DIAG_SKMEMINFO, INET_DIAG_SHUTDOWN, - INET_DIAG_DCTCPINFO, - INET_DIAG_PROTOCOL, /* response attribute only */ + + /* + * Next extenstions cannot be requested in struct inet_diag_req_v2: + * its field idiag_ext has only 8 bits. + */ + + INET_DIAG_DCTCPINFO, /* request as INET_DIAG_VEGASINFO */ + INET_DIAG_PROTOCOL, /* response attribute only */ INET_DIAG_SKV6ONLY, INET_DIAG_LOCALS, INET_DIAG_PEERS, INET_DIAG_PAD, - INET_DIAG_MARK, - INET_DIAG_BBRINFO, - INET_DIAG_CLASS_ID, + INET_DIAG_MARK, /* only with CAP_NET_ADMIN */ + INET_DIAG_BBRINFO, /* request as INET_DIAG_VEGASINFO */ + INET_DIAG_CLASS_ID, /* request as INET_DIAG_TCLASS */ INET_DIAG_MD5SIG, __INET_DIAG_MAX, }; diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 2bc33f3a..76edf706 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -547,7 +547,7 @@ static void print_vf_stats64(FILE *fp, struct rtattr *vfstats) return; } - parse_rtattr_nested(vf, IFLA_VF_MAX, vfstats); + parse_rtattr_nested(vf, IFLA_VF_STATS_MAX, vfstats); if (is_json_context()) { open_json_object("stats"); diff --git a/ip/iplink.c b/ip/iplink.c index b5519201..3a0cf459 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -98,7 +98,7 @@ void iplink_usage(void) " [ trust { on | off} ] ]\n" " [ node_guid { eui64 } ]\n" " [ port_guid { eui64 } ]\n" - " [ xdp { off |\n" + " [ { xdp | xdpgeneric | xdpdrv | xdpoffload } { off |\n" " object FILE [ section NAME ] [ verbose ] |\n" " pinned FILE } ]\n" " [ master DEVICE ][ vrf NAME ]\n" diff --git a/ip/iproute.c b/ip/iproute.c index 5f58a3b3..cc02a3e1 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -1932,6 +1932,7 @@ static int iproute_get(int argc, char **argv) int fib_match = 0; int from_ok = 0; unsigned int mark = 0; + bool address_found = false; iproute_reset_filter(0); filter.cloned = 2; @@ -2037,11 +2038,12 @@ static int iproute_get(int argc, char **argv) addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); req.r.rtm_dst_len = addr.bitlen; + address_found = true; } argc--; argv++; } - if (req.r.rtm_dst_len == 0) { + if (!address_found) { fprintf(stderr, "need at least a destination address\n"); return -1; } diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c index aee18ac5..03217b8f 100644 --- a/ip/iproute_lwtunnel.c +++ b/ip/iproute_lwtunnel.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include static const char *format_encap_type(int type) { @@ -294,6 +294,7 @@ static void print_encap_mpls(FILE *fp, struct rtattr *encap) 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); @@ -318,6 +319,16 @@ static void print_encap_ip(FILE *fp, struct rtattr *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); + } } static void print_encap_ila(FILE *fp, struct rtattr *encap) @@ -354,6 +365,7 @@ 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); @@ -379,6 +391,16 @@ static void print_encap_ip6(FILE *fp, struct rtattr *encap) if (tb[LWTUNNEL_IP6_TC]) print_uint(PRINT_ANY, "tc", "tc %u ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC])); + + 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); + } } static void print_encap_bpf(FILE *fp, struct rtattr *encap) @@ -777,9 +799,11 @@ static int parse_encap_ip(struct rtattr *rta, size_t len, int *argcp, char ***argvp) { 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; char **argv = *argvp; int argc = *argcp; int ret = 0; + __u16 flags = 0; while (argc > 0) { if (strcmp(*argv, "id") == 0) { @@ -827,6 +851,18 @@ static int parse_encap_ip(struct rtattr *rta, size_t len, if (get_u8(&ttl, *argv, 0)) invarg("\"ttl\" value is invalid\n", *argv); ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl); + } 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; } @@ -835,6 +871,9 @@ static int parse_encap_ip(struct rtattr *rta, size_t len, 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 @@ -927,9 +966,11 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len, int *argcp, char ***argvp) { 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; char **argv = *argvp; int argc = *argcp; int ret = 0; + __u16 flags = 0; while (argc > 0) { if (strcmp(*argv, "id") == 0) { @@ -979,6 +1020,18 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len, *argv); ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, hoplimit); + } 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; } @@ -987,6 +1040,9 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len, 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 diff --git a/ip/iprule.c b/ip/iprule.c index 2f58d8c2..4e9437de 100644 --- a/ip/iprule.c +++ b/ip/iprule.c @@ -459,7 +459,7 @@ int print_rule(struct nlmsghdr *n, void *arg) } else if (frh->action == FR_ACT_NOP) { print_null(PRINT_ANY, "nop", "nop", NULL); } else if (frh->action != FR_ACT_TO_TBL) { - print_string(PRINT_ANY, "to_tbl", "%s", + print_string(PRINT_ANY, "action", "%s", rtnl_rtntype_n2a(frh->action, b1, sizeof(b1))); } diff --git a/lib/bpf.c b/lib/bpf.c index c7b3ee1e..7d2a322f 100644 --- a/lib/bpf.c +++ b/lib/bpf.c @@ -2203,12 +2203,16 @@ static int bpf_btf_prep_type_data(struct bpf_elf_ctx *ctx) case BTF_KIND_ENUM: type_cur += var_len * sizeof(struct btf_enum); break; + case BTF_KIND_FUNC_PROTO: + type_cur += var_len * sizeof(struct btf_param); + break; case BTF_KIND_TYPEDEF: case BTF_KIND_PTR: case BTF_KIND_FWD: case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: + case BTF_KIND_FUNC: break; default: fprintf(stderr, "Object has unknown BTF type: %u!\n", kind); diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 110f47bc..0d48a3d4 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -476,7 +476,7 @@ int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int family, int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int family, req_filter_fn_t filter_fn) { - if (family == AF_UNSPEC) { + if (family == AF_UNSPEC || family == AF_PACKET) { struct { struct nlmsghdr nlh; struct ifinfomsg ifm; @@ -503,6 +503,29 @@ int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int family, return __rtnl_linkdump_req(rth, family); } +int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + char buf[128]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlh.nlmsg_type = RTM_GETNEIGH, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifm.ifi_family = PF_BRIDGE, + }; + int err; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + + return send(rth->fd, &req, sizeof(req), 0); +} + int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask) { struct { @@ -695,6 +718,8 @@ static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer) if (len < 0) return len; + if (len < 32768) + len = 32768; buf = malloc(len); if (!buf) { fprintf(stderr, "malloc error: not enough buffer\n"); diff --git a/man/man8/bridge.8 b/man/man8/bridge.8 index 72210f62..13c46386 100644 --- a/man/man8/bridge.8 +++ b/man/man8/bridge.8 @@ -344,7 +344,7 @@ Controls whether a given port will sync MAC addresses learned on device port to bridge FDB. .TP -.BR "flooding on " or " flooding off " +.BR "flood on " or " flood off " Controls whether a given port will flood unicast traffic for which there is no FDB entry. By default this flag is on. .TP @@ -361,7 +361,7 @@ switch. .TP .BR "mcast_flood on " or " mcast_flood off " -Controls whether a given port will be flooded with multicast traffic for which there is no MDB entry. By default this flag is on. +Controls whether a given port will flood multicast traffic for which there is no MDB entry. By default this flag is on. .TP .BR "neigh_suppress on " or " neigh_suppress off " diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 73d37c19..6f31453c 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -2183,7 +2183,7 @@ queries. option above. .BR mcast_flood " { " on " | " off " }" -- controls whether a given port will be flooded with multicast traffic for which there is no MDB entry. +- controls whether a given port will flood multicast traffic for which there is no MDB entry. .BI group_fwd_mask " MASK " - set the group forward mask. This is the bitmask that is applied to decide whether to forward incoming frames destined to link-local addresses, ie addresses of the form 01:80:C2:00:00:0X (defaults to 0, ie the bridge does not forward any link-local frames coming on this port). diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in index 9603ac6e..b9ae6e30 100644 --- a/man/man8/ip-route.8.in +++ b/man/man8/ip-route.8.in @@ -737,7 +737,8 @@ is a set of encapsulation attributes specific to the .B tos .IR TOS " ] [" .B ttl -.IR TTL " ]" +.IR TTL " ] [ " +.BR key " ] [" csum " ] [ " seq " ] " .in -2 .sp diff --git a/man/man8/ss.8 b/man/man8/ss.8 index bc120fd7..b5099c2f 100644 --- a/man/man8/ss.8 +++ b/man/man8/ss.8 @@ -244,6 +244,23 @@ the pacing rate and max pacing rate a helper variable for TCP internal auto tuning socket receive buffer .RE .TP +.B \-\-tos +Show ToS and priority information. Below fields may appear: +.RS +.P +.TP +.B tos +IPv4 Type-of-Service byte +.P +.TP +.B tclass +IPv6 Traffic Class byte +.P +.TP +.B class_id +Class id set by net_cls cgroup. If class is zero this shows priority set by SO_PRIORITY. +.RE +.TP .B \-K, \-\-kill Attempts to forcibly close sockets. This option displays sockets that are successfully closed and silently skips sockets that the kernel does not support diff --git a/misc/ss.c b/misc/ss.c index 8e5cc16b..9cb3ee19 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -52,8 +52,17 @@ #include #include +/* AF_VSOCK/PF_VSOCK is only provided since glibc 2.18 */ +#ifndef PF_VSOCK +#define PF_VSOCK 40 +#endif +#ifndef AF_VSOCK +#define AF_VSOCK PF_VSOCK +#endif + #define MAGIC_SEQ 123456 -#define BUF_CHUNK (1024 * 1024) +#define BUF_CHUNK (1024 * 1024) /* Buffer chunk allocation size */ +#define BUF_CHUNKS_MAX 5 /* Maximum number of allocated buffer chunks */ #define LEN_ALIGN(x) (((x) + 1) & ~1) #define DIAG_REQUEST(_req, _r) \ @@ -111,6 +120,7 @@ static int show_header = 1; static int follow_events; static int sctp_ino; static int show_tipcinfo; +static int show_tos; enum col_id { COL_NETID, @@ -176,6 +186,7 @@ static struct { struct buf_token *cur; /* Position of current token in chunk */ struct buf_chunk *head; /* First chunk */ struct buf_chunk *tail; /* Current chunk */ + int chunks; /* Number of allocated chunks */ } buffer; static const char *TCP_PROTO = "tcp"; @@ -946,6 +957,8 @@ static struct buf_chunk *buf_chunk_new(void) new->end = buffer.cur->data; + buffer.chunks++; + return new; } @@ -1090,33 +1103,6 @@ static int field_is_last(struct column *f) return f - columns == COL_MAX - 1; } -static void field_next(void) -{ - field_flush(current_field); - - if (field_is_last(current_field)) - current_field = columns; - else - current_field++; -} - -/* Walk through fields and flush them until we reach the desired one */ -static void field_set(enum col_id id) -{ - while (id != current_field - columns) - field_next(); -} - -/* Print header for all non-empty columns */ -static void print_header(void) -{ - while (!field_is_last(current_field)) { - if (!current_field->disabled) - out("%s", current_field->header); - field_next(); - } -} - /* Get the next available token in the buffer starting from the current token */ static struct buf_token *buf_token_next(struct buf_token *cur) { @@ -1142,6 +1128,7 @@ static void buf_free_all(void) free(tmp); } buffer.head = NULL; + buffer.chunks = 0; } /* Get current screen width, default to 80 columns if TIOCGWINSZ fails */ @@ -1304,6 +1291,40 @@ static void render(void) current_field = columns; } +/* Move to next field, and render buffer if we reached the maximum number of + * chunks, at the last field in a line. + */ +static void field_next(void) +{ + if (field_is_last(current_field) && buffer.chunks >= BUF_CHUNKS_MAX) { + render(); + return; + } + + field_flush(current_field); + if (field_is_last(current_field)) + current_field = columns; + else + current_field++; +} + +/* Walk through fields and flush them until we reach the desired one */ +static void field_set(enum col_id id) +{ + while (id != current_field - columns) + field_next(); +} + +/* Print header for all non-empty columns */ +static void print_header(void) +{ + while (!field_is_last(current_field)) { + if (!current_field->disabled) + out("%s", current_field->header); + field_next(); + } +} + static void sock_state_print(struct sockstat *s) { const char *sock_name; @@ -3022,6 +3043,15 @@ static int inet_show_sock(struct nlmsghdr *nlh, } } + if (show_tos) { + if (tb[INET_DIAG_TOS]) + out(" tos:%#x", rta_getattr_u8(tb[INET_DIAG_TOS])); + if (tb[INET_DIAG_TCLASS]) + out(" tclass:%#x", rta_getattr_u8(tb[INET_DIAG_TCLASS])); + if (tb[INET_DIAG_CLASS_ID]) + out(" class_id:%#x", rta_getattr_u32(tb[INET_DIAG_CLASS_ID])); + } + if (show_mem || (show_tcpinfo && s->type != IPPROTO_UDP)) { out("\n\t"); if (s->type == IPPROTO_SCTP) @@ -3072,6 +3102,11 @@ static int tcpdiag_send(int fd, int protocol, struct filter *f) req.r.idiag_ext |= (1<<(INET_DIAG_CONG-1)); } + if (show_tos) { + req.r.idiag_ext |= (1<<(INET_DIAG_TOS-1)); + req.r.idiag_ext |= (1<<(INET_DIAG_TCLASS-1)); + } + iov[0] = (struct iovec){ .iov_base = &req, .iov_len = sizeof(req) @@ -3132,6 +3167,11 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) req.r.idiag_ext |= (1<<(INET_DIAG_CONG-1)); } + if (show_tos) { + req.r.idiag_ext |= (1<<(INET_DIAG_TOS-1)); + req.r.idiag_ext |= (1<<(INET_DIAG_TCLASS-1)); + } + iov[0] = (struct iovec){ .iov_base = &req, .iov_len = sizeof(req) @@ -4814,6 +4854,7 @@ static void _usage(FILE *dest) " -i, --info show internal TCP information\n" " --tipcinfo show internal tipc socket information\n" " -s, --summary show socket usage summary\n" +" --tos show tos and priority information\n" " -b, --bpf show bpf filter socket information\n" " -E, --events continually display sockets as they are destroyed\n" " -Z, --context display process SELinux security contexts\n" @@ -4918,8 +4959,10 @@ static int scan_state(const char *state) #define OPT_TIPCSOCK 257 #define OPT_TIPCINFO 258 +#define OPT_TOS 259 + /* Values of 'x' are already used so a non-character is used */ -#define OPT_XDPSOCK 259 +#define OPT_XDPSOCK 260 static const struct option long_opts[] = { { "numeric", 0, 0, 'n' }, @@ -4956,6 +4999,7 @@ static const struct option long_opts[] = { { "contexts", 0, 0, 'z' }, { "net", 1, 0, 'N' }, { "tipcinfo", 0, 0, OPT_TIPCINFO}, + { "tos", 0, 0, OPT_TOS }, { "kill", 0, 0, 'K' }, { "no-header", 0, 0, 'H' }, { "xdp", 0, 0, OPT_XDPSOCK}, @@ -5139,6 +5183,9 @@ int main(int argc, char *argv[]) case OPT_TIPCINFO: show_tipcinfo = 1; break; + case OPT_TOS: + show_tos = 1; + break; case 'K': current_filter.kill = 1; break; diff --git a/tc/f_flower.c b/tc/f_flower.c index c5636667..9659e894 100644 --- a/tc/f_flower.c +++ b/tc/f_flower.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/tc/tc_util.c b/tc/tc_util.c index 1dfdae14..e5d15281 100644 --- a/tc/tc_util.c +++ b/tc/tc_util.c @@ -190,12 +190,13 @@ static const struct rate_suffix { { NULL } }; -static int parse_percent_rate(char *rate, const char *str, const char *dev) +static int parse_percent_rate(char *rate, size_t len, + const char *str, const char *dev) { long dev_mbit; int ret; - double perc, rate_mbit; - char *str_perc; + double perc, rate_bit; + char *str_perc = NULL; if (!dev[0]) { fprintf(stderr, "No device specified; specify device to rate limit by percentage\n"); @@ -219,10 +220,10 @@ static int parse_percent_rate(char *rate, const char *str, const char *dev) return -1; } - rate_mbit = perc * dev_mbit; + rate_bit = perc * dev_mbit * 1000 * 1000; - ret = snprintf(rate, 20, "%lf", rate_mbit); - if (ret <= 0 || ret >= 20) { + ret = snprintf(rate, len, "%lf", rate_bit); + if (ret <= 0 || ret >= len) { fprintf(stderr, "Unable to parse calculated rate\n"); return -1; } @@ -230,6 +231,7 @@ static int parse_percent_rate(char *rate, const char *str, const char *dev) return 0; malf: + free(str_perc); fprintf(stderr, "Specified rate value could not be read or is malformed\n"); return -1; } @@ -238,7 +240,7 @@ int get_percent_rate(unsigned int *rate, const char *str, const char *dev) { char r_str[20]; - if (parse_percent_rate(r_str, str, dev)) + if (parse_percent_rate(r_str, sizeof(r_str), str, dev)) return -1; return get_rate(rate, r_str); @@ -248,7 +250,7 @@ int get_percent_rate64(__u64 *rate, const char *str, const char *dev) { char r_str[20]; - if (parse_percent_rate(r_str, str, dev)) + if (parse_percent_rate(r_str, sizeof(r_str), str, dev)) return -1; return get_rate64(rate, r_str);