diff --git a/bash-completion/devlink b/bash-completion/devlink index 8518e7aa..f710c888 100644 --- a/bash-completion/devlink +++ b/bash-completion/devlink @@ -678,7 +678,7 @@ _devlink_trap_set_action() COMPREPLY=( $( compgen -W "action" -- "$cur" ) ) ;; $((7 + $i))) - COMPREPLY=( $( compgen -W "trap drop" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "trap drop mirror" -- "$cur" ) ) ;; esac } @@ -708,7 +708,7 @@ _devlink_trap_group_set() case $prev in action) - COMPREPLY=( $( compgen -W "trap drop" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "trap drop mirror" -- "$cur" ) ) return ;; policer) diff --git a/bridge/fdb.c b/bridge/fdb.c index d2247e80..d1f8afbe 100644 --- a/bridge/fdb.c +++ b/bridge/fdb.c @@ -635,10 +635,16 @@ static int fdb_get(int argc, char **argv) if (rtnl_talk(&rth, &req.n, &answer) < 0) return -2; + /* + * Initialize a json_writer and open an array object + * if -json was specified. + */ + new_json_obj(json); if (print_fdb(answer, stdout) < 0) { fprintf(stderr, "An error :-)\n"); return -1; } + delete_json_obj(); return 0; } diff --git a/configure b/configure index f415bf49..307912aa 100755 --- a/configure +++ b/configure @@ -208,7 +208,7 @@ typedef unsigned short ip_set_id_t; #include struct xt_set_info info; -#if IPSET_PROTOCOL == 6 +#if IPSET_PROTOCOL == 6 || IPSET_PROTOCOL == 7 int main(void) { return IPSET_MAXNAMELEN; diff --git a/devlink/devlink.c b/devlink/devlink.c index 75182cac..67681492 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -19,19 +19,26 @@ #include #include #include +#include +#include +#include +#include #include #define _LINUX_SYSINFO_H /* avoid collision with musl header */ #include #include +#include #include #include +#include +#include #include #include #include "SNAPSHOT.h" #include "list.h" #include "mnlg.h" -#include "json_writer.h" +#include "json_print.h" #include "utils.h" #include "namespace.h" @@ -1298,6 +1305,8 @@ static int trap_action_get(const char *actionstr, *p_action = DEVLINK_TRAP_ACTION_DROP; } else if (strcmp(actionstr, "trap") == 0) { *p_action = DEVLINK_TRAP_ACTION_TRAP; + } else if (strcmp(actionstr, "mirror") == 0) { + *p_action = DEVLINK_TRAP_ACTION_MIRROR; } else { pr_err("Unknown trap action \"%s\"\n", actionstr); return -EINVAL; @@ -2417,6 +2426,11 @@ static const struct param_val_conv param_val_conv[] = { .vstr = "flash", .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH, }, + { + .name = "fw_load_policy", + .vstr = "disk", + .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK, + }, { .name = "reset_dev_on_drv_probe", .vstr = "unknown", @@ -7209,6 +7223,8 @@ static const char *trap_type_name(uint8_t type) return "drop"; case DEVLINK_TRAP_TYPE_EXCEPTION: return "exception"; + case DEVLINK_TRAP_TYPE_CONTROL: + return "control"; default: return ""; } @@ -7221,6 +7237,8 @@ static const char *trap_action_name(uint8_t action) return "drop"; case DEVLINK_TRAP_ACTION_TRAP: return "trap"; + case DEVLINK_TRAP_ACTION_MIRROR: + return "mirror"; default: return ""; } @@ -7295,9 +7313,9 @@ static int cmd_trap_show_cb(const struct nlmsghdr *nlh, void *data) static void cmd_trap_help(void) { - pr_err("Usage: devlink trap set DEV trap TRAP [ action { trap | drop } ]\n"); + pr_err("Usage: devlink trap set DEV trap TRAP [ action { trap | drop | mirror } ]\n"); pr_err(" devlink trap show [ DEV trap TRAP ]\n"); - pr_err(" devlink trap group set DEV group GROUP [ action { trap | drop } ]\n"); + pr_err(" devlink trap group set DEV group GROUP [ action { trap | drop | mirror } ]\n"); pr_err(" [ policer POLICER ] [ nopolicer ]\n"); pr_err(" devlink trap group show [ DEV group GROUP ]\n"); pr_err(" devlink trap policer set DEV policer POLICER [ rate RATE ] [ burst BURST ]\n"); diff --git a/genl/genl_utils.h b/genl/genl_utils.h index a8d433a9..87b4f34c 100644 --- a/genl/genl_utils.h +++ b/genl/genl_utils.h @@ -2,11 +2,10 @@ #ifndef _TC_UTIL_H_ #define _TC_UTIL_H_ 1 +#include #include "utils.h" -#include "linux/genetlink.h" -struct genl_util -{ +struct genl_util { struct genl_util *next; char name[16]; int (*parse_genlopt)(struct genl_util *fu, int argc, char **argv); diff --git a/ip/Makefile b/ip/Makefile index 8735b8e4..4cad619c 100644 --- a/ip/Makefile +++ b/ip/Makefile @@ -11,7 +11,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \ iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \ iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \ ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \ - ipnexthop.o ipmptcp.o + ipnexthop.o ipmptcp.o iplink_bareudp.o RTMONOBJ=rtmon.o diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 3b53933f..ccf67d1d 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -30,7 +30,6 @@ #include #include -#include "utils.h" #include "rt_names.h" #include "utils.h" #include "ll_map.h" @@ -1257,7 +1256,9 @@ static const struct ifa_flag_data_t { /* Returns a pointer to the data structure for a particular interface flag, or null if no flag could be found */ static const struct ifa_flag_data_t* lookup_flag_data_by_name(const char* flag_name) { - for (int i = 0; i < ARRAY_SIZE(ifa_flag_data); ++i) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ifa_flag_data); ++i) { if (strcmp(flag_name, ifa_flag_data[i].name) == 0) return &ifa_flag_data[i]; } diff --git a/ip/ipfou.c b/ip/ipfou.c index ea126b08..9c697770 100644 --- a/ip/ipfou.c +++ b/ip/ipfou.c @@ -27,10 +27,10 @@ static void usage(void) { fprintf(stderr, - "Usage: ip fou add port PORT { ipproto PROTO | gue } [ -6 ]\n" + "Usage: ip fou add port PORT { ipproto PROTO | gue }\n" " [ local IFADDR ] [ peer IFADDR ]\n" " [ peer_port PORT ] [ dev IFNAME ]\n" - " ip fou del port PORT [ -6 ] [ local IFADDR ]\n" + " ip fou del port PORT [ local IFADDR ]\n" " [ peer IFADDR ] [ peer_port PORT ]\n" " [ dev IFNAME ]\n" " ip fou show\n" @@ -55,13 +55,17 @@ static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n, { const char *local = NULL, *peer = NULL; __u16 port, peer_port = 0; - __u8 family = AF_INET; + __u8 family = preferred_family; bool gue_set = false; int ipproto_set = 0; __u8 ipproto, type; int port_set = 0; int index = 0; + if (preferred_family == AF_UNSPEC) { + family = AF_INET; + } + while (argc > 0) { if (!matches(*argv, "port")) { NEXT_ARG(); diff --git a/ip/iplink.c b/ip/iplink.c index 47f73988..7d4b244d 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -124,7 +124,7 @@ void iplink_usage(void) " bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n" " gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan |\n" " vti | nlmon | team_slave | bond_slave | bridge_slave |\n" - " ipvlan | ipvtap | geneve | vrf | macsec | netdevsim | rmnet |\n" + " ipvlan | ipvtap | geneve | bareudp | vrf | macsec | netdevsim | rmnet |\n" " xfrm }\n"); } exit(-1); diff --git a/ip/iplink_bareudp.c b/ip/iplink_bareudp.c new file mode 100644 index 00000000..860ec699 --- /dev/null +++ b/ip/iplink_bareudp.c @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include +#include +#include + +#include "libnetlink.h" +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +#define BAREUDP_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0) + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... bareudp dstport PORT\n" + " ethertype PROTO\n" + " [ srcportmin PORT ]\n" + " [ [no]multiproto ]\n" + "\n" + "Where: PORT := 0-65535\n" + " PROTO := NUMBER | ip | mpls\n" + " SRCPORTMIN := 0-65535\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static void check_duparg(__u64 *attrs, int type, const char *key, + const char *argv) +{ + if (!BAREUDP_ATTRSET(*attrs, type)) { + *attrs |= (1L << type); + return; + } + duparg2(key, argv); +} + +static int bareudp_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + bool multiproto = false; + __u16 srcportmin = 0; + __be16 ethertype = 0; + __be16 dstport = 0; + __u64 attrs = 0; + + while (argc > 0) { + if (matches(*argv, "dstport") == 0) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_BAREUDP_PORT, "dstport", + *argv); + if (get_be16(&dstport, *argv, 0)) + invarg("dstport", *argv); + } else if (matches(*argv, "ethertype") == 0) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_BAREUDP_ETHERTYPE, + "ethertype", *argv); + if (ll_proto_a2n(ðertype, *argv)) + invarg("ethertype", *argv); + } else if (matches(*argv, "srcportmin") == 0) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_BAREUDP_SRCPORT_MIN, + "srcportmin", *argv); + if (get_u16(&srcportmin, *argv, 0)) + invarg("srcportmin", *argv); + } else if (matches(*argv, "multiproto") == 0) { + check_duparg(&attrs, IFLA_BAREUDP_MULTIPROTO_MODE, + *argv, *argv); + multiproto = true; + } else if (matches(*argv, "nomultiproto") == 0) { + check_duparg(&attrs, IFLA_BAREUDP_MULTIPROTO_MODE, + *argv, *argv); + multiproto = false; + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "bareudp: unknown command \"%s\"?\n", + *argv); + explain(); + return -1; + } + argc--, argv++; + } + + if (!BAREUDP_ATTRSET(attrs, IFLA_BAREUDP_PORT)) + missarg("dstport"); + if (!BAREUDP_ATTRSET(attrs, IFLA_BAREUDP_ETHERTYPE)) + missarg("ethertype"); + + addattr16(n, 1024, IFLA_BAREUDP_PORT, dstport); + addattr16(n, 1024, IFLA_BAREUDP_ETHERTYPE, ethertype); + if (BAREUDP_ATTRSET(attrs, IFLA_BAREUDP_SRCPORT_MIN)) + addattr16(n, 1024, IFLA_BAREUDP_SRCPORT_MIN, srcportmin); + if (multiproto) + addattr(n, 1024, IFLA_BAREUDP_MULTIPROTO_MODE); + + return 0; +} + +static void bareudp_print_opt(struct link_util *lu, FILE *f, + struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_BAREUDP_PORT]) + print_uint(PRINT_ANY, "dstport", "dstport %u ", + rta_getattr_be16(tb[IFLA_BAREUDP_PORT])); + + if (tb[IFLA_BAREUDP_ETHERTYPE]) { + struct rtattr *attr = tb[IFLA_BAREUDP_ETHERTYPE]; + SPRINT_BUF(ethertype); + + print_string(PRINT_ANY, "ethertype", "ethertype %s ", + ll_proto_n2a(rta_getattr_u16(attr), + ethertype, sizeof(ethertype))); + } + + if (tb[IFLA_BAREUDP_SRCPORT_MIN]) + print_uint(PRINT_ANY, "srcportmin", "srcportmin %u ", + rta_getattr_u16(tb[IFLA_BAREUDP_SRCPORT_MIN])); + + if (tb[IFLA_BAREUDP_MULTIPROTO_MODE]) + print_bool(PRINT_ANY, "multiproto", "multiproto ", true); + else + print_bool(PRINT_ANY, "multiproto", "nomultiproto ", false); +} + +static void bareudp_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util bareudp_link_util = { + .id = "bareudp", + .maxattr = IFLA_BAREUDP_MAX, + .parse_opt = bareudp_parse_opt, + .print_opt = bareudp_print_opt, + .print_help = bareudp_print_help, +}; diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c index d3c706d3..7cc00e7c 100644 --- a/ip/xfrm_policy.c +++ b/ip/xfrm_policy.c @@ -59,6 +59,7 @@ static void usage(void) " [ if_id IF_ID ] [ LIMIT-LIST ] [ TMPL-LIST ]\n" "Usage: ip xfrm policy { delete | get } { SELECTOR | index INDEX } dir DIR\n" " [ ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]\n" + " [ if_id IF_ID ]\n" "Usage: ip xfrm policy { deleteall | list } [ nosock ] [ SELECTOR ] [ dir DIR ]\n" " [ index INDEX ] [ ptype PTYPE ] [ action ACTION ] [ priority PRIORITY ]\n" " [ flag FLAG-LIST ]\n" @@ -582,6 +583,8 @@ static int xfrm_policy_get_or_delete(int argc, char **argv, int delete, struct xfrm_user_sec_ctx sctx; char str[CTX_BUF_SIZE]; } ctx = {}; + bool is_if_id_set = false; + __u32 if_id = 0; while (argc > 0) { if (strcmp(*argv, "dir") == 0) { @@ -619,7 +622,11 @@ static int xfrm_policy_get_or_delete(int argc, char **argv, int delete, NEXT_ARG(); xfrm_policy_ptype_parse(&upt.type, &argc, &argv); - + } else if (strcmp(*argv, "if_id") == 0) { + NEXT_ARG(); + if (get_u32(&if_id, *argv, 0)) + invarg("IF_ID value is invalid", *argv); + is_if_id_set = true; } else { if (selp) invarg("unknown", *argv); @@ -669,6 +676,9 @@ static int xfrm_policy_get_or_delete(int argc, char **argv, int delete, (void *)&ctx, ctx.sctx.len); } + if (is_if_id_set) + addattr32(&req.n, sizeof(req.buf), XFRMA_IF_ID, if_id); + if (rtnl_talk(&rth, &req.n, answer) < 0) exit(2); @@ -767,6 +777,11 @@ static int xfrm_policy_keep(struct nlmsghdr *n, void *arg) } } + if (tb[XFRMA_IF_ID]) { + addattr32(new_n, xb->size, XFRMA_IF_ID, + rta_getattr_u32(tb[XFRMA_IF_ID])); + } + xb->offset += new_n->nlmsg_len; xb->nlmsg_count++; diff --git a/lib/fs.c b/lib/fs.c index e265fc04..4b90a704 100644 --- a/lib/fs.c +++ b/lib/fs.c @@ -148,10 +148,10 @@ __u64 get_cgroup2_id(const char *path) strerror(errno)); goto out; } - if (fhp->handle_bytes != sizeof(__u64)) { - fprintf(stderr, "Invalid size of cgroup2 ID\n"); - goto out; - } + } + if (fhp->handle_bytes != sizeof(__u64)) { + fprintf(stderr, "Invalid size of cgroup2 ID\n"); + goto out; } memcpy(cg_id.bytes, fhp->f_handle, sizeof(__u64)); diff --git a/man/man3/libnetlink.3 b/man/man3/libnetlink.3 index 8e3dc620..9a2c801c 100644 --- a/man/man3/libnetlink.3 +++ b/man/man3/libnetlink.3 @@ -50,7 +50,7 @@ int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) .SH DESCRIPTION libnetlink provides a higher level interface to -.BR rtnetlink(7). +.BR rtnetlink (7). The read functions return 0 on success and a negative errno on failure. The send functions return the amount of data sent, or -1 on error. .TP @@ -129,9 +129,9 @@ for parsing. The file should contain raw data as received from a rtnetlink socke The following functions are useful to construct custom rtnetlink messages. For simple database dumping with filtering it is better to use the higher level functions above. See -.BR rtnetlink(3) +.BR rtnetlink (3) and -.BR netlink(3) +.BR netlink (3) on how to generate a rtnetlink message. The following utility functions require a continuous buffer that already contains a netlink message header and a rtnetlink request. @@ -194,7 +194,7 @@ netlink/rtnetlink was designed and written by Alexey Kuznetsov. Andi Kleen wrote the man page. .SH SEE ALSO -.BR netlink(7), -.BR rtnetlink(7) +.BR netlink (7), +.BR rtnetlink (7) .br /usr/include/linux/rtnetlink.h diff --git a/man/man8/bridge.8 b/man/man8/bridge.8 index fa8c0049..ad05cd7c 100644 --- a/man/man8/bridge.8 +++ b/man/man8/bridge.8 @@ -28,10 +28,9 @@ bridge \- show / manipulate bridge addresses and devices \fB\-o\fR[\fIneline\fr] } .ti -8 -.BR "bridge link set" +.B "bridge link set" .B dev -.IR DEV -.IR " [ " +.IR DEV " [ " .B cost .IR COST " ] [ " .B priority @@ -108,9 +107,9 @@ bridge \- show / manipulate bridge addresses and devices .ti -8 .BR "bridge mdb" " { " add " | " del " } " .B dev -.IR DEV +.I DEV .B port -.IR PORT +.I PORT .B grp .IR GROUP " [ " .BR permanent " | " temp " ] [ " @@ -125,10 +124,10 @@ bridge \- show / manipulate bridge addresses and devices .ti -8 .BR "bridge vlan" " { " add " | " del " } " .B dev -.IR DEV +.I DEV .B vid .IR VID " [ " -.BR tunnel_info +.B tunnel_info .IR TUNNEL_ID " ] [ " .BR pvid " ] [ " untagged " ] [ " .BR self " ] [ " master " ] " @@ -168,7 +167,7 @@ to the specified network namespace Actually it just simplifies executing of: .B ip netns exec -.IR NETNS +.I NETNS .B bridge .RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " .BR help " }" @@ -185,7 +184,7 @@ Read commands from provided file or standard input and invoke them. First failure will cause termination of bridge command. .TP -.BR "\-force" +.B "\-force" Don't terminate bridge command on errors in batch mode. If there were any errors during execution of the commands, the application return code will be non zero. @@ -395,7 +394,7 @@ bridge FDB. Controls whether a given port will flood unicast traffic for which there is no FDB entry. By default this flag is on. .TP -.BI hwmode +.B hwmode Some network interface cards support HW bridge functionality and they may be configured in different modes. Currently support modes are: @@ -419,7 +418,7 @@ instead of multicast. By default this flag is off. This is done by copying the packet per host and changing the multicast destination MAC to a unicast one accordingly. -.BR mcast_to_unicast +.B mcast_to_unicast works on top of the multicast snooping feature of the bridge. Which means unicast copies are only delivered to hosts which are interested in it and signalized this via IGMP/MLD reports @@ -464,15 +463,15 @@ If the port loses carrier all traffic will be redirected to the configured backup port .TP -.BR nobackup_port +.B nobackup_port Removes the currently configured backup port .TP -.BI self +.B self link setting is configured on specified physical device .TP -.BI master +.B master link setting is configured on the software bridge (default) .TP @@ -501,7 +500,7 @@ and delete old ones. This command creates a new fdb entry. .TP -.BI "LLADDR" +.B LLADDR the Ethernet MAC address. .TP @@ -633,7 +632,7 @@ and last used time for each entry. lookup a bridge forwarding table entry. .TP -.BI "LLADDR" +.B LLADDR the Ethernet MAC address. .TP @@ -757,21 +756,21 @@ dst_metadata for every packet that belongs to this vlan (applicable to bridge ports with vlan_tunnel flag set). .TP -.BI pvid +.B pvid the vlan specified is to be considered a PVID at ingress. Any untagged frames will be assigned to this VLAN. .TP -.BI untagged +.B untagged the vlan specified is to be treated as untagged on egress. .TP -.BI self +.B self the vlan is configured on the specified physical device. Required if the device is the bridge device. .TP -.BI master +.B master the vlan is configured on the software bridge (default). .SS bridge vlan delete - delete a vlan filter entry diff --git a/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8 index ac01bf60..279100c3 100644 --- a/man/man8/devlink-dev.8 +++ b/man/man8/devlink-dev.8 @@ -26,61 +26,60 @@ devlink-dev \- devlink device configuration .B devlink dev help .ti -8 -.BR "devlink dev eswitch set" -.IR DEV -.RI "[ " +.B devlink dev eswitch set +.I DEV +[ .BR mode " { " legacy " | " switchdev " } " -.RI "]" -.RI "[ " +] [ .BR inline-mode " { " none " | " link " | " network " | " transport " } " -.RI "]" -.RI "[ " +] [ .BR encap-mode " { " none " | " basic " } " -.RI "]" +] .ti -8 -.BR "devlink dev eswitch show" -.IR DEV +.B devlink dev eswitch show +.I DEV .ti -8 -.BR "devlink dev param set" -.IR DEV -.BR name -.IR PARAMETER -.BR value -.IR VALUE +.B devlink dev param set +.I DEV +.B name +.I PARAMETER +.B value +.I VALUE .BR cmode " { " runtime " | " driverinit " | " permanent " } " .ti -8 -.BR "devlink dev param show" -.RI "[ " -.IR DEV -.BR name -.IR PARAMETER -.RI "]" +.B devlink dev param show +[ +.I DEV +.B name +.I PARAMETER +] .ti -8 -.BR "devlink dev reload" -.IR DEV -.RI "[ " -.BI "netns { " PID " | " NAME " | " ID " } -.RI "]" +.B devlink dev reload +.I DEV +[ +.B netns +.RI "{ " PID " | " NAME " | " ID " }" +] .ti -8 -.BR "devlink dev info" -.RI "[ " -.IR DEV -.RI "]" +.B devlink dev info +[ +.I DEV +] .ti -8 -.BR "devlink dev flash" -.IR DEV -.BR file -.IR PATH -.RI "[" -.BR target -.IR ID -.RI "]" +.B devlink dev flash +.I DEV +.B file +.I PATH +[ +.B target +.I ID +] .SH "DESCRIPTION" .SS devlink dev show - display devlink device attributes @@ -159,8 +158,8 @@ Configuration mode in which the new value is set. .SS devlink dev param show - display devlink device supported configuration parameters attributes -.BR name -.IR PARAMETER +.B name +.I PARAMETER Specify parameter name to show. If this argument is omitted all parameters supported by devlink devices are listed. @@ -170,8 +169,8 @@ If this argument is omitted all parameters supported by devlink devices are list .I "DEV" - Specifies the devlink device to reload. -.BR netns -.BI { " PID " | " NAME " | " ID " } +.B netns +.RI { " PID " | " NAME " | " ID " } - Specifies the network namespace to reload into, either by pid, name or id. .SS devlink dev info - display device information. @@ -200,13 +199,13 @@ If this argument is omitted all devices are listed. .I "DEV" - specifies the devlink device to write to. -.BR file +.B file .I PATH - Path to the file which will be written into device's flash. The path needs to be relative to one of the directories searched by the kernel firmware loaded, such as /lib/firmware. -.BR component +.B component .I NAME - If device stores multiple firmware images in non-volatile memory, this parameter may be used to indicate which firmware image should be written. diff --git a/man/man8/devlink-health.8 b/man/man8/devlink-health.8 index 8a3c77be..215f549b 100644 --- a/man/man8/devlink-health.8 +++ b/man/man8/devlink-health.8 @@ -18,49 +18,47 @@ devlink-health \- devlink health reporting and recovery \fB\-V\fR[\fIersion\fR] } .ti -8 -.BR "devlink health show" +.B devlink health show .RI "[ " DEV "" .B reporter .RI ""REPORTER " ] " .ti -8 -.BR "devlink health recover" +.B devlink health recover .RI "" DEV "" .B reporter .RI "" REPORTER "" .ti -8 -.BR "devlink health diagnose" +.B devlink health diagnose .RI "" DEV "" .B reporter .RI "" REPORTER "" .ti -8 -.BR "devlink health dump show" +.B devlink health dump show .RI "" DEV "" .B reporter .RI "" REPORTER "" .ti -8 -.BR "devlink health dump clear" +.B devlink health dump clear .RI "" DEV "" .B reporter .RI "" REPORTER "" .ti -8 -.BR "devlink health set" +.B devlink health set .RI "" DEV "" .B reporter .RI "" REPORTER "" -.RI "[ " +[ .BI "grace_period " MSEC " -.RI "]" -.RI "[ " +] [ .BR auto_recover " { " true " | " false " } " -.RI "]" -.RI "[ " +] [ .BR auto_dump " { " true " | " false " } " -.RI "]" +] .ti -8 .B devlink health help diff --git a/man/man8/devlink-trap.8 b/man/man8/devlink-trap.8 index f01f8317..f5e66412 100644 --- a/man/man8/devlink-trap.8 +++ b/man/man8/devlink-trap.8 @@ -26,7 +26,7 @@ devlink-trap \- devlink trap configuration .ti -8 .BI "devlink trap set " DEV " trap " TRAP -.RB "[ " action " { " trap " | " drop " } ]" +.RB "[ " action " { " trap " | " drop " | " mirror " } ]" .ti -8 .B "devlink trap group show" @@ -36,7 +36,7 @@ devlink-trap \- devlink trap configuration .ti -8 .BI "devlink trap group set " DEV " group " GROUP -.RB "[ " action " { " trap " | " drop " } ]" +.RB "[ " action " { " trap " | " drop " | " mirror " } ]" .br .RB "[ " policer .IB "POLICER " ] @@ -76,7 +76,7 @@ Only applicable if a devlink device is also specified. - specifies the packet trap. .TP -.BR action " { " trap " | " drop " } " +.BR action " { " trap " | " drop " | " mirror " } " packet trap action. .I trap @@ -85,6 +85,9 @@ packet trap action. .I drop - the packet is dropped by the underlying device and a copy is not sent to the CPU. +.I mirror +- the packet is forwarded by the underlying device and a copy is sent to the CPU. + .SS devlink trap group show - display available packet trap groups and their attributes .PP @@ -108,14 +111,15 @@ Only applicable if a devlink device is also specified. - specifies the packet trap group. .TP -.BR action " { " trap " | " drop " } " +.BR action " { " trap " | " drop " | " mirror " } " packet trap action. The action is set for all the packet traps member in the trap group. The actions of non-drop traps cannot be changed and are thus skipped. .TP .BI policer " POLICER" -packet trap policer. The policer to bind to the packet trap group. +packet trap policer. The policer to bind to the packet trap group. A value of +"0" will unbind the currently bound policer. .TP .B nopolicer diff --git a/man/man8/devlink.8 b/man/man8/devlink.8 index 7f4eda56..866fda51 100644 --- a/man/man8/devlink.8 +++ b/man/man8/devlink.8 @@ -31,7 +31,7 @@ Read commands from provided file or standard input and invoke them. First failure will cause termination of devlink. .TP -.BR "\-force" +.B \-force Don't terminate devlink on errors in batch mode. If there were any errors during execution of the commands, the application return code will be non zero. diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index e8a25451..c6bd2c53 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -223,6 +223,7 @@ ip-link \- network device configuration .BR ipvtap " |" .BR lowpan " |" .BR geneve " |" +.BR bareudp " |" .BR vrf " |" .BR macsec " |" .BR netdevsim " |" @@ -356,6 +357,9 @@ Link types: .BR geneve - GEneric NEtwork Virtualization Encapsulation .sp +.BR bareudp +- Bare UDP L3 encapsulation support +.sp .BR macsec - Interface for IEEE 802.1AE MAC Security (MACsec) .sp @@ -1293,6 +1297,46 @@ options. .in -8 +.TP +Bareudp Type Support +For a link of type +.I Bareudp +the following additional arguments are supported: + +.BI "ip link add " DEVICE +.BI type " bareudp " dstport " PORT " ethertype " ETHERTYPE" +[ +.BI srcportmin " SRCPORTMIN " +] [ +.RB [ no ] multiproto +] + +.in +8 +.sp +.BI dstport " PORT" +- specifies the destination port for the UDP tunnel. + +.sp +.BI ethertype " ETHERTYPE" +- specifies the ethertype of the L3 protocol being tunnelled. + +.sp +.BI srcportmin " SRCPORTMIN" +- selects the lowest value of the UDP tunnel source port range. + +.sp +.RB [ no ] multiproto +- activates support for protocols similar to the one +.RB "specified by " ethertype . +When +.I ETHERTYPE +is "mpls_uc" (that is, unicast MPLS), this allows the tunnel to also handle +multicast MPLS. +When +.I ETHERTYPE +is "ipv4", this allows the tunnel to also handle IPv6. This option is disabled +by default. + .TP MACVLAN and MACVTAP Type Support For a link of type diff --git a/man/man8/ip-xfrm.8 b/man/man8/ip-xfrm.8 index f99f30bb..aa28db49 100644 --- a/man/man8/ip-xfrm.8 +++ b/man/man8/ip-xfrm.8 @@ -61,6 +61,8 @@ ip-xfrm \- transform configuration .IR EXTRA-FLAG-LIST " ]" .RB "[ " output-mark .IR OUTPUT-MARK " ]" +.RB "[ " if_id +.IR IF-ID " ]" .ti -8 .B "ip xfrm state allocspi" @@ -238,6 +240,8 @@ ip-xfrm \- transform configuration .IR PRIORITY " ]" .RB "[ " flag .IR FLAG-LIST " ]" +.RB "[ " if_id +.IR IF-ID " ]" .RI "[ " LIMIT-LIST " ] [ " TMPL-LIST " ]" .ti -8 @@ -255,6 +259,8 @@ ip-xfrm \- transform configuration .IR MASK " ] ]" .RB "[ " ptype .IR PTYPE " ]" +.RB "[ " if_id +.IR IF-ID " ]" .ti -8 .BR ip " [ " -4 " | " -6 " ] " "xfrm policy" " { " deleteall " | " list " }" @@ -561,6 +567,10 @@ used to match xfrm policies and states used to set the output mark to influence the routing of the packets emitted by the state +.TP +.I IF-ID +xfrm interface identifier used to in both xfrm policies and states + .sp .PP .TS diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index 4d32ff1b..b5bcfd1d 100644 --- a/man/man8/tc-flower.8 +++ b/man/man8/tc-flower.8 @@ -46,6 +46,8 @@ flower \- flow based traffic control filter .IR PRIORITY " | " .BR cvlan_ethtype " { " ipv4 " | " ipv6 " | " .IR ETH_TYPE " } | " +.B mpls +.IR LSE_LIST " | " .B mpls_label .IR LABEL " | " .B mpls_tc @@ -96,7 +98,24 @@ flower \- flow based traffic control filter } .IR OPTIONS " | " .BR ip_flags -.IR IP_FLAGS +.IR IP_FLAGS " }" + +.ti -8 +.IR LSE_LIST " := [ " LSE_LIST " ] " LSE + +.ti -8 +.IR LSE " := " +.B lse depth +.IR DEPTH " { " +.B label +.IR LABEL " | " +.B tc +.IR TC " | " +.B bos +.IR BOS " | " +.B ttl +.IR TTL " }" + .SH DESCRIPTION The .B flower @@ -182,6 +201,56 @@ Match on QinQ layer three protocol. may be either .BR ipv4 ", " ipv6 or an unsigned 16bit value in hexadecimal format. + +.TP +.BI mpls " LSE_LIST" +Match on the MPLS label stack. +.I LSE_LIST +is a list of Label Stack Entries, each introduced by the +.BR lse " keyword." +This option can't be used together with the standalone +.BR mpls_label ", " mpls_tc ", " mpls_bos " and " mpls_ttl " options." +.RS +.TP +.BI lse " LSE_OPTIONS" +Match on an MPLS Label Stack Entry. +.I LSE_OPTIONS +is a list of options that describe the properties of the LSE to match. +.RS +.TP +.BI depth " DEPTH" +The depth of the Label Stack Entry to consider. Depth starts at 1 (the +outermost Label Stack Entry). The maximum usable depth may be limited by the +kernel. This option is mandatory. +.I DEPTH +is an unsigned 8 bit value in decimal format. +.TP +.BI label " LABEL" +Match on the MPLS Label field at the specified +.BR depth . +.I LABEL +is an unsigned 20 bit value in decimal format. +.TP +.BI tc " TC" +Match on the MPLS Traffic Class field at the specified +.BR depth . +.I TC +is an unsigned 3 bit value in decimal format. +.TP +.BI bos " BOS" +Match on the MPLS Bottom Of Stack field at the specified +.BR depth . +.I BOS +is a 1 bit value in decimal format. +.TP +.BI ttl " TTL" +Match on the MPLS Time To Live field at the specified +.BR depth . +.I TTL +is an unsigned 8 bit value in decimal format. +.RE +.RE + .TP .BI mpls_label " LABEL" Match the label id in the outermost MPLS label stack entry. @@ -393,7 +462,7 @@ on the matches of the next lower layer. Precisely, layer one and two matches (\fBindev\fR, \fBdst_mac\fR and \fBsrc_mac\fR) have no dependency, MPLS and layer three matches -(\fBmpls_label\fR, \fBmpls_tc\fR, \fBmpls_bos\fR, \fBmpls_ttl\fR, +(\fBmpls\fR, \fBmpls_label\fR, \fBmpls_tc\fR, \fBmpls_bos\fR, \fBmpls_ttl\fR, \fBip_proto\fR, \fBdst_ip\fR, \fBsrc_ip\fR, \fBarp_tip\fR, \fBarp_sip\fR, \fBarp_op\fR, \fBarp_tha\fR, \fBarp_sha\fR and \fBip_flags\fR) depend on the diff --git a/man/man8/tc.8 b/man/man8/tc.8 index eba73dbf..7e9019f5 100644 --- a/man/man8/tc.8 +++ b/man/man8/tc.8 @@ -77,9 +77,13 @@ tc \- show / manipulate traffic control settings .B tc .RI "[ " OPTIONS " ]" .RI "[ " FORMAT " ]" -.B qdisc show [ dev +.B qdisc { show | list } [ dev \fIDEV\fR -.B ] +.B ] [ root | ingress | handle +\fIQHANDLE\fR +.B | parent +\fICLASSID\fR +.B ] [ invisible ] .P .B tc .RI "[ " OPTIONS " ]" @@ -473,7 +477,7 @@ qdiscs will use all three: tc filters If tc filters are attached to a class, they are consulted first for relevant instructions. Filters can match on all fields of a packet header, -as well as on the firewall mark applied by ipchains or iptables. +as well as on the firewall mark applied by iptables. .TP Type of Service Some qdiscs have built in rules for classifying packets based on the TOS field. diff --git a/misc/rtacct.c b/misc/rtacct.c index b1cb3788..c4bb5bc3 100644 --- a/misc/rtacct.c +++ b/misc/rtacct.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/tc/f_flower.c b/tc/f_flower.c index fc136911..00c919fd 100644 --- a/tc/f_flower.c +++ b/tc/f_flower.c @@ -59,6 +59,7 @@ static void explain(void) " ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n" " ip_tos MASKED-IP_TOS |\n" " ip_ttl MASKED-IP_TTL |\n" + " mpls LSE-LIST |\n" " mpls_label LABEL |\n" " mpls_tc TC |\n" " mpls_bos BOS |\n" @@ -89,6 +90,8 @@ static void explain(void) " ct_label MASKED_CT_LABEL |\n" " ct_mark MASKED_CT_MARK |\n" " ct_zone MASKED_CT_ZONE }\n" + " LSE-LIST := [ LSE-LIST ] LSE\n" + " LSE := lse depth DEPTH { label LABEL | tc TC | bos BOS | ttl TTL }\n" " FILTERID := X:Y:Z\n" " MASKED_LLADDR := { LLADDR | LLADDR/MASK | LLADDR/BITS }\n" " MASKED_CT_STATE := combination of {+|-} and flags trk,est,new\n" @@ -1199,11 +1202,127 @@ static int flower_parse_enc_opts_erspan(char *str, struct nlmsghdr *n) return 0; } +static int flower_parse_mpls_lse(int *argc_p, char ***argv_p, + struct nlmsghdr *nlh) +{ + struct rtattr *lse_attr; + char **argv = *argv_p; + int argc = *argc_p; + __u8 depth = 0; + int ret; + + lse_attr = addattr_nest(nlh, MAX_MSG, + TCA_FLOWER_KEY_MPLS_OPTS_LSE | NLA_F_NESTED); + + while (argc > 0) { + if (matches(*argv, "depth") == 0) { + NEXT_ARG(); + ret = get_u8(&depth, *argv, 10); + if (ret < 0 || depth < 1) { + fprintf(stderr, "Illegal \"depth\"\n"); + return -1; + } + addattr8(nlh, MAX_MSG, + TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH, depth); + } else if (matches(*argv, "label") == 0) { + __u32 label; + + NEXT_ARG(); + ret = get_u32(&label, *argv, 10); + if (ret < 0 || + label & ~(MPLS_LS_LABEL_MASK >> MPLS_LS_LABEL_SHIFT)) { + fprintf(stderr, "Illegal \"label\"\n"); + return -1; + } + addattr32(nlh, MAX_MSG, + TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL, label); + } else if (matches(*argv, "tc") == 0) { + __u8 tc; + + NEXT_ARG(); + ret = get_u8(&tc, *argv, 10); + if (ret < 0 || + tc & ~(MPLS_LS_TC_MASK >> MPLS_LS_TC_SHIFT)) { + fprintf(stderr, "Illegal \"tc\"\n"); + return -1; + } + addattr8(nlh, MAX_MSG, TCA_FLOWER_KEY_MPLS_OPT_LSE_TC, + tc); + } else if (matches(*argv, "bos") == 0) { + __u8 bos; + + NEXT_ARG(); + ret = get_u8(&bos, *argv, 10); + if (ret < 0 || bos & ~(MPLS_LS_S_MASK >> MPLS_LS_S_SHIFT)) { + fprintf(stderr, "Illegal \"bos\"\n"); + return -1; + } + addattr8(nlh, MAX_MSG, TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS, + bos); + } else if (matches(*argv, "ttl") == 0) { + __u8 ttl; + + NEXT_ARG(); + ret = get_u8(&ttl, *argv, 10); + if (ret < 0 || ttl & ~(MPLS_LS_TTL_MASK >> MPLS_LS_TTL_SHIFT)) { + fprintf(stderr, "Illegal \"ttl\"\n"); + return -1; + } + addattr8(nlh, MAX_MSG, TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL, + ttl); + } else { + break; + } + argc--; argv++; + } + + if (!depth) { + missarg("depth"); + return -1; + } + + addattr_nest_end(nlh, lse_attr); + + *argc_p = argc; + *argv_p = argv; + + return 0; +} + +static int flower_parse_mpls(int *argc_p, char ***argv_p, struct nlmsghdr *nlh) +{ + struct rtattr *mpls_attr; + char **argv = *argv_p; + int argc = *argc_p; + + mpls_attr = addattr_nest(nlh, MAX_MSG, + TCA_FLOWER_KEY_MPLS_OPTS | NLA_F_NESTED); + + while (argc > 0) { + if (matches(*argv, "lse") == 0) { + NEXT_ARG(); + if (flower_parse_mpls_lse(&argc, &argv, nlh) < 0) + return -1; + } else { + break; + } + } + + addattr_nest_end(nlh, mpls_attr); + + *argc_p = argc; + *argv_p = argv; + + return 0; +} + static int flower_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { int ret; struct tcmsg *t = NLMSG_DATA(n); + bool mpls_format_old = false; + bool mpls_format_new = false; struct rtattr *tail; __be16 eth_type = TC_H_MIN(t->tcm_info); __be16 vlan_ethtype = 0; @@ -1381,6 +1500,23 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, &cvlan_ethtype, n); if (ret < 0) return -1; + } else if (matches(*argv, "mpls") == 0) { + NEXT_ARG(); + if (eth_type != htons(ETH_P_MPLS_UC) && + eth_type != htons(ETH_P_MPLS_MC)) { + fprintf(stderr, + "Can't set \"mpls\" if ethertype isn't MPLS\n"); + return -1; + } + if (mpls_format_old) { + fprintf(stderr, + "Can't set \"mpls\" if \"mpls_label\", \"mpls_tc\", \"mpls_bos\" or \"mpls_ttl\" is set\n"); + return -1; + } + mpls_format_new = true; + if (flower_parse_mpls(&argc, &argv, n) < 0) + return -1; + continue; } else if (matches(*argv, "mpls_label") == 0) { __u32 label; @@ -1391,6 +1527,12 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, "Can't set \"mpls_label\" if ethertype isn't MPLS\n"); return -1; } + if (mpls_format_new) { + fprintf(stderr, + "Can't set \"mpls_label\" if \"mpls\" is set\n"); + return -1; + } + mpls_format_old = true; ret = get_u32(&label, *argv, 10); if (ret < 0 || label & ~(MPLS_LS_LABEL_MASK >> MPLS_LS_LABEL_SHIFT)) { fprintf(stderr, "Illegal \"mpls_label\"\n"); @@ -1407,6 +1549,12 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, "Can't set \"mpls_tc\" if ethertype isn't MPLS\n"); return -1; } + if (mpls_format_new) { + fprintf(stderr, + "Can't set \"mpls_tc\" if \"mpls\" is set\n"); + return -1; + } + mpls_format_old = true; ret = get_u8(&tc, *argv, 10); if (ret < 0 || tc & ~(MPLS_LS_TC_MASK >> MPLS_LS_TC_SHIFT)) { fprintf(stderr, "Illegal \"mpls_tc\"\n"); @@ -1423,6 +1571,12 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, "Can't set \"mpls_bos\" if ethertype isn't MPLS\n"); return -1; } + if (mpls_format_new) { + fprintf(stderr, + "Can't set \"mpls_bos\" if \"mpls\" is set\n"); + return -1; + } + mpls_format_old = true; ret = get_u8(&bos, *argv, 10); if (ret < 0 || bos & ~(MPLS_LS_S_MASK >> MPLS_LS_S_SHIFT)) { fprintf(stderr, "Illegal \"mpls_bos\"\n"); @@ -1439,6 +1593,12 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, "Can't set \"mpls_ttl\" if ethertype isn't MPLS\n"); return -1; } + if (mpls_format_new) { + fprintf(stderr, + "Can't set \"mpls_ttl\" if \"mpls\" is set\n"); + return -1; + } + mpls_format_old = true; ret = get_u8(&ttl, *argv, 10); if (ret < 0 || ttl & ~(MPLS_LS_TTL_MASK >> MPLS_LS_TTL_SHIFT)) { fprintf(stderr, "Illegal \"mpls_ttl\"\n"); @@ -2316,6 +2476,66 @@ static void flower_print_u32(const char *name, struct rtattr *attr) print_uint(PRINT_ANY, name, namefrm, rta_getattr_u32(attr)); } +static void flower_print_mpls_opt_lse(const char *name, struct rtattr *lse) +{ + struct rtattr *tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1]; + struct rtattr *attr; + + if (lse->rta_type != (TCA_FLOWER_KEY_MPLS_OPTS_LSE | NLA_F_NESTED)) { + fprintf(stderr, "rta_type 0x%x, expecting 0x%x (0x%x & 0x%x)\n", + lse->rta_type, + TCA_FLOWER_KEY_MPLS_OPTS_LSE & NLA_F_NESTED, + TCA_FLOWER_KEY_MPLS_OPTS_LSE, NLA_F_NESTED); + return; + } + + parse_rtattr(tb, TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX, RTA_DATA(lse), + RTA_PAYLOAD(lse)); + + print_nl(); + open_json_array(PRINT_ANY, name); + attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH]; + if (attr) + print_hhu(PRINT_ANY, "depth", " depth %u", + rta_getattr_u8(attr)); + attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL]; + if (attr) + print_uint(PRINT_ANY, "label", " label %u", + rta_getattr_u32(attr)); + attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TC]; + if (attr) + print_hhu(PRINT_ANY, "tc", " tc %u", rta_getattr_u8(attr)); + attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS]; + if (attr) + print_hhu(PRINT_ANY, "bos", " bos %u", rta_getattr_u8(attr)); + attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL]; + if (attr) + print_hhu(PRINT_ANY, "ttl", " ttl %u", rta_getattr_u8(attr)); + close_json_array(PRINT_JSON, NULL); +} + +static void flower_print_mpls_opts(const char *name, struct rtattr *attr) +{ + struct rtattr *lse; + int rem; + + if (!attr || !(attr->rta_type & NLA_F_NESTED)) + return; + + print_nl(); + open_json_array(PRINT_ANY, name); + rem = RTA_PAYLOAD(attr); + lse = RTA_DATA(attr); + while (RTA_OK(lse, rem)) { + flower_print_mpls_opt_lse(" lse", lse); + lse = RTA_NEXT(lse, rem); + }; + if (rem) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + rem, lse->rta_len); + close_json_array(PRINT_JSON, NULL); +} + static void flower_print_arp_op(const char *name, struct rtattr *op_attr, struct rtattr *mask_attr) @@ -2430,6 +2650,7 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, flower_print_ip_attr("ip_ttl", tb[TCA_FLOWER_KEY_IP_TTL], tb[TCA_FLOWER_KEY_IP_TTL_MASK]); + flower_print_mpls_opts(" mpls", tb[TCA_FLOWER_KEY_MPLS_OPTS]); flower_print_u32("mpls_label", tb[TCA_FLOWER_KEY_MPLS_LABEL]); flower_print_u8("mpls_tc", tb[TCA_FLOWER_KEY_MPLS_TC]); flower_print_u8("mpls_bos", tb[TCA_FLOWER_KEY_MPLS_BOS]); diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c index 181fe2f0..8eb08c34 100644 --- a/tc/tc_qdisc.c +++ b/tc/tc_qdisc.c @@ -35,11 +35,12 @@ static int usage(void) " [ ingress_block BLOCK_INDEX ] [ egress_block BLOCK_INDEX ]\n" " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" "\n" - " tc qdisc show [ dev STRING ] [ ingress | clsact ] [ invisible ]\n" + " tc qdisc { show | list } [ dev STRING ] [ QDISC_ID ] [ invisible ]\n" "Where:\n" "QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n" "OPTIONS := ... try tc qdisc add help\n" - "STAB_OPTIONS := ... try tc qdisc add stab help\n"); + "STAB_OPTIONS := ... try tc qdisc add stab help\n" + "QDISC_ID := { root | ingress | handle QHANDLE | parent CLASSID }\n"); return -1; } @@ -212,6 +213,8 @@ static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv) } static int filter_ifindex; +static __u32 filter_parent; +static __u32 filter_handle; int print_qdisc(struct nlmsghdr *n, void *arg) { @@ -235,6 +238,12 @@ int print_qdisc(struct nlmsghdr *n, void *arg) if (filter_ifindex && filter_ifindex != t->tcm_ifindex) return 0; + if (filter_handle && filter_handle != t->tcm_handle) + return 0; + + if (filter_parent && filter_parent != t->tcm_parent) + return 0; + parse_rtattr_flags(tb, TCA_MAX, TCA_RTA(t), len, NLA_F_NESTED); if (tb[TCA_KIND] == NULL) { @@ -344,21 +353,55 @@ int print_qdisc(struct nlmsghdr *n, void *arg) static int tc_qdisc_list(int argc, char **argv) { - struct tcmsg t = { .tcm_family = AF_UNSPEC }; + struct { + struct nlmsghdr n; + struct tcmsg t; + char buf[256]; + } req = { + .n.nlmsg_type = RTM_GETQDISC, + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), + .t.tcm_family = AF_UNSPEC, + }; + char d[IFNAMSIZ] = {}; bool dump_invisible = false; + __u32 handle; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "root") == 0) { + if (filter_parent) + invarg("parent is already specified", *argv); + else if (filter_handle) + invarg("handle is already specified", *argv); + filter_parent = TC_H_ROOT; } else if (strcmp(*argv, "ingress") == 0 || - strcmp(*argv, "clsact") == 0) { - if (t.tcm_parent) { - fprintf(stderr, "Duplicate parent ID\n"); - usage(); - } - t.tcm_parent = TC_H_INGRESS; + strcmp(*argv, "clsact") == 0) { + if (filter_parent) + invarg("parent is already specified", *argv); + else if (filter_handle) + invarg("handle is already specified", *argv); + filter_parent = TC_H_INGRESS; + } else if (matches(*argv, "parent") == 0) { + if (filter_parent) + invarg("parent is already specified", *argv); + else if (filter_handle) + invarg("handle is already specified", *argv); + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) + invarg("invalid parent ID", *argv); + filter_parent = handle; + } else if (matches(*argv, "handle") == 0) { + if (filter_parent) + invarg("parent is already specified", *argv); + else if (filter_handle) + invarg("handle is already specified", *argv); + NEXT_ARG(); + if (get_qdisc_handle(&handle, *argv)) + invarg("invalid handle ID", *argv); + filter_handle = handle; } else if (matches(*argv, "help") == 0) { usage(); } else if (strcmp(*argv, "invisible") == 0) { @@ -374,32 +417,18 @@ static int tc_qdisc_list(int argc, char **argv) ll_init_map(&rth); if (d[0]) { - t.tcm_ifindex = ll_name_to_index(d); - if (!t.tcm_ifindex) + req.t.tcm_ifindex = ll_name_to_index(d); + if (!req.t.tcm_ifindex) return -nodev(d); - filter_ifindex = t.tcm_ifindex; + filter_ifindex = req.t.tcm_ifindex; } if (dump_invisible) { - struct { - struct nlmsghdr n; - struct tcmsg t; - char buf[256]; - } req = { - .n.nlmsg_type = RTM_GETQDISC, - .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), - }; - - req.t.tcm_family = AF_UNSPEC; - addattr(&req.n, 256, TCA_DUMP_INVISIBLE); - if (rtnl_dump_request_n(&rth, &req.n) < 0) { - perror("Cannot send dump request"); - return 1; - } + } - } else if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) { - perror("Cannot send dump request"); + if (rtnl_dump_request_n(&rth, &req.n) < 0) { + perror("Cannot send request"); return 1; } @@ -427,10 +456,6 @@ int do_qdisc(int argc, char **argv) return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_REPLACE, argc-1, argv+1); if (matches(*argv, "delete") == 0) return tc_qdisc_modify(RTM_DELQDISC, 0, argc-1, argv+1); -#if 0 - if (matches(*argv, "get") == 0) - return tc_qdisc_get(RTM_GETQDISC, 0, argc-1, argv+1); -#endif if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 || matches(*argv, "lst") == 0) return tc_qdisc_list(argc-1, argv+1); diff --git a/tipc/link.c b/tipc/link.c index ba77a201..192736ea 100644 --- a/tipc/link.c +++ b/tipc/link.c @@ -217,7 +217,7 @@ static int cmd_link_get_bcast_cb(const struct nlmsghdr *nlh, void *data) print_string(PRINT_ANY, "method", "%s", "AUTOSELECT"); close_json_object(); open_json_object(NULL); - print_uint(PRINT_ANY, "ratio", " ratio:%u%\n", + print_uint(PRINT_ANY, "ratio", " ratio:%u\n", mnl_attr_get_u32(props[prop_ratio])); break; default: