summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Hemminger <stephen@networkplumber.org>2023-04-29 12:10:38 -0700
committerStephen Hemminger <stephen@networkplumber.org>2023-04-29 12:10:38 -0700
commit6f9bd171ffe25c8ed81828382117004532a241de (patch)
tree570656a527417689dae3dd75241cca191c52fa1c
parent1be3fcf0abb77063c87aa9504add3178fa20adde (diff)
parent9c7bdc9f3328fb3fd5e7b77eb7b86f6c62538143 (diff)
downloadiproute2-6f9bd171ffe25c8ed81828382117004532a241de.tar.gz
Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next
-rw-r--r--bridge/link.c19
-rw-r--r--bridge/mdb.c163
-rw-r--r--bridge/vlan.c18
-rw-r--r--include/rt_names.h2
-rw-r--r--include/uapi/linux/bpf.h112
-rw-r--r--include/uapi/linux/if_bridge.h11
-rw-r--r--include/uapi/linux/if_link.h2
-rw-r--r--include/uapi/linux/if_packet.h1
-rw-r--r--include/uapi/linux/pkt_sched.h17
-rw-r--r--include/uapi/linux/sctp.h4
-rw-r--r--include/uapi/linux/tc_act/tc_tunnel_key.h1
-rw-r--r--include/uapi/linux/virtio_net.h1
-rw-r--r--include/utils.h8
-rw-r--r--ip/ip_common.h2
-rw-r--r--ip/ipaddress.c34
-rw-r--r--ip/iplink_bridge_slave.c10
-rw-r--r--ip/iplink_macvlan.c26
-rw-r--r--lib/rt_names.c62
-rw-r--r--man/man8/bridge.871
-rw-r--r--man/man8/ip-address.8.in49
-rw-r--r--man/man8/ip-link.8.in26
-rw-r--r--man/man8/tc-mqprio.836
-rw-r--r--man/man8/tc-taprio.811
-rw-r--r--man/man8/tc-tunnel_key.83
-rw-r--r--tc/m_tunnel_key.c48
-rw-r--r--tc/q_mqprio.c99
-rw-r--r--tc/q_taprio.c100
27 files changed, 874 insertions, 62 deletions
diff --git a/bridge/link.c b/bridge/link.c
index 9dd7475d..b3542986 100644
--- a/bridge/link.c
+++ b/bridge/link.c
@@ -165,6 +165,14 @@ static void print_protinfo(FILE *fp, struct rtattr *attr)
if (prtb[IFLA_BRPORT_NEIGH_SUPPRESS])
print_on_off(PRINT_ANY, "neigh_suppress", "neigh_suppress %s ",
rta_getattr_u8(prtb[IFLA_BRPORT_NEIGH_SUPPRESS]));
+ if (prtb[IFLA_BRPORT_NEIGH_VLAN_SUPPRESS]) {
+ struct rtattr *at;
+
+ at = prtb[IFLA_BRPORT_NEIGH_VLAN_SUPPRESS];
+ print_on_off(PRINT_ANY, "neigh_vlan_suppress",
+ "neigh_vlan_suppress %s ",
+ rta_getattr_u8(at));
+ }
if (prtb[IFLA_BRPORT_VLAN_TUNNEL])
print_on_off(PRINT_ANY, "vlan_tunnel", "vlan_tunnel %s ",
rta_getattr_u8(prtb[IFLA_BRPORT_VLAN_TUNNEL]));
@@ -296,6 +304,7 @@ static void usage(void)
" [ mcast_to_unicast {on | off} ]\n"
" [ mcast_max_groups MAX_GROUPS ]\n"
" [ neigh_suppress {on | off} ]\n"
+ " [ neigh_vlan_suppress {on | off} ]\n"
" [ vlan_tunnel {on | off} ]\n"
" [ isolated {on | off} ]\n"
" [ locked {on | off} ]\n"
@@ -322,6 +331,7 @@ static int brlink_modify(int argc, char **argv)
char *d = NULL;
int backup_port_idx = -1;
__s8 neigh_suppress = -1;
+ __s8 neigh_vlan_suppress = -1;
__s8 learning = -1;
__s8 learning_sync = -1;
__s8 flood = -1;
@@ -447,6 +457,12 @@ static int brlink_modify(int argc, char **argv)
neigh_suppress = parse_on_off("neigh_suppress", *argv, &ret);
if (ret)
return ret;
+ } else if (strcmp(*argv, "neigh_vlan_suppress") == 0) {
+ NEXT_ARG();
+ neigh_vlan_suppress = parse_on_off("neigh_vlan_suppress",
+ *argv, &ret);
+ if (ret)
+ return ret;
} else if (strcmp(*argv, "vlan_tunnel") == 0) {
NEXT_ARG();
vlan_tunnel = parse_on_off("vlan_tunnel", *argv, &ret);
@@ -544,6 +560,9 @@ static int brlink_modify(int argc, char **argv)
if (neigh_suppress != -1)
addattr8(&req.n, sizeof(req), IFLA_BRPORT_NEIGH_SUPPRESS,
neigh_suppress);
+ if (neigh_vlan_suppress != -1)
+ addattr8(&req.n, sizeof(req), IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
+ neigh_vlan_suppress);
if (vlan_tunnel != -1)
addattr8(&req.n, sizeof(req), IFLA_BRPORT_VLAN_TUNNEL,
vlan_tunnel);
diff --git a/bridge/mdb.c b/bridge/mdb.c
index 9b550365..dcc08235 100644
--- a/bridge/mdb.c
+++ b/bridge/mdb.c
@@ -14,6 +14,7 @@
#include <linux/if_ether.h>
#include <string.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include "libnetlink.h"
#include "utils.h"
@@ -32,7 +33,8 @@ static void usage(void)
{
fprintf(stderr,
"Usage: bridge mdb { add | del | replace } dev DEV port PORT grp GROUP [src SOURCE] [permanent | temp] [vid VID]\n"
- " [ filter_mode { include | exclude } ] [ source_list SOURCE_LIST ] [ proto PROTO ]\n"
+ " [ filter_mode { include | exclude } ] [ source_list SOURCE_LIST ] [ proto PROTO ] [ dst IPADDR ]\n"
+ " [ dst_port DST_PORT ] [ vni VNI ] [ src_vni SRC_VNI ] [ via DEV ]\n"
" bridge mdb {show} [ dev DEV ] [ vid VID ]\n");
exit(-1);
}
@@ -146,6 +148,21 @@ static void print_src_entry(struct rtattr *src_attr, int af, const char *sep)
close_json_object();
}
+static void print_dst(const struct rtattr *dst_attr)
+{
+ SPRINT_BUF(abuf);
+ int af = AF_INET;
+ const void *dst;
+
+ if (RTA_PAYLOAD(dst_attr) == sizeof(struct in6_addr))
+ af = AF_INET6;
+
+ dst = (const void *)RTA_DATA(dst_attr);
+ print_color_string(PRINT_ANY, ifa_family_color(af),
+ "dst", " dst %s",
+ inet_ntop(af, dst, abuf, sizeof(abuf)));
+}
+
static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
struct nlmsghdr *n, struct rtattr **tb)
{
@@ -240,6 +257,29 @@ static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
if (e->vid)
print_uint(PRINT_ANY, "vid", " vid %u", e->vid);
+ if (tb[MDBA_MDB_EATTR_DST])
+ print_dst(tb[MDBA_MDB_EATTR_DST]);
+
+ if (tb[MDBA_MDB_EATTR_DST_PORT])
+ print_uint(PRINT_ANY, "dst_port", " dst_port %u",
+ rta_getattr_u16(tb[MDBA_MDB_EATTR_DST_PORT]));
+
+ if (tb[MDBA_MDB_EATTR_VNI])
+ print_uint(PRINT_ANY, "vni", " vni %u",
+ rta_getattr_u32(tb[MDBA_MDB_EATTR_VNI]));
+
+ if (tb[MDBA_MDB_EATTR_SRC_VNI])
+ print_uint(PRINT_ANY, "src_vni", " src_vni %u",
+ rta_getattr_u32(tb[MDBA_MDB_EATTR_SRC_VNI]));
+
+ if (tb[MDBA_MDB_EATTR_IFINDEX]) {
+ unsigned int ifindex;
+
+ ifindex = rta_getattr_u32(tb[MDBA_MDB_EATTR_IFINDEX]);
+ print_string(PRINT_ANY, "via", " via %s",
+ ll_index_to_name(ifindex));
+ }
+
if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
__u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]);
@@ -570,6 +610,76 @@ static int mdb_parse_proto(struct nlmsghdr *n, int maxlen, const char *proto)
return 0;
}
+static int mdb_parse_dst(struct nlmsghdr *n, int maxlen, const char *dst)
+{
+ struct in6_addr dst_ip6;
+ __be32 dst_ip4;
+
+ if (inet_pton(AF_INET, dst, &dst_ip4)) {
+ addattr32(n, maxlen, MDBE_ATTR_DST, dst_ip4);
+ return 0;
+ }
+
+ if (inet_pton(AF_INET6, dst, &dst_ip6)) {
+ addattr_l(n, maxlen, MDBE_ATTR_DST, &dst_ip6,
+ sizeof(dst_ip6));
+ return 0;
+ }
+
+ return -1;
+}
+
+static int mdb_parse_dst_port(struct nlmsghdr *n, int maxlen,
+ const char *dst_port)
+{
+ unsigned long port;
+ char *endptr;
+
+ port = strtoul(dst_port, &endptr, 0);
+ if (endptr && *endptr) {
+ struct servent *pse;
+
+ pse = getservbyname(dst_port, "udp");
+ if (!pse)
+ return -1;
+ port = ntohs(pse->s_port);
+ } else if (port > USHRT_MAX) {
+ return -1;
+ }
+
+ addattr16(n, maxlen, MDBE_ATTR_DST_PORT, port);
+
+ return 0;
+}
+
+static int mdb_parse_vni(struct nlmsghdr *n, int maxlen, const char *vni,
+ int attr_type)
+{
+ unsigned long vni_num;
+ char *endptr;
+
+ vni_num = strtoul(vni, &endptr, 0);
+ if ((endptr && *endptr) || vni_num == ULONG_MAX)
+ return -1;
+
+ addattr32(n, maxlen, attr_type, vni_num);
+
+ return 0;
+}
+
+static int mdb_parse_dev(struct nlmsghdr *n, int maxlen, const char *dev)
+{
+ unsigned int ifindex;
+
+ ifindex = ll_name_to_index(dev);
+ if (!ifindex)
+ return -1;
+
+ addattr32(n, maxlen, MDBE_ATTR_IFINDEX, ifindex);
+
+ return 0;
+}
+
static int mdb_modify(int cmd, int flags, int argc, char **argv)
{
struct {
@@ -583,7 +693,8 @@ static int mdb_modify(int cmd, int flags, int argc, char **argv)
.bpm.family = PF_BRIDGE,
};
char *d = NULL, *p = NULL, *grp = NULL, *src = NULL, *mode = NULL;
- char *src_list = NULL, *proto = NULL;
+ char *dst_port = NULL, *vni = NULL, *src_vni = NULL, *via = NULL;
+ char *src_list = NULL, *proto = NULL, *dst = NULL;
struct br_mdb_entry entry = {};
bool set_attrs = false;
short vid = 0;
@@ -622,6 +733,26 @@ static int mdb_modify(int cmd, int flags, int argc, char **argv)
NEXT_ARG();
proto = *argv;
set_attrs = true;
+ } else if (strcmp(*argv, "dst") == 0) {
+ NEXT_ARG();
+ dst = *argv;
+ set_attrs = true;
+ } else if (strcmp(*argv, "dst_port") == 0) {
+ NEXT_ARG();
+ dst_port = *argv;
+ set_attrs = true;
+ } else if (strcmp(*argv, "vni") == 0) {
+ NEXT_ARG();
+ vni = *argv;
+ set_attrs = true;
+ } else if (strcmp(*argv, "src_vni") == 0) {
+ NEXT_ARG();
+ src_vni = *argv;
+ set_attrs = true;
+ } else if (strcmp(*argv, "via") == 0) {
+ NEXT_ARG();
+ via = *argv;
+ set_attrs = true;
} else {
if (matches(*argv, "help") == 0)
usage();
@@ -675,6 +806,34 @@ static int mdb_modify(int cmd, int flags, int argc, char **argv)
return -1;
}
+ if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
+ fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
+ dst);
+ return -1;
+ }
+
+ if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
+ dst_port)) {
+ fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
+ return -1;
+ }
+
+ if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
+ MDBE_ATTR_VNI)) {
+ fprintf(stderr, "Invalid destination VNI \"%s\"\n",
+ vni);
+ return -1;
+ }
+
+ if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
+ MDBE_ATTR_SRC_VNI)) {
+ fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
+ return -1;
+ }
+
+ if (via && mdb_parse_dev(&req.n, sizeof(req), via))
+ return nodev(via);
+
addattr_nest_end(&req.n, nest);
}
diff --git a/bridge/vlan.c b/bridge/vlan.c
index 44e1ba39..5b304ea9 100644
--- a/bridge/vlan.c
+++ b/bridge/vlan.c
@@ -38,6 +38,7 @@ static void usage(void)
" bridge vlan { set } vid VLAN_ID dev DEV [ state STP_STATE ]\n"
" [ mcast_router MULTICAST_ROUTER ]\n"
" [ mcast_max_groups MAX_GROUPS ]\n"
+ " [ neigh_suppress {on | off} ]\n"
" bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"
" bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n"
" bridge vlan global { set } vid VLAN_ID dev DEV\n"
@@ -354,6 +355,18 @@ static int vlan_option_set(int argc, char **argv)
addattr32(&req.n, sizeof(req),
BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS,
max_groups);
+ } else if (strcmp(*argv, "neigh_suppress") == 0) {
+ bool neigh_suppress;
+ int ret;
+
+ NEXT_ARG();
+ neigh_suppress = parse_on_off("neigh_suppress", *argv,
+ &ret);
+ if (ret)
+ return ret;
+ addattr8(&req.n, sizeof(req),
+ BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS,
+ neigh_suppress);
} else {
if (matches(*argv, "help") == 0)
NEXT_ARG();
@@ -1041,6 +1054,11 @@ static void print_vlan_opts(struct rtattr *a, int ifindex)
print_uint(PRINT_ANY, "mcast_max_groups", "mcast_max_groups %u ",
rta_getattr_u32(vattr));
}
+ if (vtb[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS]) {
+ vattr = vtb[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS];
+ print_on_off(PRINT_ANY, "neigh_suppress", "neigh_suppress %s ",
+ rta_getattr_u8(vattr));
+ }
print_nl();
if (show_stats)
__print_one_vlan_stats(&vstats);
diff --git a/include/rt_names.h b/include/rt_names.h
index 6358650d..e96d80f3 100644
--- a/include/rt_names.h
+++ b/include/rt_names.h
@@ -5,6 +5,7 @@
#include <asm/types.h>
const char *rtnl_rtprot_n2a(int id, char *buf, int len);
+const char *rtnl_addrprot_n2a(int id, char *buf, int len);
const char *rtnl_rtscope_n2a(int id, char *buf, int len);
const char *rtnl_rttable_n2a(__u32 id, char *buf, int len);
const char *rtnl_rtrealm_n2a(int id, char *buf, int len);
@@ -13,6 +14,7 @@ const char *rtnl_dsfield_get_name(int id);
const char *rtnl_group_n2a(int id, char *buf, int len);
int rtnl_rtprot_a2n(__u32 *id, const char *arg);
+int rtnl_addrprot_a2n(__u32 *id, const char *arg);
int rtnl_rtscope_a2n(__u32 *id, const char *arg);
int rtnl_rttable_a2n(__u32 *id, const char *arg);
int rtnl_rtrealm_a2n(__u32 *id, const char *arg);
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 4b4d13a3..58e30f17 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -986,6 +986,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_LSM,
BPF_PROG_TYPE_SK_LOOKUP,
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
+ BPF_PROG_TYPE_NETFILTER,
};
enum bpf_attach_type {
@@ -1033,6 +1034,7 @@ enum bpf_attach_type {
BPF_PERF_EVENT,
BPF_TRACE_KPROBE_MULTI,
BPF_LSM_CGROUP,
+ BPF_STRUCT_OPS,
__MAX_BPF_ATTACH_TYPE
};
@@ -1049,6 +1051,7 @@ enum bpf_link_type {
BPF_LINK_TYPE_PERF_EVENT = 7,
BPF_LINK_TYPE_KPROBE_MULTI = 8,
BPF_LINK_TYPE_STRUCT_OPS = 9,
+ BPF_LINK_TYPE_NETFILTER = 10,
MAX_BPF_LINK_TYPE,
};
@@ -1108,7 +1111,7 @@ enum bpf_link_type {
*/
#define BPF_F_STRICT_ALIGNMENT (1U << 0)
-/* If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the
+/* If BPF_F_ANY_ALIGNMENT is used in BPF_PROG_LOAD command, the
* verifier will allow any alignment whatsoever. On platforms
* with strict alignment requirements for loads ands stores (such
* as sparc and mips) the verifier validates that all loads and
@@ -1266,6 +1269,9 @@ enum {
/* Create a map that is suitable to be an inner map with dynamic max entries */
BPF_F_INNER_MAP = (1U << 12),
+
+/* Create a map that will be registered/unregesitered by the backed bpf_link */
+ BPF_F_LINK = (1U << 13),
};
/* Flags for BPF_PROG_QUERY. */
@@ -1403,6 +1409,11 @@ union bpf_attr {
__aligned_u64 fd_array; /* array of FDs */
__aligned_u64 core_relos;
__u32 core_relo_rec_size; /* sizeof(struct bpf_core_relo) */
+ /* output: actual total log contents size (including termintaing zero).
+ * It could be both larger than original log_size (if log was
+ * truncated), or smaller (if log buffer wasn't filled completely).
+ */
+ __u32 log_true_size;
};
struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -1488,6 +1499,11 @@ union bpf_attr {
__u32 btf_size;
__u32 btf_log_size;
__u32 btf_log_level;
+ /* output: actual total log contents size (including termintaing zero).
+ * It could be both larger than original log_size (if log was
+ * truncated), or smaller (if log buffer wasn't filled completely).
+ */
+ __u32 btf_log_true_size;
};
struct {
@@ -1507,7 +1523,10 @@ union bpf_attr {
} task_fd_query;
struct { /* struct used by BPF_LINK_CREATE command */
- __u32 prog_fd; /* eBPF program to attach */
+ union {
+ __u32 prog_fd; /* eBPF program to attach */
+ __u32 map_fd; /* struct_ops to attach */
+ };
union {
__u32 target_fd; /* object to attach to */
__u32 target_ifindex; /* target ifindex */
@@ -1543,17 +1562,34 @@ union bpf_attr {
*/
__u64 cookie;
} tracing;
+ struct {
+ __u32 pf;
+ __u32 hooknum;
+ __s32 priority;
+ __u32 flags;
+ } netfilter;
};
} link_create;
struct { /* struct used by BPF_LINK_UPDATE command */
__u32 link_fd; /* link fd */
- /* new program fd to update link with */
- __u32 new_prog_fd;
+ union {
+ /* new program fd to update link with */
+ __u32 new_prog_fd;
+ /* new struct_ops map fd to update link with */
+ __u32 new_map_fd;
+ };
__u32 flags; /* extra flags */
- /* expected link's program fd; is specified only if
- * BPF_F_REPLACE flag is set in flags */
- __u32 old_prog_fd;
+ union {
+ /* expected link's program fd; is specified only if
+ * BPF_F_REPLACE flag is set in flags.
+ */
+ __u32 old_prog_fd;
+ /* expected link's map fd; is specified only
+ * if BPF_F_REPLACE flag is set.
+ */
+ __u32 old_map_fd;
+ };
} link_update;
struct {
@@ -1647,17 +1683,17 @@ union bpf_attr {
* Description
* This helper is a "printk()-like" facility for debugging. It
* prints a message defined by format *fmt* (of size *fmt_size*)
- * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if
+ * to file *\/sys/kernel/tracing/trace* from TraceFS, if
* available. It can take up to three additional **u64**
* arguments (as an eBPF helpers, the total number of arguments is
* limited to five).
*
* Each time the helper is called, it appends a line to the trace.
- * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is
- * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this.
+ * Lines are discarded while *\/sys/kernel/tracing/trace* is
+ * open, use *\/sys/kernel/tracing/trace_pipe* to avoid this.
* The format of the trace is customizable, and the exact output
* one will get depends on the options set in
- * *\/sys/kernel/debug/tracing/trace_options* (see also the
+ * *\/sys/kernel/tracing/trace_options* (see also the
* *README* file under the same directory). However, it usually
* defaults to something like:
*
@@ -4969,6 +5005,12 @@ union bpf_attr {
* different maps if key/value layout matches across maps.
* Every bpf_timer_set_callback() can have different callback_fn.
*
+ * *flags* can be one of:
+ *
+ * **BPF_F_TIMER_ABS**
+ * Start the timer in absolute expire value instead of the
+ * default relative one.
+ *
* Return
* 0 on success.
* **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier
@@ -5325,11 +5367,22 @@ union bpf_attr {
* Description
* Write *len* bytes from *src* into *dst*, starting from *offset*
* into *dst*.
- * *flags* is currently unused.
+ *
+ * *flags* must be 0 except for skb-type dynptrs.
+ *
+ * For skb-type dynptrs:
+ * * All data slices of the dynptr are automatically
+ * invalidated after **bpf_dynptr_write**\ (). This is
+ * because writing may pull the skb and change the
+ * underlying packet buffer.
+ *
+ * * For *flags*, please see the flags accepted by
+ * **bpf_skb_store_bytes**\ ().
* Return
* 0 on success, -E2BIG if *offset* + *len* exceeds the length
* of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst*
- * is a read-only dynptr or if *flags* is not 0.
+ * is a read-only dynptr or if *flags* is not correct. For skb-type dynptrs,
+ * other errors correspond to errors returned by **bpf_skb_store_bytes**\ ().
*
* void *bpf_dynptr_data(const struct bpf_dynptr *ptr, u32 offset, u32 len)
* Description
@@ -5337,6 +5390,9 @@ union bpf_attr {
*
* *len* must be a statically known value. The returned data slice
* is invalidated whenever the dynptr is invalidated.
+ *
+ * skb and xdp type dynptrs may not use bpf_dynptr_data. They should
+ * instead use bpf_dynptr_slice and bpf_dynptr_slice_rdwr.
* Return
* Pointer to the underlying dynptr data, NULL if the dynptr is
* read-only, if the dynptr is invalid, or if the offset and length
@@ -6359,6 +6415,15 @@ struct bpf_link_info {
struct {
__u32 ifindex;
} xdp;
+ struct {
+ __u32 map_id;
+ } struct_ops;
+ struct {
+ __u32 pf;
+ __u32 hooknum;
+ __s32 priority;
+ __u32 flags;
+ } netfilter;
};
} __attribute__((aligned(8)));
@@ -6934,6 +6999,10 @@ struct bpf_rb_node {
__u64 :64;
} __attribute__((aligned(8)));
+struct bpf_refcount {
+ __u32 :32;
+} __attribute__((aligned(4)));
+
struct bpf_sysctl {
__u32 write; /* Sysctl is being read (= 0) or written (= 1).
* Allows 1,2,4-byte read, but no write.
@@ -7083,4 +7152,21 @@ struct bpf_core_relo {
enum bpf_core_relo_kind kind;
};
+/*
+ * Flags to control bpf_timer_start() behaviour.
+ * - BPF_F_TIMER_ABS: Timeout passed is absolute time, by default it is
+ * relative to current time.
+ */
+enum {
+ BPF_F_TIMER_ABS = (1ULL << 0),
+};
+
+/* BPF numbers iterator state */
+struct bpf_iter_num {
+ /* opaque iterator state; having __u64 here allows to preserve correct
+ * alignment requirements in vmlinux.h, generated from BTF
+ */
+ __u64 __opaque[1];
+} __attribute__((aligned(8)));
+
#endif /* __LINUX_BPF_H__ */
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 921b212d..e3d55b12 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -525,6 +525,7 @@ enum {
BRIDGE_VLANDB_ENTRY_MCAST_ROUTER,
BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS,
BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS,
+ BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS,
__BRIDGE_VLANDB_ENTRY_MAX,
};
#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1)
@@ -633,6 +634,11 @@ enum {
MDBA_MDB_EATTR_GROUP_MODE,
MDBA_MDB_EATTR_SOURCE,
MDBA_MDB_EATTR_RTPROT,
+ MDBA_MDB_EATTR_DST,
+ MDBA_MDB_EATTR_DST_PORT,
+ MDBA_MDB_EATTR_VNI,
+ MDBA_MDB_EATTR_IFINDEX,
+ MDBA_MDB_EATTR_SRC_VNI,
__MDBA_MDB_EATTR_MAX
};
#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1)
@@ -728,6 +734,11 @@ enum {
MDBE_ATTR_SRC_LIST,
MDBE_ATTR_GROUP_MODE,
MDBE_ATTR_RTPROT,
+ MDBE_ATTR_DST,
+ MDBE_ATTR_DST_PORT,
+ MDBE_ATTR_VNI,
+ MDBE_ATTR_IFINDEX,
+ MDBE_ATTR_SRC_VNI,
__MDBE_ATTR_MAX,
};
#define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1)
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index d61bd32d..94fb7ef9 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -567,6 +567,7 @@ enum {
IFLA_BRPORT_MAB,
IFLA_BRPORT_MCAST_N_GROUPS,
IFLA_BRPORT_MCAST_MAX_GROUPS,
+ IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
__IFLA_BRPORT_MAX
};
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -633,6 +634,7 @@ enum {
IFLA_MACVLAN_MACADDR_COUNT,
IFLA_MACVLAN_BC_QUEUE_LEN,
IFLA_MACVLAN_BC_QUEUE_LEN_USED,
+ IFLA_MACVLAN_BC_CUTOFF,
__IFLA_MACVLAN_MAX,
};
diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h
index 78c981d6..9efc4238 100644
--- a/include/uapi/linux/if_packet.h
+++ b/include/uapi/linux/if_packet.h
@@ -59,6 +59,7 @@ struct sockaddr_ll {
#define PACKET_ROLLOVER_STATS 21
#define PACKET_FANOUT_DATA 22
#define PACKET_IGNORE_OUTGOING 23
+#define PACKET_VNET_HDR_SZ 24
#define PACKET_FANOUT_HASH 0
#define PACKET_FANOUT_LB 1
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index 000eec10..51a7addc 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -719,6 +719,11 @@ enum {
#define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1)
+enum {
+ TC_FP_EXPRESS = 1,
+ TC_FP_PREEMPTIBLE = 2,
+};
+
struct tc_mqprio_qopt {
__u8 num_tc;
__u8 prio_tc_map[TC_QOPT_BITMASK + 1];
@@ -733,11 +738,22 @@ struct tc_mqprio_qopt {
#define TC_MQPRIO_F_MAX_RATE 0x8
enum {
+ TCA_MQPRIO_TC_ENTRY_UNSPEC,
+ TCA_MQPRIO_TC_ENTRY_INDEX, /* u32 */
+ TCA_MQPRIO_TC_ENTRY_FP, /* u32 */
+
+ /* add new constants above here */
+ __TCA_MQPRIO_TC_ENTRY_CNT,
+ TCA_MQPRIO_TC_ENTRY_MAX = (__TCA_MQPRIO_TC_ENTRY_CNT - 1)
+};
+
+enum {
TCA_MQPRIO_UNSPEC,
TCA_MQPRIO_MODE,
TCA_MQPRIO_SHAPER,
TCA_MQPRIO_MIN_RATE64,
TCA_MQPRIO_MAX_RATE64,
+ TCA_MQPRIO_TC_ENTRY,
__TCA_MQPRIO_MAX,
};
@@ -1236,6 +1252,7 @@ enum {
TCA_TAPRIO_TC_ENTRY_UNSPEC,
TCA_TAPRIO_TC_ENTRY_INDEX, /* u32 */
TCA_TAPRIO_TC_ENTRY_MAX_SDU, /* u32 */
+ TCA_TAPRIO_TC_ENTRY_FP, /* u32 */
/* add new constants above here */
__TCA_TAPRIO_TC_ENTRY_CNT,
diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h
index c5f42903..d52e1229 100644
--- a/include/uapi/linux/sctp.h
+++ b/include/uapi/linux/sctp.h
@@ -1205,7 +1205,9 @@ enum sctp_sched_type {
SCTP_SS_DEFAULT = SCTP_SS_FCFS,
SCTP_SS_PRIO,
SCTP_SS_RR,
- SCTP_SS_MAX = SCTP_SS_RR
+ SCTP_SS_FC,
+ SCTP_SS_WFQ,
+ SCTP_SS_MAX = SCTP_SS_WFQ
};
/* Probe Interval socket option */
diff --git a/include/uapi/linux/tc_act/tc_tunnel_key.h b/include/uapi/linux/tc_act/tc_tunnel_key.h
index 49ad4033..37c6f612 100644
--- a/include/uapi/linux/tc_act/tc_tunnel_key.h
+++ b/include/uapi/linux/tc_act/tc_tunnel_key.h
@@ -34,6 +34,7 @@ enum {
*/
TCA_TUNNEL_KEY_ENC_TOS, /* u8 */
TCA_TUNNEL_KEY_ENC_TTL, /* u8 */
+ TCA_TUNNEL_KEY_NO_FRAG, /* flag */
__TCA_TUNNEL_KEY_MAX,
};
diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
index 61986c7e..8b9bc4fd 100644
--- a/include/uapi/linux/virtio_net.h
+++ b/include/uapi/linux/virtio_net.h
@@ -61,6 +61,7 @@
#define VIRTIO_NET_F_GUEST_USO6 55 /* Guest can handle USOv6 in. */
#define VIRTIO_NET_F_HOST_USO 56 /* Host can handle USO in. */
#define VIRTIO_NET_F_HASH_REPORT 57 /* Supports hash report */
+#define VIRTIO_NET_F_GUEST_HDRLEN 59 /* Guest provides the exact hdr_len value. */
#define VIRTIO_NET_F_RSS 60 /* Supports RSS RX steering */
#define VIRTIO_NET_F_RSC_EXT 61 /* extended coalescing info */
#define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device
diff --git a/include/utils.h b/include/utils.h
index 2eb80b3e..0f1b3bef 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -284,6 +284,14 @@ unsigned int print_name_and_link(const char *fmt,
_min1 < _min2 ? _min1 : _min2; })
#endif
+#ifndef max
+# define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 < _max2 ? _max2 : _max1; })
+#endif
+
#ifndef __check_format_string
# define __check_format_string(pos_str, pos_args) \
__attribute__ ((format (printf, (pos_str), (pos_args))))
diff --git a/ip/ip_common.h b/ip/ip_common.h
index c4cb1bcb..4a20ec3c 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -28,6 +28,8 @@ struct link_filter {
char *kind;
char *slave_kind;
int target_nsid;
+ bool have_proto;
+ int proto;
};
const char *get_ip_lib_dir(void);
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 9ba81438..41055c43 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -57,11 +57,13 @@ static void usage(void)
" ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n"
" [ nomaster ]\n"
" [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n"
- " [ label LABEL ] [up] [ vrf NAME ] ]\n"
+ " [ label LABEL ] [up] [ vrf NAME ]\n"
+ " [ proto ADDRPROTO ] ]\n"
" ip address {showdump|restore}\n"
"IFADDR := PREFIX | ADDR peer PREFIX\n"
" [ broadcast ADDR ] [ anycast ADDR ]\n"
" [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]\n"
+ " [ proto ADDRPROTO ]\n"
"SCOPE-ID := [ host | link | global | NUMBER ]\n"
"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
"FLAG := [ permanent | dynamic | secondary | primary |\n"
@@ -70,7 +72,9 @@ static void usage(void)
"CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n"
"CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n"
"LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n"
- "LFT := forever | SECONDS\n");
+ "LFT := forever | SECONDS\n"
+ "ADDRPROTO := [ NAME | NUMBER ]\n"
+ );
iplink_types_usage();
exit(-1);
@@ -1568,6 +1572,9 @@ int print_addrinfo(struct nlmsghdr *n, void *arg)
if (filter.family && filter.family != ifa->ifa_family)
return 0;
+ if (filter.have_proto && rta_tb[IFA_PROTO] &&
+ filter.proto != rta_getattr_u8(rta_tb[IFA_PROTO]))
+ return 0;
if (ifa_label_match_rta(ifa->ifa_index, rta_tb[IFA_LABEL]))
return 0;
@@ -1675,6 +1682,14 @@ int print_addrinfo(struct nlmsghdr *n, void *arg)
print_ifa_flags(fp, ifa, ifa_flags);
+ if (rta_tb[IFA_PROTO]) {
+ __u8 proto = rta_getattr_u8(rta_tb[IFA_PROTO]);
+
+ if (proto || is_json_context())
+ print_string(PRINT_ANY, "protocol", "proto %s ",
+ rtnl_addrprot_n2a(proto, b1, sizeof(b1)));
+ }
+
if (rta_tb[IFA_LABEL])
print_string(PRINT_ANY,
"label",
@@ -2196,6 +2211,14 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
} else {
filter.kind = *argv;
}
+ } else if (strcmp(*argv, "proto") == 0) {
+ __u8 proto;
+
+ NEXT_ARG();
+ if (get_u8(&proto, *argv, 0))
+ invarg("\"proto\" value is invalid\n", *argv);
+ filter.have_proto = true;
+ filter.proto = proto;
} else {
if (strcmp(*argv, "dev") == 0)
NEXT_ARG();
@@ -2520,6 +2543,13 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
} else {
ifa_flags |= flag_data->mask;
}
+ } else if (strcmp(*argv, "proto") == 0) {
+ __u8 proto;
+
+ NEXT_ARG();
+ if (get_u8(&proto, *argv, 0))
+ invarg("\"proto\" value is invalid\n", *argv);
+ addattr8(&req.n, sizeof(req), IFA_PROTO, proto);
} else {
if (strcmp(*argv, "local") == 0)
NEXT_ARG();
diff --git a/ip/iplink_bridge_slave.c b/ip/iplink_bridge_slave.c
index 66a67961..11ab2113 100644
--- a/ip/iplink_bridge_slave.c
+++ b/ip/iplink_bridge_slave.c
@@ -37,6 +37,7 @@ static void print_explain(FILE *f)
" [ mcast_to_unicast {on | off} ]\n"
" [ group_fwd_mask MASK ]\n"
" [ neigh_suppress {on | off} ]\n"
+ " [ neigh_vlan_suppress {on | off} ]\n"
" [ vlan_tunnel {on | off} ]\n"
" [ isolated {on | off} ]\n"
" [ locked {on | off} ]\n"
@@ -261,6 +262,11 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
print_on_off(PRINT_ANY, "neigh_suppress", "neigh_suppress %s ",
rta_getattr_u8(tb[IFLA_BRPORT_NEIGH_SUPPRESS]));
+ if (tb[IFLA_BRPORT_NEIGH_VLAN_SUPPRESS])
+ print_on_off(PRINT_ANY, "neigh_vlan_suppress",
+ "neigh_vlan_suppress %s ",
+ rta_getattr_u8(tb[IFLA_BRPORT_NEIGH_VLAN_SUPPRESS]));
+
if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
char convbuf[256];
__u16 fwd_mask;
@@ -393,6 +399,10 @@ static int bridge_slave_parse_opt(struct link_util *lu, int argc, char **argv,
NEXT_ARG();
bridge_slave_parse_on_off("neigh_suppress", *argv, n,
IFLA_BRPORT_NEIGH_SUPPRESS);
+ } else if (strcmp(*argv, "neigh_vlan_suppress") == 0) {
+ NEXT_ARG();
+ bridge_slave_parse_on_off("neigh_vlan_suppress", *argv,
+ n, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS);
} else if (matches(*argv, "group_fwd_mask") == 0) {
__u16 mask;
diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c
index 0f13637d..6bdc76d1 100644
--- a/ip/iplink_macvlan.c
+++ b/ip/iplink_macvlan.c
@@ -26,13 +26,14 @@
static void print_explain(struct link_util *lu, FILE *f)
{
fprintf(f,
- "Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS [bcqueuelen BC_QUEUE_LEN]\n"
+ "Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS [bcqueuelen BC_QUEUE_LEN] [bclim BCLIM]\n"
"\n"
"MODE: private | vepa | bridge | passthru | source\n"
"MODE_FLAG: null | nopromisc | nodst\n"
"MODE_OPTS: for mode \"source\":\n"
"\tmacaddr { { add | del } <macaddr> | set [ <macaddr> [ <macaddr> ... ] ] | flush }\n"
- "BC_QUEUE_LEN: Length of the rx queue for broadcast/multicast: [0-4294967295]\n",
+ "BC_QUEUE_LEN: Length of the rx queue for broadcast/multicast: [0-4294967295]\n"
+ "BCLIM: Threshold for broadcast queueing: 32-bit integer\n",
lu->id
);
}
@@ -67,6 +68,12 @@ static int bc_queue_len_arg(const char *arg)
return -1;
}
+static int bclim_arg(const char *arg)
+{
+ fprintf(stderr, "Error: illegal value for \"bclim\": \"%s\"\n", arg);
+ return -1;
+}
+
static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
struct nlmsghdr *n)
{
@@ -168,6 +175,15 @@ static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
return bc_queue_len_arg(*argv);
}
addattr32(n, 1024, IFLA_MACVLAN_BC_QUEUE_LEN, bc_queue_len);
+ } else if (!strcmp(*argv, "bclim")) {
+ __s32 bclim;
+ NEXT_ARG();
+
+ if (get_s32(&bclim, *argv, 0)) {
+ return bclim_arg(*argv);
+ }
+ addattr_l(n, 1024, IFLA_MACVLAN_BC_CUTOFF,
+ &bclim, sizeof(bclim));
} else if (matches(*argv, "help") == 0) {
explain(lu);
return -1;
@@ -245,6 +261,12 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]
print_luint(PRINT_ANY, "usedbcqueuelen", "usedbcqueuelen %lu ", bc_queue_len);
}
+ if (tb[IFLA_MACVLAN_BC_CUTOFF] &&
+ RTA_PAYLOAD(tb[IFLA_MACVLAN_BC_CUTOFF]) >= sizeof(__s32)) {
+ __s32 bclim = rta_getattr_s32(tb[IFLA_MACVLAN_BC_CUTOFF]);
+ print_int(PRINT_ANY, "bclim", "bclim %d ", bclim);
+ }
+
/* in source mode, there are more options to print */
if (mode != MACVLAN_MODE_SOURCE)
diff --git a/lib/rt_names.c b/lib/rt_names.c
index 2432224a..51d11fd0 100644
--- a/lib/rt_names.c
+++ b/lib/rt_names.c
@@ -226,6 +226,68 @@ int rtnl_rtprot_a2n(__u32 *id, const char *arg)
}
+static char *rtnl_addrprot_tab[256] = {
+ [IFAPROT_UNSPEC] = "unspec",
+ [IFAPROT_KERNEL_LO] = "kernel_lo",
+ [IFAPROT_KERNEL_RA] = "kernel_ra",
+ [IFAPROT_KERNEL_LL] = "kernel_ll",
+};
+static bool rtnl_addrprot_tab_initialized;
+
+static void rtnl_addrprot_initialize(void)
+{
+ rtnl_tab_initialize(CONFDIR "/rt_addrprotos",
+ rtnl_addrprot_tab,
+ ARRAY_SIZE(rtnl_addrprot_tab));
+ rtnl_addrprot_tab_initialized = true;
+}
+
+const char *rtnl_addrprot_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256 || numeric)
+ goto numeric;
+ if (!rtnl_addrprot_tab_initialized)
+ rtnl_addrprot_initialize();
+ if (rtnl_addrprot_tab[id])
+ return rtnl_addrprot_tab[id];
+numeric:
+ snprintf(buf, len, "%#x", id);
+ return buf;
+}
+
+int rtnl_addrprot_a2n(__u32 *id, const char *arg)
+{
+ static char *cache;
+ static unsigned long res;
+ char *end;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ if (!rtnl_addrprot_tab_initialized)
+ rtnl_addrprot_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_addrprot_tab[i] &&
+ strcmp(rtnl_addrprot_tab[i], arg) == 0) {
+ cache = rtnl_addrprot_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = strtoul(arg, &end, 0);
+ if (!end || end == arg || *end || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
static char *rtnl_rtscope_tab[256] = {
[RT_SCOPE_UNIVERSE] = "global",
[RT_SCOPE_NOWHERE] = "nowhere",
diff --git a/man/man8/bridge.8 b/man/man8/bridge.8
index abc0417b..e0552819 100644
--- a/man/man8/bridge.8
+++ b/man/man8/bridge.8
@@ -53,6 +53,7 @@ bridge \- show / manipulate bridge addresses and devices
.IR MULTICAST_ROUTER " ] ["
.BR mcast_to_unicast " { " on " | " off " } ] [ "
.BR neigh_suppress " { " on " | " off " } ] [ "
+.BR neigh_vlan_suppress " { " on " | " off " } ] [ "
.BR vlan_tunnel " { " on " | " off " } ] [ "
.BR isolated " { " on " | " off " } ] [ "
.BR locked " { " on " | " off " } ] [ "
@@ -145,7 +146,17 @@ bridge \- show / manipulate bridge addresses and devices
.B source_list
.IR SOURCE_LIST " ] [ "
.B proto
-.IR PROTO " ]
+.IR PROTO " ] [ "
+.B dst
+.IR IPADDR " ] [ "
+.B dst_port
+.IR DST_PORT " ] [ "
+.B vni
+.IR VNI " ] [ "
+.B src_vni
+.IR SRC_VNI " ] [ "
+.B via
+.IR DEV " ]
.ti -8
.BR "bridge mdb show" " [ "
@@ -174,7 +185,8 @@ bridge \- show / manipulate bridge addresses and devices
.B mcast_max_groups
.IR MAX_GROUPS " ] [ "
.B mcast_router
-.IR MULTICAST_ROUTER " ]"
+.IR MULTICAST_ROUTER " ] [ "
+.BR neigh_suppress " { " on " | " off " } ]"
.ti -8
.BR "bridge vlan" " [ " show " | " tunnelshow " ] [ "
@@ -580,6 +592,13 @@ Controls whether neigh discovery (arp and nd) proxy and suppression is
enabled on the port. By default this flag is off.
.TP
+.BR "neigh_vlan_suppress on " or " neigh_vlan_suppress off "
+Controls whether per-VLAN neigh discovery (arp and nd) proxy and suppression is
+enabled on the port. When on, the \fBbridge link\fR option \fBneigh_suppress\fR
+has no effect and the per-VLAN state is set using the \fBbridge vlan\fR option
+\fBneigh_suppress\fR. By default this flag is off.
+
+.TP
.BR "vlan_tunnel on " or " vlan_tunnel off "
Controls whether vlan to tunnel mapping is enabled on the port. By
default this flag is off.
@@ -970,6 +989,46 @@ then
is assumed.
.in -8
+The next command line parameters apply only
+when the specified device
+.I DEV
+is of type VXLAN.
+
+.TP
+.BI dst " IPADDR"
+the IP address of the destination
+VXLAN tunnel endpoint where the multicast receivers reside.
+
+.TP
+.BI dst_port " DST_PORT"
+the UDP destination port number to use to connect to the remote VXLAN tunnel
+endpoint. If omitted, the value specified at VXLAN device creation will be
+used.
+
+.TP
+.BI vni " VNI"
+the VXLAN VNI Network Identifier to use to connect to the remote VXLAN tunnel
+endpoint. If omitted, the value specified at VXLAN device creation will be used
+or the source VNI when the VXLAN device is in external mode.
+
+.TP
+.BI src_vni " SRC_VNI"
+the source VNI Network Identifier this entry belongs to. Used only when the
+VXLAN device is in external mode. If omitted, the value specified at VXLAN
+device creation will be used.
+
+.TP
+.BI via " DEV"
+device name of the outgoing interface for the VXLAN device to reach the remote
+VXLAN tunnel endpoint.
+
+.in -8
+The 0.0.0.0 and :: MDB entries are special catchall entries used to flood IPv4
+and IPv6 unregistered multicast packets, respectively. Therefore, when these
+entries are programmed, the catchall 00:00:00:00:00:00 FDB entry will only
+flood broadcast, unknown unicast and link-local multicast.
+
+.in -8
.SS bridge mdb delete - delete a multicast group database entry
This command removes an existing mdb entry.
@@ -1154,6 +1213,14 @@ may be either
enable multicast traffic forwarding. This mode is available only for ports.
.sp
+.TP
+.BR "neigh_suppress on " or " neigh_suppress off "
+Controls whether neigh discovery (arp and nd) proxy and suppression is enabled
+for a given VLAN on a given port. By default this flag is off.
+
+Note that this option only takes effect when \fBbridge link\fR option
+\fBneigh_vlan_suppress\fR is enabled for a given port.
+
.SS bridge vlan show - list vlan configuration.
This command displays the current VLAN filter table.
diff --git a/man/man8/ip-address.8.in b/man/man8/ip-address.8.in
index 1846252d..abdd6a20 100644
--- a/man/man8/ip-address.8.in
+++ b/man/man8/ip-address.8.in
@@ -50,7 +50,9 @@ ip-address \- protocol address management
.B vrf
.IR NAME " ] [ "
.BR up " ] ["
-.BR nomaster " ] ]"
+.BR nomaster " ]"
+.B proto
+.IR ADDRPROTO " ] ]"
.ti -8
.BR "ip address" " { " showdump " | " restore " }"
@@ -66,7 +68,9 @@ ip-address \- protocol address management
.B label
.IR LABEL " ] [ "
.B scope
-.IR SCOPE-ID " ]"
+.IR SCOPE-ID " ] [ "
+.B proto
+.IR ADDRPROTO " ]"
.ti -8
.IR SCOPE-ID " := "
@@ -74,6 +78,10 @@ ip-address \- protocol address management
.IR NUMBER " ]"
.ti -8
+.IR ADDRPROTO " := [ "
+.IR NAME " | " NUMBER " ]"
+
+.ti -8
.IR FLAG-LIST " := [ " FLAG-LIST " ] " FLAG
.ti -8
@@ -288,6 +296,36 @@ flag when adding a multicast address enables similar functionality for
Openvswitch VXLAN interfaces as well as other tunneling mechanisms that need to
receive multicast traffic.
+.TP
+.BI proto " ADDRPROTO"
+the protocol identifier of this route.
+.I ADDRPROTO
+may be a number or a string from the file
+.BR "/etc/iproute2/rt_addrprotos" .
+If the protocol ID is not given,
+
+.B ip assumes protocol 0. Several protocol
+values have a fixed interpretation. Namely:
+
+.in +8
+.B kernel_lo
+- The ::1 address that kernel installs on a loopback netdevice has this
+ protocol value
+.sp
+
+.B kernel_ra
+- IPv6 addresses installed in response to router advertisement messages
+.sp
+
+.B kernel_ll
+- Link-local addresses have this protocol value
+.sp
+.in -8
+
+.sp
+The rest of the values are not reserved and the administrator is free
+to assign (or not to assign) protocol tags.
+
.SS ip address delete - delete protocol address
.B Arguments:
coincide with the arguments of
@@ -400,6 +438,13 @@ inverse of
This is an alias for
.BR temporary " or " secondary .
+.TP
+.BI proto " ADDRPROTO"
+Only show addresses with a given protocol, or those for which the kernel
+response did not include protocol. See the corresponding argument to
+.B ip addr add
+for details about address protocols.
+
.SS ip address flush - flush protocol addresses
This command flushes the protocol addresses selected by some criteria.
diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
index e23474b3..bf3605a9 100644
--- a/man/man8/ip-link.8.in
+++ b/man/man8/ip-link.8.in
@@ -1486,6 +1486,7 @@ the following additional arguments are supported:
.BR mode " { " private " | " vepa " | " bridge " | " passthru
.RB " [ " nopromisc " ] | " source " [ " nodst " ] } "
.RB " [ " bcqueuelen " { " LENGTH " } ] "
+.RB " [ " bclim " " LIMIT " ] "
.in +8
.sp
@@ -1544,6 +1545,13 @@ will be the maximum length that any macvlan interface has requested.
When listing device parameters both the bcqueuelen parameter
as well as the actual used bcqueuelen are listed to better help
the user understand the setting.
+
+.BR bclim " " LIMIT
+- Set the threshold for broadcast queueing.
+.BR LIMIT " must be a 32-bit integer."
+Setting this to -1 disables broadcast queueing altogether. Otherwise
+a multicast address will be queued as broadcast if the number of devices
+using it is greater than the given value.
.in -8
.TP
@@ -2509,6 +2517,8 @@ the following additional arguments are supported:
] [
.BR neigh_suppress " { " on " | " off " }"
] [
+.BR neigh_vlan_suppress " { " on " | " off " }"
+] [
.BR vlan_tunnel " { " on " | " off " }"
] [
.BR isolated " { " on " | " off " }"
@@ -2614,6 +2624,12 @@ this port).
- controls whether neigh discovery (arp and nd) proxy and suppression
is enabled on the port. By default this flag is off.
+.BR neigh_vlan_suppress " { " on " | " off " }"
+- controls whether per-VLAN neigh discovery (arp and nd) proxy and suppression
+is enabled on the port. When on, the \fBbridge link\fR option
+\fBneigh_suppress\fR has no effect and the per-VLAN state is set using the
+\fBbridge vlan\fR option \fBneigh_suppress\fR. By default this flag is off.
+
.BR vlan_tunnel " { " on " | " off " }"
- controls whether vlan to tunnel mapping is enabled on the port. By
default this flag is off.
@@ -2711,6 +2727,9 @@ Update the broadcast/multicast queue length.
[
.BI bcqueuelen " LENGTH "
]
+[
+.BI bclim " LIMIT "
+]
.in +8
.BI bcqueuelen " LENGTH "
@@ -2724,6 +2743,13 @@ will be the maximum length that any macvlan interface has requested.
When listing device parameters both the bcqueuelen parameter
as well as the actual used bcqueuelen are listed to better help
the user understand the setting.
+
+.BI bclim " LIMIT "
+- Set the threshold for broadcast queueing.
+.IR LIMIT " must be a 32-bit integer."
+Setting this to -1 disables broadcast queueing altogether. Otherwise
+a multicast address will be queued as broadcast if the number of devices
+using it is greater than the given value.
.in -8
.TP
diff --git a/man/man8/tc-mqprio.8 b/man/man8/tc-mqprio.8
index 3441cb68..724ef906 100644
--- a/man/man8/tc-mqprio.8
+++ b/man/man8/tc-mqprio.8
@@ -30,9 +30,11 @@ dcb|bw_rlimit ]
.B min_rate
min_rate1 min_rate2 ... ] [
.B max_rate
-max_rate1 max_rate2 ...
-.B ]
-
+max_rate1 max_rate2 ... ]
+.ti +8
+[
+.B fp
+FP0 FP1 FP2 ... ]
.SH DESCRIPTION
The MQPRIO qdisc is a simple queuing discipline that allows mapping
@@ -162,6 +164,34 @@ the
argument is set to
.B 'bw_rlimit'.
+.TP
+fp
+Selects whether traffic classes are express (deliver packets via the eMAC) or
+preemptible (deliver packets via the pMAC), according to IEEE 802.1Q-2018
+clause 6.7.2 Frame preemption. Takes the form of an array (one element per
+traffic class) with values being
+.B 'E'
+(for express) or
+.B 'P'
+(for preemptible).
+
+Multiple priorities which map to the same traffic class, as well as multiple
+TXQs which map to the same traffic class, must have the same FP attributes.
+To interpret the FP as an attribute per priority, the
+.B 'map'
+argument can be used for translation. To interpret FP as an attribute per TXQ,
+the
+.B 'queues'
+argument can be used for translation.
+
+Traffic classes are express by default. The argument is supported only with
+.B 'hw'
+set to 1. Preemptible traffic classes are accepted only if the device has a MAC
+Merge layer configurable through
+.BR ethtool(8).
+
+.SH SEE ALSO
+.BR ethtool(8)
.SH EXAMPLE
diff --git a/man/man8/tc-taprio.8 b/man/man8/tc-taprio.8
index c3ccefea..bf489b03 100644
--- a/man/man8/tc-taprio.8
+++ b/man/man8/tc-taprio.8
@@ -36,6 +36,10 @@ clockid
[
.B max-sdu
<queueMaxSDU[TC 0]> <queueMaxSDU[TC 1]> <queueMaxSDU[TC N]> ]
+.ti +8
+[
+.B fp
+<adminStatus[TC 0]> <adminStatus[TC 1]> <adminStatus[TC N]> ]
.SH DESCRIPTION
The TAPRIO qdisc implements a simplified version of the scheduling
@@ -163,6 +167,13 @@ represents the maximum L2 payload size that can egress that traffic class.
Elements that are not filled in default to 0. The value 0 means that the
traffic class can send packets up to the port's maximum MTU in size.
+.TP
+fp
+.br
+Selects whether traffic classes are express or preemptible. See
+.BR tc-mqprio(8)
+for details.
+
.SH EXAMPLES
The following example shows how an traffic schedule with three traffic
diff --git a/man/man8/tc-tunnel_key.8 b/man/man8/tc-tunnel_key.8
index f639f433..b987cd0d 100644
--- a/man/man8/tc-tunnel_key.8
+++ b/man/man8/tc-tunnel_key.8
@@ -131,6 +131,9 @@ If using
.B nocsum
with IPv6, be sure you know what you are doing. Zero UDP checksums provide
weaker protection against corrupted packets. See RFC6935 for details.
+.TP
+.B nofrag
+disallow IP fragmentation.
.RE
.SH EXAMPLES
The following example encapsulates incoming ICMP packets on eth0 into a vxlan
diff --git a/tc/m_tunnel_key.c b/tc/m_tunnel_key.c
index 1b4c8bd6..ff699cc8 100644
--- a/tc/m_tunnel_key.c
+++ b/tc/m_tunnel_key.c
@@ -26,7 +26,8 @@ static void explain(void)
"dst_ip <IP> (mandatory)\n"
"dst_port <UDP_PORT>\n"
"geneve_opts | vxlan_opts | erspan_opts <OPTIONS>\n"
- "csum | nocsum (default is \"csum\")\n");
+ "csum | nocsum (default is \"csum\")\n"
+ "nofrag\n");
}
static void usage(void)
@@ -321,7 +322,7 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
int ret;
int has_src_ip = 0;
int has_dst_ip = 0;
- int csum = 1;
+ int csum = 1, nofrag = 0;
if (matches(*argv, "tunnel_key") != 0)
return -1;
@@ -425,6 +426,8 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
csum = 1;
} else if (matches(*argv, "nocsum") == 0) {
csum = 0;
+ } else if (strcmp(*argv, "nofrag") == 0) {
+ nofrag = 1;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
@@ -435,6 +438,9 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
addattr8(n, MAX_MSG, TCA_TUNNEL_KEY_NO_CSUM, !csum);
+ if (nofrag)
+ addattr(n, MAX_MSG, TCA_TUNNEL_KEY_NO_FRAG);
+
parse_action_control_dflt(&argc, &argv, &parm.action,
false, TC_ACT_PIPE);
@@ -513,15 +519,36 @@ static void tunnel_key_print_dst_port(FILE *f, char *name,
rta_getattr_be16(attr));
}
-static void tunnel_key_print_flag(FILE *f, const char *name_on,
- const char *name_off,
- struct rtattr *attr)
+static const struct {
+ const char *name;
+ unsigned int nl_flag;
+} tunnel_key_flag_names[] = {
+ { "", TCA_TUNNEL_KEY_NO_CSUM }, /* special handling, not bool */
+ { "nofrag", TCA_TUNNEL_KEY_NO_FRAG },
+};
+
+static void tunnel_key_print_flags(struct rtattr *tb[])
{
- if (!attr)
- return;
+ unsigned int i, nl_flag;
+
print_nl();
- print_string(PRINT_ANY, "flag", "\t%s",
- rta_getattr_u8(attr) ? name_on : name_off);
+ for (i = 0; i < ARRAY_SIZE(tunnel_key_flag_names); i++) {
+ nl_flag = tunnel_key_flag_names[i].nl_flag;
+ if (nl_flag == TCA_TUNNEL_KEY_NO_CSUM) {
+ /* special handling to preserve csum/nocsum design */
+ if (!tb[nl_flag])
+ continue;
+ print_string(PRINT_ANY, "flag", "\t%s",
+ rta_getattr_u8(tb[nl_flag]) ?
+ "nocsum" : "csum" );
+ } else {
+ if (tb[nl_flag])
+ print_string(PRINT_FP, NULL, "\t%s",
+ tunnel_key_flag_names[i].name);
+ print_bool(PRINT_JSON, tunnel_key_flag_names[i].name,
+ NULL, !!tb[nl_flag]);
+ }
+ }
}
static void tunnel_key_print_geneve_options(struct rtattr *attr)
@@ -697,8 +724,7 @@ static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg)
tunnel_key_print_dst_port(f, "dst_port",
tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
tunnel_key_print_key_opt(tb[TCA_TUNNEL_KEY_ENC_OPTS]);
- tunnel_key_print_flag(f, "nocsum", "csum",
- tb[TCA_TUNNEL_KEY_NO_CSUM]);
+ tunnel_key_print_flags(tb);
tunnel_key_print_tos_ttl(f, "tos",
tb[TCA_TUNNEL_KEY_ENC_TOS]);
tunnel_key_print_tos_ttl(f, "ttl",
diff --git a/tc/q_mqprio.c b/tc/q_mqprio.c
index 99c43491..7a4417f5 100644
--- a/tc/q_mqprio.c
+++ b/tc/q_mqprio.c
@@ -23,12 +23,29 @@ static void explain(void)
"Usage: ... mqprio [num_tc NUMBER] [map P0 P1 ...]\n"
" [queues count1@offset1 count2@offset2 ...] "
"[hw 1|0]\n"
+ " [fp FP0 FP1 FP2 ...]\n"
" [mode dcb|channel]\n"
" [shaper bw_rlimit SHAPER_PARAMS]\n"
"Where: SHAPER_PARAMS := { min_rate MIN_RATE1 MIN_RATE2 ...|\n"
" max_rate MAX_RATE1 MAX_RATE2 ... }\n");
}
+static void add_tc_entries(struct nlmsghdr *n, __u32 fp[TC_QOPT_MAX_QUEUE],
+ int num_fp_entries)
+{
+ struct rtattr *l;
+ __u32 tc;
+
+ for (tc = 0; tc < num_fp_entries; tc++) {
+ l = addattr_nest(n, 1024, TCA_MQPRIO_TC_ENTRY | NLA_F_NESTED);
+
+ addattr32(n, 1024, TCA_MQPRIO_TC_ENTRY_INDEX, tc);
+ addattr32(n, 1024, TCA_MQPRIO_TC_ENTRY_FP, fp[tc]);
+
+ addattr_nest_end(n, l);
+ }
+}
+
static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
char **argv, struct nlmsghdr *n, const char *dev)
{
@@ -43,7 +60,10 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
__u64 min_rate64[TC_QOPT_MAX_QUEUE] = {0};
__u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0};
__u16 shaper = TC_MQPRIO_SHAPER_DCB;
+ __u32 fp[TC_QOPT_MAX_QUEUE] = { };
__u16 mode = TC_MQPRIO_MODE_DCB;
+ bool have_tc_entries = false;
+ int num_fp_entries = 0;
int cnt_off_pairs = 0;
struct rtattr *tail;
__u32 flags = 0;
@@ -93,6 +113,21 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
idx++;
cnt_off_pairs++;
}
+ } else if (strcmp(*argv, "fp") == 0) {
+ while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
+ NEXT_ARG();
+ if (strcmp(*argv, "E") == 0) {
+ fp[idx] = TC_FP_EXPRESS;
+ } else if (strcmp(*argv, "P") == 0) {
+ fp[idx] = TC_FP_PREEMPTIBLE;
+ } else {
+ PREV_ARG();
+ break;
+ }
+ num_fp_entries++;
+ idx++;
+ }
+ have_tc_entries = true;
} else if (strcmp(*argv, "hw") == 0) {
NEXT_ARG();
if (get_u8(&opt.hw, *argv, 10)) {
@@ -187,6 +222,9 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
addattr_l(n, 1024, TCA_MQPRIO_SHAPER,
&shaper, sizeof(shaper));
+ if (have_tc_entries)
+ add_tc_entries(n, fp, num_fp_entries);
+
if (flags & TC_MQPRIO_F_MIN_RATE) {
struct rtattr *start;
@@ -218,6 +256,64 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
return 0;
}
+static void dump_tc_entry(struct rtattr *rta, __u32 fp[TC_QOPT_MAX_QUEUE],
+ int *max_tc_fp)
+{
+ struct rtattr *tb[TCA_MQPRIO_TC_ENTRY_MAX + 1];
+ __u32 tc, val = 0;
+
+ parse_rtattr_nested(tb, TCA_MQPRIO_TC_ENTRY_MAX, rta);
+
+ if (!tb[TCA_MQPRIO_TC_ENTRY_INDEX]) {
+ fprintf(stderr, "Missing tc entry index\n");
+ return;
+ }
+
+ tc = rta_getattr_u32(tb[TCA_MQPRIO_TC_ENTRY_INDEX]);
+ /* Prevent array out of bounds access */
+ if (tc >= TC_QOPT_MAX_QUEUE) {
+ fprintf(stderr, "Unexpected tc entry index %d\n", tc);
+ return;
+ }
+
+ if (tb[TCA_MQPRIO_TC_ENTRY_FP]) {
+ val = rta_getattr_u32(tb[TCA_MQPRIO_TC_ENTRY_FP]);
+ fp[tc] = val;
+
+ if (*max_tc_fp < (int)tc)
+ *max_tc_fp = tc;
+ }
+}
+
+static void dump_tc_entries(FILE *f, struct rtattr *opt, int len)
+{
+ __u32 fp[TC_QOPT_MAX_QUEUE] = {};
+ int max_tc_fp = -1;
+ struct rtattr *rta;
+ int tc;
+
+ for (rta = opt; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+ if (rta->rta_type != (TCA_MQPRIO_TC_ENTRY | NLA_F_NESTED))
+ continue;
+
+ dump_tc_entry(rta, fp, &max_tc_fp);
+ }
+
+ if (max_tc_fp >= 0) {
+ open_json_array(PRINT_ANY,
+ is_json_context() ? "fp" : "\n fp:");
+ for (tc = 0; tc <= max_tc_fp; tc++) {
+ print_string(PRINT_ANY, NULL, " %s",
+ fp[tc] == TC_FP_PREEMPTIBLE ? "P" :
+ fp[tc] == TC_FP_EXPRESS ? "E" :
+ "?");
+ }
+ close_json_array(PRINT_ANY, "");
+
+ print_nl();
+ }
+}
+
static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
{
int i;
@@ -309,7 +405,10 @@ static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
tc_print_rate(PRINT_ANY, NULL, "%s ", max_rate64[i]);
close_json_array(PRINT_ANY, "");
}
+
+ dump_tc_entries(f, RTA_DATA(opt) + RTA_ALIGN(sizeof(*qopt)), len);
}
+
return 0;
}
diff --git a/tc/q_taprio.c b/tc/q_taprio.c
index c0da65fe..bc29710c 100644
--- a/tc/q_taprio.c
+++ b/tc/q_taprio.c
@@ -49,6 +49,7 @@ static void explain(void)
" [queues COUNT@OFFSET COUNT@OFFSET COUNT@OFFSET ...]\n"
" [ [sched-entry index cmd gate-mask interval] ... ]\n"
" [base-time time] [txtime-delay delay]\n"
+ " [fp FP0 FP1 FP2 ...]\n"
"\n"
"CLOCKID must be a valid SYS-V id (i.e. CLOCK_TAI)\n");
}
@@ -148,17 +149,29 @@ static struct sched_entry *create_entry(uint32_t gatemask, uint32_t interval, ui
}
static void add_tc_entries(struct nlmsghdr *n, __u32 max_sdu[TC_QOPT_MAX_QUEUE],
- int num_max_sdu_entries)
+ int num_max_sdu_entries, __u32 fp[TC_QOPT_MAX_QUEUE],
+ int num_fp_entries)
{
struct rtattr *l;
+ int num_tc;
__u32 tc;
- for (tc = 0; tc < num_max_sdu_entries; tc++) {
+ num_tc = max(num_max_sdu_entries, num_fp_entries);
+
+ for (tc = 0; tc < num_tc; tc++) {
l = addattr_nest(n, 1024, TCA_TAPRIO_ATTR_TC_ENTRY | NLA_F_NESTED);
addattr_l(n, 1024, TCA_TAPRIO_TC_ENTRY_INDEX, &tc, sizeof(tc));
- addattr_l(n, 1024, TCA_TAPRIO_TC_ENTRY_MAX_SDU,
- &max_sdu[tc], sizeof(max_sdu[tc]));
+
+ if (tc < num_max_sdu_entries) {
+ addattr_l(n, 1024, TCA_TAPRIO_TC_ENTRY_MAX_SDU,
+ &max_sdu[tc], sizeof(max_sdu[tc]));
+ }
+
+ if (tc < num_fp_entries) {
+ addattr_l(n, 1024, TCA_TAPRIO_TC_ENTRY_FP, &fp[tc],
+ sizeof(fp[tc]));
+ }
addattr_nest_end(n, l);
}
@@ -168,6 +181,7 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
char **argv, struct nlmsghdr *n, const char *dev)
{
__u32 max_sdu[TC_QOPT_MAX_QUEUE] = { };
+ __u32 fp[TC_QOPT_MAX_QUEUE] = { };
__s32 clockid = CLOCKID_INVALID;
struct tc_mqprio_qopt opt = { };
__s64 cycle_time_extension = 0;
@@ -175,6 +189,7 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
bool have_tc_entries = false;
int num_max_sdu_entries = 0;
struct rtattr *tail, *l;
+ int num_fp_entries = 0;
__u32 taprio_flags = 0;
__u32 txtime_delay = 0;
__s64 cycle_time = 0;
@@ -227,6 +242,23 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
free(tmp);
idx++;
}
+ } else if (strcmp(*argv, "fp") == 0) {
+ while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
+ NEXT_ARG();
+ if (strcmp(*argv, "E") == 0) {
+ fp[idx] = TC_FP_EXPRESS;
+ } else if (strcmp(*argv, "P") == 0) {
+ fp[idx] = TC_FP_PREEMPTIBLE;
+ } else {
+ fprintf(stderr,
+ "Illegal \"fp\" value \"%s\", expected \"E\" or \"P\"\n",
+ *argv);
+ return -1;
+ }
+ num_fp_entries++;
+ idx++;
+ }
+ have_tc_entries = true;
} else if (strcmp(*argv, "max-sdu") == 0) {
while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
NEXT_ARG();
@@ -369,7 +401,7 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
&cycle_time_extension, sizeof(cycle_time_extension));
if (have_tc_entries)
- add_tc_entries(n, max_sdu, num_max_sdu_entries);
+ add_tc_entries(n, max_sdu, num_max_sdu_entries, fp, num_fp_entries);
l = addattr_nest(n, 1024, TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST | NLA_F_NESTED);
@@ -460,9 +492,10 @@ static int print_schedule(FILE *f, struct rtattr **tb)
return 0;
}
-static void dump_tc_entry(__u32 max_sdu[TC_QOPT_MAX_QUEUE],
- struct rtattr *item, bool *have_tc_entries,
- int *max_tc_index)
+static void dump_tc_entry(struct rtattr *item,
+ __u32 max_sdu[TC_QOPT_MAX_QUEUE],
+ __u32 fp[TC_QOPT_MAX_QUEUE],
+ int *max_tc_max_sdu, int *max_tc_fp)
{
struct rtattr *tb[TCA_TAPRIO_TC_ENTRY_MAX + 1];
__u32 tc, val = 0;
@@ -481,23 +514,30 @@ static void dump_tc_entry(__u32 max_sdu[TC_QOPT_MAX_QUEUE],
return;
}
- if (*max_tc_index < tc)
- *max_tc_index = tc;
-
- if (tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU])
+ if (tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]) {
val = rta_getattr_u32(tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]);
+ max_sdu[tc] = val;
+ if (*max_tc_max_sdu < (int)tc)
+ *max_tc_max_sdu = tc;
+ }
- max_sdu[tc] = val;
+ if (tb[TCA_TAPRIO_TC_ENTRY_FP]) {
+ val = rta_getattr_u32(tb[TCA_TAPRIO_TC_ENTRY_FP]);
+ fp[tc] = val;
- *have_tc_entries = true;
+ if (*max_tc_fp < (int)tc)
+ *max_tc_fp = tc;
+ }
}
static void dump_tc_entries(FILE *f, struct rtattr *opt)
{
__u32 max_sdu[TC_QOPT_MAX_QUEUE] = {};
- int tc, rem, max_tc_index = 0;
- bool have_tc_entries = false;
+ __u32 fp[TC_QOPT_MAX_QUEUE] = {};
+ int max_tc_max_sdu = -1;
+ int max_tc_fp = -1;
struct rtattr *i;
+ int tc, rem;
rem = RTA_PAYLOAD(opt);
@@ -505,18 +545,30 @@ static void dump_tc_entries(FILE *f, struct rtattr *opt)
if (i->rta_type != (TCA_TAPRIO_ATTR_TC_ENTRY | NLA_F_NESTED))
continue;
- dump_tc_entry(max_sdu, i, &have_tc_entries, &max_tc_index);
+ dump_tc_entry(i, max_sdu, fp, &max_tc_max_sdu, &max_tc_fp);
}
- if (!have_tc_entries)
- return;
+ if (max_tc_max_sdu >= 0) {
+ open_json_array(PRINT_ANY, "max-sdu");
+ for (tc = 0; tc <= max_tc_max_sdu; tc++)
+ print_uint(PRINT_ANY, NULL, " %u", max_sdu[tc]);
+ close_json_array(PRINT_ANY, "");
- open_json_array(PRINT_ANY, "max-sdu");
- for (tc = 0; tc <= max_tc_index; tc++)
- print_uint(PRINT_ANY, NULL, " %u", max_sdu[tc]);
- close_json_array(PRINT_ANY, "");
+ print_nl();
+ }
- print_nl();
+ if (max_tc_fp >= 0) {
+ open_json_array(PRINT_ANY, "fp");
+ for (tc = 0; tc <= max_tc_fp; tc++) {
+ print_string(PRINT_ANY, NULL, " %s",
+ fp[tc] == TC_FP_PREEMPTIBLE ? "P" :
+ fp[tc] == TC_FP_EXPRESS ? "E" :
+ "?");
+ }
+ close_json_array(PRINT_ANY, "");
+
+ print_nl();
+ }
}
static int taprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)