diff options
Diffstat (limited to '')
111 files changed, 1324 insertions, 450 deletions
@@ -42,6 +42,7 @@ DEFINES+=-DCONF_USR_DIR=\"$(CONF_USR_DIR)\" \ -DCONF_ETC_DIR=\"$(CONF_ETC_DIR)\" \ -DNETNS_RUN_DIR=\"$(NETNS_RUN_DIR)\" \ -DNETNS_ETC_DIR=\"$(NETNS_ETC_DIR)\" \ + -DARPDDIR=\"$(ARPDDIR)\" \ -DCONF_COLOR=$(CONF_COLOR) #options for AX.25 @@ -104,7 +105,6 @@ config.mk: install: all install -m 0755 -d $(DESTDIR)$(SBINDIR) install -m 0755 -d $(DESTDIR)$(CONF_USR_DIR) - install -m 0755 -d $(DESTDIR)$(ARPDDIR) install -m 0755 -d $(DESTDIR)$(HDRDIR) @for i in $(SUBDIRS); do $(MAKE) -C $$i install; done install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONF_USR_DIR) @@ -12,6 +12,19 @@ Stable version repository: Development repository: git://git.kernel.org/pub/scm/network/iproute2/iproute2-next.git +Compatibility +------------- +A new release of iproute2 is done with each kernel version, but +there is a wide range of compatibility. Newer versions of iproute2 +will still work with older kernels, but there are some limitations. + +If an iproute2 command with a new feature is used with an older +kernel, the kernel may report an error or silently ignore the new +attribute. Likewise if older iproute2 is used with an newer kernel, +it is not possible to use or see new features. The range of +compatibility extends back as far as the oldest supported Long Term +Support (LTS) kernel version. + How to compile this. -------------------- 1. libdbm diff --git a/bridge/bridge.c b/bridge/bridge.c index f480509..ef59281 100644 --- a/bridge/bridge.c +++ b/bridge/bridge.c @@ -39,7 +39,7 @@ static void usage(void) "where OBJECT := { link | fdb | mdb | vlan | vni | monitor }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] |\n" " -o[neline] | -t[imestamp] | -n[etns] name |\n" -" -c[ompressvlans] -color -p[retty] -j[son] }\n"); +" -com[pressvlans] -c[olor] -p[retty] -j[son] }\n"); exit(-1); } diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 0a2380a..109ad86 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -42,6 +42,7 @@ #define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ #define BPF_JSLT 0xc0 /* SLT is signed, '<' */ #define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ +#define BPF_JCOND 0xe0 /* conditional pseudo jumps: may_goto, goto_or_nop */ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ @@ -50,6 +51,10 @@ #define BPF_XCHG (0xe0 | BPF_FETCH) /* atomic exchange */ #define BPF_CMPXCHG (0xf0 | BPF_FETCH) /* atomic compare-and-write */ +enum bpf_cond_pseudo_jmp { + BPF_MAY_GOTO = 0, +}; + /* Register numbers */ enum { BPF_REG_0 = 0, @@ -77,12 +82,29 @@ struct bpf_insn { __s32 imm; /* signed immediate constant */ }; -/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ +/* Deprecated: use struct bpf_lpm_trie_key_u8 (when the "data" member is needed for + * byte access) or struct bpf_lpm_trie_key_hdr (when using an alternative type for + * the trailing flexible array member) instead. + */ struct bpf_lpm_trie_key { __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ __u8 data[0]; /* Arbitrary size */ }; +/* Header for bpf_lpm_trie_key structs */ +struct bpf_lpm_trie_key_hdr { + __u32 prefixlen; +}; + +/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry, with trailing byte array. */ +struct bpf_lpm_trie_key_u8 { + union { + struct bpf_lpm_trie_key_hdr hdr; + __u32 prefixlen; + }; + __u8 data[]; /* Arbitrary size */ +}; + struct bpf_cgroup_storage_key { __u64 cgroup_inode_id; /* cgroup inode id */ __u32 attach_type; /* program attach type (enum bpf_attach_type) */ @@ -617,7 +639,11 @@ union bpf_iter_link_info { * to NULL to begin the batched operation. After each subsequent * **BPF_MAP_LOOKUP_BATCH**, the caller should pass the resultant * *out_batch* as the *in_batch* for the next operation to - * continue iteration from the current point. + * continue iteration from the current point. Both *in_batch* and + * *out_batch* must point to memory large enough to hold a key, + * except for maps of type **BPF_MAP_TYPE_{HASH, PERCPU_HASH, + * LRU_HASH, LRU_PERCPU_HASH}**, for which batch parameters + * must be at least 4 bytes wide regardless of key size. * * The *keys* and *values* are output parameters which must point * to memory large enough to hold *count* items based on the key @@ -847,6 +873,36 @@ union bpf_iter_link_info { * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * + * BPF_TOKEN_CREATE + * Description + * Create BPF token with embedded information about what + * BPF-related functionality it allows: + * - a set of allowed bpf() syscall commands; + * - a set of allowed BPF map types to be created with + * BPF_MAP_CREATE command, if BPF_MAP_CREATE itself is allowed; + * - a set of allowed BPF program types and BPF program attach + * types to be loaded with BPF_PROG_LOAD command, if + * BPF_PROG_LOAD itself is allowed. + * + * BPF token is created (derived) from an instance of BPF FS, + * assuming it has necessary delegation mount options specified. + * This BPF token can be passed as an extra parameter to various + * bpf() syscall commands to grant BPF subsystem functionality to + * unprivileged processes. + * + * When created, BPF token is "associated" with the owning + * user namespace of BPF FS instance (super block) that it was + * derived from, and subsequent BPF operations performed with + * BPF token would be performing capabilities checks (i.e., + * CAP_BPF, CAP_PERFMON, CAP_NET_ADMIN, CAP_SYS_ADMIN) within + * that user namespace. Without BPF token, such capabilities + * have to be granted in init user namespace, making bpf() + * syscall incompatible with user namespace, for the most part. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * * NOTES * eBPF objects (maps and programs) can be shared between processes. * @@ -901,6 +957,8 @@ enum bpf_cmd { BPF_ITER_CREATE, BPF_LINK_DETACH, BPF_PROG_BIND_MAP, + BPF_TOKEN_CREATE, + __MAX_BPF_CMD, }; enum bpf_map_type { @@ -951,6 +1009,8 @@ enum bpf_map_type { BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_USER_RINGBUF, BPF_MAP_TYPE_CGRP_STORAGE, + BPF_MAP_TYPE_ARENA, + __MAX_BPF_MAP_TYPE }; /* Note that tracing related programs such as @@ -995,6 +1055,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ BPF_PROG_TYPE_NETFILTER, + __MAX_BPF_PROG_TYPE }; enum bpf_attach_type { @@ -1278,6 +1339,10 @@ enum { */ #define BPF_PSEUDO_KFUNC_CALL 2 +enum bpf_addr_space_cast { + BPF_ADDR_SPACE_CAST = 1, +}; + /* flags for BPF_MAP_UPDATE_ELEM command */ enum { BPF_ANY = 0, /* create new element or update existing */ @@ -1330,6 +1395,18 @@ enum { /* Get path from provided FD in BPF_OBJ_PIN/BPF_OBJ_GET commands */ BPF_F_PATH_FD = (1U << 14), + +/* Flag for value_type_btf_obj_fd, the fd is available */ + BPF_F_VTYPE_BTF_OBJ_FD = (1U << 15), + +/* BPF token FD is passed in a corresponding command's token_fd field */ + BPF_F_TOKEN_FD = (1U << 16), + +/* When user space page faults in bpf_arena send SIGSEGV instead of inserting new page */ + BPF_F_SEGV_ON_FAULT = (1U << 17), + +/* Do not translate kernel bpf_arena pointers to user pointers */ + BPF_F_NO_USER_CONV = (1U << 18), }; /* Flags for BPF_PROG_QUERY. */ @@ -1401,8 +1478,20 @@ union bpf_attr { * BPF_MAP_TYPE_BLOOM_FILTER - the lowest 4 bits indicate the * number of hash functions (if 0, the bloom filter will default * to using 5 hash functions). + * + * BPF_MAP_TYPE_ARENA - contains the address where user space + * is going to mmap() the arena. It has to be page aligned. */ __u64 map_extra; + + __s32 value_type_btf_obj_fd; /* fd pointing to a BTF + * type data for + * btf_vmlinux_value_type_id. + */ + /* BPF token FD to use with BPF_MAP_CREATE operation. + * If provided, map_flags should have BPF_F_TOKEN_FD flag set. + */ + __s32 map_token_fd; }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -1472,6 +1561,10 @@ union bpf_attr { * truncated), or smaller (if log buffer wasn't filled completely). */ __u32 log_true_size; + /* BPF token FD to use with BPF_PROG_LOAD operation. + * If provided, prog_flags should have BPF_F_TOKEN_FD flag set. + */ + __s32 prog_token_fd; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ @@ -1584,6 +1677,11 @@ union bpf_attr { * truncated), or smaller (if log buffer wasn't filled completely). */ __u32 btf_log_true_size; + __u32 btf_flags; + /* BPF token FD to use with BPF_BTF_LOAD operation. + * If provided, btf_flags should have BPF_F_TOKEN_FD flag set. + */ + __s32 btf_token_fd; }; struct { @@ -1714,6 +1812,11 @@ union bpf_attr { __u32 flags; /* extra flags */ } prog_bind_map; + struct { /* struct used by BPF_TOKEN_CREATE command */ + __u32 flags; + __u32 bpffs_fd; + } token_create; + } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF @@ -4839,9 +4942,9 @@ union bpf_attr { * going through the CPU's backlog queue. * * The *flags* argument is reserved and must be 0. The helper is - * currently only supported for tc BPF program types at the ingress - * hook and for veth device types. The peer device must reside in a - * different network namespace. + * currently only supported for tc BPF program types at the + * ingress hook and for veth and netkit target device types. The + * peer device must reside in a different network namespace. * Return * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. @@ -6487,7 +6590,7 @@ struct bpf_map_info { __u32 btf_id; __u32 btf_key_type_id; __u32 btf_value_type_id; - __u32 :32; /* alignment pad */ + __u32 btf_vmlinux_id; __u64 map_extra; } __attribute__((aligned(8))); @@ -6563,6 +6666,7 @@ struct bpf_link_info { __u32 count; /* in/out: kprobe_multi function count */ __u32 flags; __u64 missed; + __aligned_u64 cookies; } kprobe_multi; struct { __aligned_u64 path; @@ -6582,6 +6686,7 @@ struct bpf_link_info { __aligned_u64 file_name; /* in/out */ __u32 name_len; __u32 offset; /* offset from file_name */ + __u64 cookie; } uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */ struct { __aligned_u64 func_name; /* in/out */ @@ -6589,14 +6694,19 @@ struct bpf_link_info { __u32 offset; /* offset from func_name */ __u64 addr; __u64 missed; + __u64 cookie; } kprobe; /* BPF_PERF_EVENT_KPROBE, BPF_PERF_EVENT_KRETPROBE */ struct { __aligned_u64 tp_name; /* in/out */ __u32 name_len; + __u32 :32; + __u64 cookie; } tracepoint; /* BPF_PERF_EVENT_TRACEPOINT */ struct { __u64 config; __u32 type; + __u32 :32; + __u64 cookie; } event; /* BPF_PERF_EVENT_EVENT */ }; } perf_event; diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h index 23d5bf4..229a77f 100644 --- a/include/uapi/linux/can.h +++ b/include/uapi/linux/can.h @@ -193,9 +193,14 @@ struct canfd_frame { #define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */ #define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */ +/* the 8-bit VCID is optionally placed in the canxl_frame.prio element */ +#define CANXL_VCID_OFFSET 16 /* bit offset of VCID in prio element */ +#define CANXL_VCID_VAL_MASK 0xFFUL /* VCID is an 8-bit value */ +#define CANXL_VCID_MASK (CANXL_VCID_VAL_MASK << CANXL_VCID_OFFSET) + /** * struct canxl_frame - CAN with e'X'tended frame 'L'ength frame structure - * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags + * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags / VCID * @flags: additional flags for CAN XL * @sdt: SDU (service data unit) type * @len: frame payload length in byte (CANXL_MIN_DLEN .. CANXL_MAX_DLEN) @@ -205,7 +210,7 @@ struct canfd_frame { * @prio shares the same position as @can_id from struct can[fd]_frame. */ struct canxl_frame { - canid_t prio; /* 11 bit priority for arbitration (canid_t) */ + canid_t prio; /* 11 bit priority for arbitration / 8 bit VCID */ __u8 flags; /* additional flags for CAN XL */ __u8 sdt; /* SDU (service data unit) type */ __u16 len; /* frame payload length in byte */ diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index e771701..aaac243 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -614,7 +614,10 @@ enum devlink_attr { DEVLINK_ATTR_REGION_DIRECT, /* flag */ - /* add new attributes above here, update the policy in devlink.c */ + /* Add new attributes above here, update the spec in + * Documentation/netlink/specs/devlink.yaml and re-generate + * net/devlink/netlink_gen.c. + */ __DEVLINK_ATTR_MAX, DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1 diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index d17271f..ff4ceea 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -1503,6 +1503,7 @@ enum { IFLA_BOND_AD_LACP_ACTIVE, IFLA_BOND_MISSED_MAX, IFLA_BOND_NS_IP6_TARGET, + IFLA_BOND_COUPLED_CONTROL, __IFLA_BOND_MAX, }; diff --git a/include/uapi/linux/ioam6_genl.h b/include/uapi/linux/ioam6_genl.h index 6043d9f..3f89b53 100644 --- a/include/uapi/linux/ioam6_genl.h +++ b/include/uapi/linux/ioam6_genl.h @@ -49,4 +49,24 @@ enum { #define IOAM6_CMD_MAX (__IOAM6_CMD_MAX - 1) +#define IOAM6_GENL_EV_GRP_NAME "ioam6_events" + +enum ioam6_event_type { + IOAM6_EVENT_UNSPEC, + IOAM6_EVENT_TRACE, +}; + +enum ioam6_event_attr { + IOAM6_EVENT_ATTR_UNSPEC, + + IOAM6_EVENT_ATTR_TRACE_NAMESPACE, /* u16 */ + IOAM6_EVENT_ATTR_TRACE_NODELEN, /* u8 */ + IOAM6_EVENT_ATTR_TRACE_TYPE, /* u32 */ + IOAM6_EVENT_ATTR_TRACE_DATA, /* Binary */ + + __IOAM6_EVENT_ATTR_MAX +}; + +#define IOAM6_EVENT_ATTR_MAX (__IOAM6_EVENT_ATTR_MAX - 1) + #endif /* _LINUX_IOAM6_GENL_H */ diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 6325d1d..1b40a96 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -101,5 +101,6 @@ #define DMA_BUF_MAGIC 0x444d4142 /* "DMAB" */ #define DEVMEM_MAGIC 0x454d444d /* "DMEM" */ #define SECRETMEM_MAGIC 0x5345434d /* "SECM" */ +#define PID_FS_MAGIC 0x50494446 /* "PIDF" */ #endif /* __LINUX_MAGIC_H__ */ diff --git a/include/uapi/linux/nexthop.h b/include/uapi/linux/nexthop.h index 37b14b4..7e340be 100644 --- a/include/uapi/linux/nexthop.h +++ b/include/uapi/linux/nexthop.h @@ -30,6 +30,9 @@ enum { #define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1) +#define NHA_OP_FLAG_DUMP_STATS BIT(0) +#define NHA_OP_FLAG_DUMP_HW_STATS BIT(1) + enum { NHA_UNSPEC, NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */ @@ -60,6 +63,18 @@ enum { /* nested; nexthop bucket attributes */ NHA_RES_BUCKET, + /* u32; operation-specific flags */ + NHA_OP_FLAGS, + + /* nested; nexthop group stats */ + NHA_GROUP_STATS, + + /* u32; nexthop hardware stats enable */ + NHA_HW_STATS_ENABLE, + + /* u32; read-only; whether any driver collects HW stats */ + NHA_HW_STATS_USED, + __NHA_MAX, }; @@ -101,4 +116,34 @@ enum { #define NHA_RES_BUCKET_MAX (__NHA_RES_BUCKET_MAX - 1) +enum { + NHA_GROUP_STATS_UNSPEC, + + /* nested; nexthop group entry stats */ + NHA_GROUP_STATS_ENTRY, + + __NHA_GROUP_STATS_MAX, +}; + +#define NHA_GROUP_STATS_MAX (__NHA_GROUP_STATS_MAX - 1) + +enum { + NHA_GROUP_STATS_ENTRY_UNSPEC, + + /* u32; nexthop id of the nexthop group entry */ + NHA_GROUP_STATS_ENTRY_ID, + + /* uint; number of packets forwarded via the nexthop group entry */ + NHA_GROUP_STATS_ENTRY_PACKETS, + + /* uint; number of packets forwarded via the nexthop group entry in + * hardware + */ + NHA_GROUP_STATS_ENTRY_PACKETS_HW, + + __NHA_GROUP_STATS_ENTRY_MAX, +}; + +#define NHA_GROUP_STATS_ENTRY_MAX (__NHA_GROUP_STATS_ENTRY_MAX - 1) + #endif diff --git a/include/uapi/linux/tc_act/tc_pedit.h b/include/uapi/linux/tc_act/tc_pedit.h index f3e61b0..f5cab7f 100644 --- a/include/uapi/linux/tc_act/tc_pedit.h +++ b/include/uapi/linux/tc_act/tc_pedit.h @@ -62,7 +62,7 @@ struct tc_pedit_sel { tc_gen; unsigned char nkeys; unsigned char flags; - struct tc_pedit_key keys[0]; + struct tc_pedit_key keys[] __counted_by(nkeys); }; #define tc_pedit tc_pedit_sel diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 43efaec..cc480c3 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -228,7 +228,7 @@ enum { #define XFRM_NR_MSGTYPES (XFRM_MSG_MAX + 1 - XFRM_MSG_BASE) /* - * Generic LSM security context for comunicating to user space + * Generic LSM security context for communicating to user space * NOTE: Same format as sadb_x_sec_ctx */ struct xfrm_user_sec_ctx { diff --git a/include/utils.h b/include/utils.h index 9ba129b..a2a98b9 100644 --- a/include/utils.h +++ b/include/utils.h @@ -393,4 +393,6 @@ int proto_a2n(unsigned short *id, const char *buf, const char *proto_n2a(unsigned short id, char *buf, int len, const struct proto *proto_tb, size_t tb_len); +FILE *generic_proc_open(const char *env, const char *name); + #endif /* __UTILS_H__ */ diff --git a/include/version.h b/include/version.h index ae9eec9..3252e80 100644 --- a/include/version.h +++ b/include/version.h @@ -1 +1 @@ -static const char version[] = "6.8.0"; +static const char version[] = "6.9.0"; @@ -203,15 +203,15 @@ int main(int argc, char **argv) argc--; argv++; if (argc <= 1) - usage(); + missarg("loop count"); max_flush_loops = atoi(argv[1]); } else if (matches(opt, "-family") == 0) { argc--; argv++; if (argc <= 1) - usage(); + missarg("family type"); if (strcmp(argv[1], "help") == 0) - usage(); + do_help(argc, argv); else preferred_family = read_family(argv[1]); if (preferred_family == AF_UNSPEC) @@ -258,7 +258,7 @@ int main(int argc, char **argv) argc--; argv++; if (argc <= 1) - usage(); + missarg("batch file"); batch_file = argv[1]; } else if (matches(opt, "-brief") == 0) { ++brief; @@ -272,7 +272,7 @@ int main(int argc, char **argv) argc--; argv++; if (argc <= 1) - usage(); + missarg("rcvbuf size"); if (get_unsigned(&size, argv[1], 0)) { fprintf(stderr, "Invalid rcvbuf size '%s'\n", argv[1]); @@ -301,7 +301,9 @@ int do_ipila(int argc, char **argv) return do_add(argc-1, argv+1); if (matches(*argv, "delete") == 0) return do_del(argc-1, argv+1); - if (matches(*argv, "list") == 0) + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) return do_list(argc-1, argv+1); fprintf(stderr, "Command \"%s\" is unknown, try \"ip ila help\".\n", diff --git a/ip/ipioam6.c b/ip/ipioam6.c index b63d7d5..1886098 100644 --- a/ip/ipioam6.c +++ b/ip/ipioam6.c @@ -13,6 +13,7 @@ #include <inttypes.h> #include <linux/genetlink.h> +#include <linux/ioam6.h> #include <linux/ioam6_genl.h> #include "utils.h" @@ -30,7 +31,8 @@ static void usage(void) " ip ioam schema show\n" " ip ioam schema add ID DATA\n" " ip ioam schema del ID\n" - " ip ioam namespace set ID schema { ID | none }\n"); + " ip ioam namespace set ID schema { ID | none }\n" + " ip ioam monitor\n"); exit(-1); } @@ -42,6 +44,7 @@ static int genl_family = -1; IOAM6_GENL_VERSION, _cmd, _flags) static struct { + bool monitor; unsigned int cmd; __u32 sc_id; __u32 ns_data; @@ -96,6 +99,37 @@ static void print_schema(struct rtattr *attrs[]) print_nl(); } +static void print_trace(struct rtattr *attrs[]) +{ + __u8 data[IOAM6_TRACE_DATA_SIZE_MAX]; + int len, i = 0; + + printf("[TRACE] "); + + if (attrs[IOAM6_EVENT_ATTR_TRACE_NAMESPACE]) + printf("Namespace=%u ", + rta_getattr_u16(attrs[IOAM6_EVENT_ATTR_TRACE_NAMESPACE])); + + if (attrs[IOAM6_EVENT_ATTR_TRACE_NODELEN]) + printf("NodeLen=%u ", + rta_getattr_u8(attrs[IOAM6_EVENT_ATTR_TRACE_NODELEN])); + + if (attrs[IOAM6_EVENT_ATTR_TRACE_TYPE]) + printf("Type=%#08x ", + rta_getattr_u32(attrs[IOAM6_EVENT_ATTR_TRACE_TYPE])); + + len = RTA_PAYLOAD(attrs[IOAM6_EVENT_ATTR_TRACE_DATA]); + memcpy(data, RTA_DATA(attrs[IOAM6_EVENT_ATTR_TRACE_DATA]), len); + + printf("Data="); + while (i < len) { + printf("%02x", data[i]); + i++; + } + + printf("\n"); +} + static int process_msg(struct nlmsghdr *n, void *arg) { struct rtattr *attrs[IOAM6_ATTR_MAX + 1]; @@ -126,6 +160,32 @@ static int process_msg(struct nlmsghdr *n, void *arg) return 0; } +static int ioam6_monitor_msg(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, + void *arg) +{ + struct rtattr *attrs[IOAM6_EVENT_ATTR_MAX + 1]; + const struct genlmsghdr *ghdr = NLMSG_DATA(n); + int len = n->nlmsg_len; + + if (n->nlmsg_type != genl_family) + return -1; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + parse_rtattr(attrs, IOAM6_EVENT_ATTR_MAX, + (void *)ghdr + GENL_HDRLEN, len); + + switch (ghdr->cmd) { + case IOAM6_EVENT_TRACE: + print_trace(attrs); + break; + } + + return 0; +} + static int ioam6_do_cmd(void) { IOAM6_REQUEST(req, 1056, opts.cmd, NLM_F_REQUEST); @@ -134,6 +194,19 @@ static int ioam6_do_cmd(void) if (genl_init_handle(&grth, IOAM6_GENL_NAME, &genl_family)) exit(1); + if (opts.monitor) { + if (genl_add_mcast_grp(&grth, genl_family, + IOAM6_GENL_EV_GRP_NAME) < 0) { + perror("can't subscribe to ioam6 events"); + exit(1); + } + + if (rtnl_listen(&grth, ioam6_monitor_msg, stdout) < 0) + exit(1); + + return 0; + } + req.n.nlmsg_type = genl_family; switch (opts.cmd) { @@ -325,6 +398,9 @@ int do_ioam6(int argc, char **argv) invarg("Unknown", *argv); } + } else if (strcmp(*argv, "monitor") == 0) { + opts.monitor = true; + } else { invarg("Unknown", *argv); } diff --git a/ip/iplink.c b/ip/iplink.c index 95314af..d5cb9a0 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -64,7 +64,7 @@ void iplink_usage(void) "\n" " ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n" "\n" - " ip link set { DEVICE | dev DEVICE | group DEVGROUP }\n" + " ip link { set | change } { DEVICE | dev DEVICE | group DEVGROUP }\n" " [ { up | down } ]\n" " [ type TYPE ARGS ]\n"); diff --git a/ip/iplink_bond.c b/ip/iplink_bond.c index 214244d..19af67d 100644 --- a/ip/iplink_bond.c +++ b/ip/iplink_bond.c @@ -148,6 +148,7 @@ static void print_explain(FILE *f) " [ tlb_dynamic_lb TLB_DYNAMIC_LB ]\n" " [ lacp_rate LACP_RATE ]\n" " [ lacp_active LACP_ACTIVE]\n" + " [ coupled_control COUPLED_CONTROL ]\n" " [ ad_select AD_SELECT ]\n" " [ ad_user_port_key PORTKEY ]\n" " [ ad_actor_sys_prio SYSPRIO ]\n" @@ -163,6 +164,7 @@ static void print_explain(FILE *f) "LACP_ACTIVE := off|on\n" "LACP_RATE := slow|fast\n" "AD_SELECT := stable|bandwidth|count\n" + "COUPLED_CONTROL := off|on\n" ); } @@ -176,13 +178,14 @@ static int bond_parse_opt(struct link_util *lu, int argc, char **argv, { __u8 mode, use_carrier, primary_reselect, fail_over_mac; __u8 xmit_hash_policy, num_peer_notif, all_slaves_active; - __u8 lacp_active, lacp_rate, ad_select, tlb_dynamic_lb; + __u8 lacp_active, lacp_rate, ad_select, tlb_dynamic_lb, coupled_control; __u16 ad_user_port_key, ad_actor_sys_prio; __u32 miimon, updelay, downdelay, peer_notify_delay, arp_interval, arp_validate; __u32 arp_all_targets, resend_igmp, min_links, lp_interval; __u32 packets_per_slave; __u8 missed_max; unsigned int ifindex; + int ret; while (argc > 0) { if (matches(*argv, "mode") == 0) { @@ -367,6 +370,12 @@ static int bond_parse_opt(struct link_util *lu, int argc, char **argv, lacp_active = get_index(lacp_active_tbl, *argv); addattr8(n, 1024, IFLA_BOND_AD_LACP_ACTIVE, lacp_active); + } else if (strcmp(*argv, "coupled_control") == 0) { + NEXT_ARG(); + coupled_control = parse_on_off("coupled_control", *argv, &ret); + if (ret) + return ret; + addattr8(n, 1024, IFLA_BOND_COUPLED_CONTROL, coupled_control); } else if (matches(*argv, "ad_select") == 0) { NEXT_ARG(); if (get_index(ad_select_tbl, *argv) < 0) @@ -659,6 +668,13 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) lacp_rate); } + if (tb[IFLA_BOND_COUPLED_CONTROL]) { + print_on_off(PRINT_ANY, + "coupled_control", + "coupled_control %s ", + rta_getattr_u8(tb[IFLA_BOND_COUPLED_CONTROL])); + } + if (tb[IFLA_BOND_AD_SELECT]) { const char *ad_select = get_name(ad_select_tbl, rta_getattr_u8(tb[IFLA_BOND_AD_SELECT])); diff --git a/ip/iproute.c b/ip/iproute.c index 73dbab4..b530461 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -61,12 +61,13 @@ static void usage(void) " ip route save SELECTOR\n" " ip route restore\n" " ip route showdump\n" - " ip route get [ ROUTE_GET_FLAGS ] ADDRESS\n" + " ip route get [ ROUTE_GET_FLAGS ] [ to ] ADDRESS\n" " [ from ADDRESS iif STRING ]\n" " [ oif STRING ] [ tos TOS ]\n" " [ mark NUMBER ] [ vrf NAME ]\n" " [ uid NUMBER ] [ ipproto PROTOCOL ]\n" " [ sport NUMBER ] [ dport NUMBER ]\n" + " [ as ADDRESS ]\n" " ip route { add | del | change | append | replace } ROUTE\n" "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n" " [ table TABLE_ID ] [ vrf NAME ] [ proto RTPROTO ]\n" @@ -112,7 +113,8 @@ static void usage(void) "FLAVOR := { psp | usp | usd | next-csid }\n" "IOAM6HDR := trace prealloc type IOAM6_TRACE_TYPE ns IOAM6_NAMESPACE size IOAM6_TRACE_SIZE\n" "XFRMINFO := if_id IF_ID [ link_dev LINK ]\n" - "ROUTE_GET_FLAGS := [ fibmatch ]\n"); + "ROUTE_GET_FLAGS := ROUTE_GET_FLAG [ ROUTE_GET_FLAGS ]\n" + "ROUTE_GET_FLAG := [ connected | fibmatch | notify ]\n"); exit(-1); } diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c index 9498597..b4df434 100644 --- a/ip/iproute_lwtunnel.c +++ b/ip/iproute_lwtunnel.c @@ -2228,11 +2228,8 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp, invarg("\"encap type\" value is invalid\n", *argv); NEXT_ARG(); - if (argc <= 1) { - fprintf(stderr, - "Error: unexpected end of line after \"encap\"\n"); - exit(-1); - } + if (argc <= 1) + missarg("encap type"); nest = rta_nest(rta, len, encap_attr); switch (type) { @@ -82,7 +82,7 @@ main(int argc, char **argv) argc--; argv++; if (argc <= 1) - usage(); + missarg("family type"); if (strcmp(argv[1], "inet") == 0) family = AF_INET; else if (strcmp(argv[1], "inet6") == 0) @@ -108,7 +108,7 @@ main(int argc, char **argv) argc--; argv++; if (argc <= 1) - usage(); + missarg("file"); file = argv[1]; } else if (matches(argv[1], "link") == 0) { llink = 1; @@ -36,8 +36,13 @@ int cmd_exec(const char *cmd, char **argv, bool do_fork, } } - if (setup && setup(arg)) + if (setup && setup(arg)) { + if (do_fork) { + /* In child, nothing to do */ + _exit(1); + } return -1; + } if (execvp(cmd, argv) < 0) fprintf(stderr, "exec of \"%s\" failed: %s\n", diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 0164822..e2b284e 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -111,6 +111,10 @@ int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn) err_nlh = &err->msg; } + if (tb[NLMSGERR_ATTR_MISS_TYPE]) + fprintf(stderr, "Missing required attribute type %u\n", + mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_TYPE])); + if (errfn) return errfn(msg, off, err_nlh); diff --git a/lib/mnl_utils.c b/lib/mnl_utils.c index af5aa4f..6c8f527 100644 --- a/lib/mnl_utils.c +++ b/lib/mnl_utils.c @@ -193,6 +193,8 @@ int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name, if (!nlg->nl) goto err_socket_open; + nlg->version = version; + err = family_get(nlg, family_name); if (err) goto err_socket; diff --git a/lib/rt_names.c b/lib/rt_names.c index dafef3f..e967e0c 100644 --- a/lib/rt_names.c +++ b/lib/rt_names.c @@ -160,7 +160,7 @@ struct tabhash { static void rtnl_tabhash_readdir(const char *dirpath_base, const char *dirpath_overload, - const struct tabhash tabhash, const int size) + const struct tabhash tabhash, const int size) { struct dirent *de; DIR *d; @@ -216,14 +216,15 @@ rtnl_tabhash_initialize_dir(const char *ddir, const struct tabhash tabhash, cons } static void -rtnl_tab_initialize_dir(const char *ddir, char **tab, const int size) { +rtnl_tab_initialize_dir(const char *ddir, char **tab, const int size) +{ struct tabhash tab_data = {.type = TAB, .data.tab = tab}; rtnl_tabhash_initialize_dir(ddir, tab_data, size); } static void rtnl_hash_initialize_dir(const char *ddir, struct rtnl_hash_entry **hash, - const int size) { + const int size) { struct tabhash hash_data = {.type = HASH, .data.hash = hash}; rtnl_tabhash_initialize_dir(ddir, hash_data, size); } @@ -236,10 +237,10 @@ static void rtnl_rtprot_initialize(void) rtnl_rtprot_init = 1; ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_protos", - rtnl_rtprot_tab, 256); + rtnl_rtprot_tab, 256); if (ret == -ENOENT) rtnl_tab_initialize(CONF_USR_DIR "/rt_protos", - rtnl_rtprot_tab, 256); + rtnl_rtprot_tab, 256); rtnl_tab_initialize_dir("rt_protos.d", rtnl_rtprot_tab, 256); } @@ -308,12 +309,12 @@ static void rtnl_addrprot_initialize(void) rtnl_addrprot_tab_initialized = true; ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_addrprotos", - rtnl_addrprot_tab, - ARRAY_SIZE(rtnl_addrprot_tab)); + rtnl_addrprot_tab, + ARRAY_SIZE(rtnl_addrprot_tab)); if (ret == -ENOENT) ret = rtnl_tab_initialize(CONF_USR_DIR "/rt_addrprotos", - rtnl_addrprot_tab, - ARRAY_SIZE(rtnl_addrprot_tab)); + rtnl_addrprot_tab, + ARRAY_SIZE(rtnl_addrprot_tab)); } const char *rtnl_addrprot_n2a(__u8 id, char *buf, int len) @@ -370,7 +371,7 @@ static void rtnl_rtscope_initialize(void) rtnl_rtscope_init = 1; ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_scopes", - rtnl_rtscope_tab, 256); + rtnl_rtscope_tab, 256); if (ret == -ENOENT) rtnl_tab_initialize(CONF_USR_DIR "/rt_scopes", rtnl_rtscope_tab, 256); @@ -440,10 +441,10 @@ static void rtnl_rtrealm_initialize(void) rtnl_rtrealm_init = 1; ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_realms", - rtnl_rtrealm_tab, 256); + rtnl_rtrealm_tab, 256); if (ret == -ENOENT) rtnl_tab_initialize(CONF_USR_DIR "/rt_realms", - rtnl_rtrealm_tab, 256); + rtnl_rtrealm_tab, 256); } const char *rtnl_rtrealm_n2a(int id, char *buf, int len) @@ -519,10 +520,10 @@ static void rtnl_rttable_initialize(void) rtnl_rttable_hash[i]->id = i; } ret = rtnl_hash_initialize(CONF_ETC_DIR "/rt_tables", - rtnl_rttable_hash, 256); + rtnl_rttable_hash, 256); if (ret == -ENOENT) rtnl_hash_initialize(CONF_USR_DIR "/rt_tables", - rtnl_rttable_hash, 256); + rtnl_rttable_hash, 256); rtnl_hash_initialize_dir("rt_tables.d", rtnl_rttable_hash, 256); } @@ -590,10 +591,10 @@ static void rtnl_rtdsfield_initialize(void) rtnl_rtdsfield_init = 1; ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_dsfield", - rtnl_rtdsfield_tab, 256); + rtnl_rtdsfield_tab, 256); if (ret == -ENOENT) rtnl_tab_initialize(CONF_USR_DIR "/rt_dsfield", - rtnl_rtdsfield_tab, 256); + rtnl_rtdsfield_tab, 256); } const char *rtnl_dsfield_n2a(int id, char *buf, int len) @@ -674,10 +675,10 @@ static void rtnl_group_initialize(void) rtnl_group_init = 1; ret = rtnl_hash_initialize(CONF_ETC_DIR "/group", - rtnl_group_hash, 256); + rtnl_group_hash, 256); if (ret == -ENOENT) rtnl_hash_initialize(CONF_USR_DIR "/group", - rtnl_group_hash, 256); + rtnl_group_hash, 256); } int rtnl_group_a2n(int *id, const char *arg) @@ -769,10 +770,10 @@ static void nl_proto_initialize(void) nl_proto_init = 1; ret = rtnl_tab_initialize(CONF_ETC_DIR "/nl_protos", - nl_proto_tab, 256); + nl_proto_tab, 256); if (ret == -ENOENT) rtnl_tab_initialize(CONF_USR_DIR "/nl_protos", - nl_proto_tab, 256); + nl_proto_tab, 256); } const char *nl_proto_n2a(int id, char *buf, int len) @@ -835,7 +836,7 @@ static void protodown_reason_initialize(void) protodown_reason_init = 1; rtnl_tab_initialize_dir("protodown_reasons.d", protodown_reason_tab, - PROTODOWN_REASON_NUM_BITS); + PROTODOWN_REASON_NUM_BITS); } int protodown_reason_n2a(int id, char *buf, int len) diff --git a/lib/utils.c b/lib/utils.c index 6c1c1a8..deb7654 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -2003,3 +2003,17 @@ int proto_a2n(unsigned short *id, const char *buf, return 0; } + +FILE *generic_proc_open(const char *env, const char *name) +{ + const char *p = getenv(env); + char store[128]; + + if (!p) { + p = getenv("PROC_ROOT") ? : "/proc"; + snprintf(store, sizeof(store) - 1, "%s/%s", p, name); + p = store; + } + + return fopen(p, "r"); +} diff --git a/man/man8/bridge.8 b/man/man8/bridge.8 index eeea407..bb02bd2 100644 --- a/man/man8/bridge.8 +++ b/man/man8/bridge.8 @@ -22,6 +22,7 @@ bridge \- show / manipulate bridge addresses and devices \fB\-s\fR[\fItatistics\fR] | \fB\-n\fR[\fIetns\fR] name | \fB\-b\fR[\fIatch\fR] filename | +\fB\-com\fR[\fIpressvlans\fR] | \fB\-c\fR[\fIolor\fR] | \fB\-p\fR[\fIretty\fR] | \fB\-j\fR[\fIson\fR] | @@ -346,6 +347,11 @@ If there were any errors during execution of the commands, the application return code will be non zero. .TP +.BR "\-com", " \-compressvlans" +Show compressed VLAN list. It will show continuous VLANs with the range instead +of separated VLANs. Default is off. + +.TP .BR \-c [ color ][ = { always | auto | never } Configure color output. If parameter is omitted or .BR always , diff --git a/man/man8/ip-ioam.8 b/man/man8/ip-ioam.8 index 1bdc0ec..c723d78 100644 --- a/man/man8/ip-ioam.8 +++ b/man/man8/ip-ioam.8 @@ -49,12 +49,17 @@ ip-ioam \- IPv6 In-situ OAM (IOAM) .RI " { " ID " | " .BR none " }" +.ti -8 +.B ip ioam monitor + .SH DESCRIPTION The \fBip ioam\fR command is used to configure IPv6 In-situ OAM (IOAM6) internal parameters, namely IOAM namespaces and schemas. .PP Those parameters also include the mapping between an IOAM namespace and an IOAM schema. +.PP +The \fBip ioam monitor\fR command displays IOAM data received. .SH EXAMPLES .PP diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 31e2d7f..1e4dfcd 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -63,7 +63,7 @@ ip-link \- network device configuration .RI "[ " ARGS " ]" .ti -8 -.BR "ip link set " { +.BR "ip link" " { " set " | " change " } " { .IR DEVICE " | " .BI "group " GROUP } diff --git a/man/man8/ip-netns.8.in b/man/man8/ip-netns.8.in index 2911bdd..2e12e28 100644 --- a/man/man8/ip-netns.8.in +++ b/man/man8/ip-netns.8.in @@ -98,7 +98,7 @@ If NAME is available in @NETNS_RUN_DIR@ this command creates a new network namespace and assigns NAME. .TP -.B ip netns attach NAME PID - create a new named network namespace +.B ip netns attach NAME PID - assign a name to the network namespace of the process .sp If NAME is available in @NETNS_RUN_DIR@ this command attaches the network namespace of the process PID to NAME as if it were created with ip netns. diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in index 10387bc..df49f8b 100644 --- a/man/man8/ip-route.8.in +++ b/man/man8/ip-route.8.in @@ -29,6 +29,7 @@ ip-route \- routing table management .ti -8 .B ip route get .I ROUTE_GET_FLAGS +.B [ to ] .IR ADDRESS " [ " .BI from " ADDRESS " iif " STRING" .RB " ] [ " oif @@ -44,7 +45,9 @@ ip-route \- routing table management .B sport .IR NUMBER " ] [ " .B dport -.IR NUMBER " ] " +.IR NUMBER " ] [" +.B as +.IR ADDRESS " ]" .ti -8 .BR "ip route" " { " add " | " del " | " change " | " append " | "\ @@ -263,9 +266,14 @@ throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]" .ti -8 .IR ROUTE_GET_FLAGS " := " -.BR " [ " -.BR fibmatch -.BR " ] " +.IR ROUTE_GET_FLAG " [ " +.IR ROUTE_GET_FLAGS " ] " + +.ti -8 +.IR ROUTE_GET_FLAG " := " +.BR "[ " +.BR connected " | " fibmatch " | " notify +.BR "]" .SH DESCRIPTION .B ip route diff --git a/man/man8/ss.8 b/man/man8/ss.8 index 4ece41f..e23af82 100644 --- a/man/man8/ss.8 +++ b/man/man8/ss.8 @@ -24,6 +24,9 @@ Output version information. .B \-H, \-\-no-header Suppress header line. .TP +.B \-Q, \-\-no-queues +Suppress sending and receiving queue columns. +.TP .B \-O, \-\-oneline Print each socket's data on a single line. .TP @@ -423,6 +426,12 @@ to FILE after applying filters. If FILE is - stdout is used. Read filter information from FILE. Each line of FILE is interpreted like single command line option. If FILE is - stdin is used. .TP +.B \-\-bpf-maps +Pretty-print all the BPF socket-local data entries for each socket. +.TP +.B \-\-bpf-map-id=MAP_ID +Pretty-print the BPF socket-local data entries for the requested map ID. Can be used more than once. +.TP .B FILTER := [ state STATE-FILTER ] [ EXPRESSION ] Please take a look at the official documentation for details regarding filters. diff --git a/man/man8/tc-matchall.8 b/man/man8/tc-matchall.8 index d022406..538cf74 100644 --- a/man/man8/tc-matchall.8 +++ b/man/man8/tc-matchall.8 @@ -37,39 +37,39 @@ To create ingress mirroring from port eth1 to port eth2: .RS .EX -tc qdisc add dev eth1 handle ffff: ingress -tc filter add dev eth1 parent ffff: \\ +tc qdisc add dev eth1 handle ffff: clsact +tc filter add dev eth1 ingress \\ matchall skip_sw \\ action mirred egress mirror \\ dev eth2 .EE .RE -The first command creates an ingress qdisc with handle +The first command creates a clsact qdisc with handle .BR ffff: on device .BR eth1 where the second command attaches a matchall filters on it that mirrors the -packets to device eth2. +packets to device eth2 for ingress. To create egress mirroring from port eth1 to port eth2: .RS .EX -tc qdisc add dev eth1 handle 1: root prio -tc filter add dev eth1 parent 1: \\ +tc qdisc add dev eth1 handle ffff: clsact +tc filter add dev eth1 egress \\ matchall skip_sw \\ action mirred egress mirror \\ dev eth2 .EE .RE -The first command creates an egress qdisc with handle -.BR 1: -that replaces the root qdisc on device +The first command creates a clsact qdisc with handle +.BR ffff: +on device .BR eth1 where the second command attaches a matchall filters on it that mirrors the -packets to device eth2. +packets to device eth2 for egress. To sample one of every 100 packets flowing into interface eth0 to psample group 12: diff --git a/man/man8/tc-mirred.8 b/man/man8/tc-mirred.8 index e529fa6..6959e3e 100644 --- a/man/man8/tc-mirred.8 +++ b/man/man8/tc-mirred.8 @@ -9,13 +9,24 @@ mirred - mirror/redirect action .I DIRECTION ACTION .RB "[ " index .IR INDEX " ] " -.BI dev " DEVICENAME" +.I TARGET .ti -8 .IR DIRECTION " := { " .BR ingress " | " egress " }" .ti -8 +.IR TARGET " := { " DEV " | " BLOCK " }" + +.ti -8 +.IR DEV " := " +.BI dev " DEVICENAME" + +.ti -8 +.IR BLOCK " := " +.BI blockid " BLOCKID" + +.ti -8 .IR ACTION " := { " .BR mirror " | " redirect " }" .SH DESCRIPTION @@ -24,6 +35,12 @@ The action allows packet mirroring (copying) or redirecting (stealing) the packet it receives. Mirroring is what is sometimes referred to as Switch Port Analyzer (SPAN) and is commonly used to analyze and/or debug flows. +When mirroring to a tc block, the packet will be mirrored to all the ports in +the block with exception of the port where the packet ingressed, if that port is +part of the tc block. Redirecting is similar to mirroring except that the +behaviour is to mirror to the first N - 1 ports in the block and redirect to the +last one (note that the port in which the packet arrived is not going to be +mirrored or redirected to). .SH OPTIONS .TP .B ingress @@ -39,7 +56,7 @@ Define whether the packet should be copied .RB ( mirror ) or moved .RB ( redirect ) -to the destination interface. +to the destination interface or block. .TP .BI index " INDEX" Assign a unique ID to this action instead of letting the kernel choose one @@ -49,14 +66,17 @@ is a 32bit unsigned integer greater than zero. .TP .BI dev " DEVICENAME" Specify the network interface to redirect or mirror to. +.TP +.BI blockid " BLOCKID" +Specify the tc block to redirect or mirror to. .SH EXAMPLES Limit ingress bandwidth on eth0 to 1mbit/s, redirect exceeding traffic to lo for debugging purposes: .RS .EX -# tc qdisc add dev eth0 handle ffff: ingress -# tc filter add dev eth0 parent ffff: u32 \\ +# tc qdisc add dev eth0 handle ffff: clsact +# tc filter add dev eth0 ingress u32 \\ match u32 0 0 \\ action police rate 1mbit burst 100k conform-exceed pipe \\ action mirred egress redirect dev lo @@ -70,8 +90,8 @@ with e.g. tcpdump: .EX # ip link add dummy0 type dummy # ip link set dummy0 up -# tc qdisc add dev eth0 handle ffff: ingress -# tc filter add dev eth0 parent ffff: protocol ip \\ +# tc qdisc add dev eth0 handle ffff: clsact +# tc filter add dev eth0 ingress protocol ip \\ u32 match ip protocol 1 0xff \\ action mirred egress mirror dev dummy0 .EE @@ -87,14 +107,14 @@ interface, it is possible to send ingress traffic through an instance of # modprobe ifb # ip link set ifb0 up # tc qdisc add dev ifb0 root sfq -# tc qdisc add dev eth0 handle ffff: ingress -# tc filter add dev eth0 parent ffff: u32 \\ +# tc qdisc add dev eth0 handle ffff: clsact +# tc filter add dev eth0 ingress u32 \\ match u32 0 0 \\ action mirred egress redirect dev ifb0 .EE .RE -.SH LIMITIATIONS +.SH LIMITATIONS The kernel restricts nesting to four levels to avoid the chance of nesting loops. .PP diff --git a/man/man8/tc-simple.8 b/man/man8/tc-simple.8 index f565755..ae1aec3 100644 --- a/man/man8/tc-simple.8 +++ b/man/man8/tc-simple.8 @@ -55,11 +55,11 @@ grep the logs to see the logged message display stats again and observe increment by 1 .EX - hadi@noma1:$ tc qdisc add dev eth0 ingress - hadi@noma1:$tc filter add dev eth0 parent ffff: protocol ip prio 5 \\ + $ tc qdisc add dev eth0 ingress + $ tc filter add dev eth0 parent ffff: protocol ip prio 5 \\ u32 match ip protocol 1 0xff flowid 1:1 action simple sdata "Incoming ICMP" - hadi@noma1:$ sudo tc -s filter ls dev eth0 parent ffff: + $ sudo tc -s filter ls dev eth0 parent ffff: filter protocol ip pref 5 u32 filter protocol ip pref 5 u32 fh 800: ht divisor 1 filter protocol ip pref 5 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 @@ -71,7 +71,7 @@ display stats again and observe increment by 1 backlog 0b 0p requeues 0 - hadi@noma1$ ping -c 1 www.google.ca + $ ping -c 1 www.google.ca PING www.google.ca (74.125.225.120) 56(84) bytes of data. 64 bytes from ord08s08-in-f24.1e100.net (74.125.225.120): icmp_req=1 ttl=53 time=31.3 ms @@ -79,10 +79,10 @@ display stats again and observe increment by 1 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 31.316/31.316/31.316/0.000 ms - hadi@noma1$ dmesg | grep simple + $ dmesg | grep simple [135354.473951] simple: Incoming ICMP_1 - hadi@noma1$ sudo tc/tc -s filter ls dev eth0 parent ffff: + $ sudo tc/tc -s filter ls dev eth0 parent ffff: filter protocol ip pref 5 u32 filter protocol ip pref 5 u32 fh 800: ht divisor 1 filter protocol ip pref 5 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 diff --git a/man/man8/tc.8 b/man/man8/tc.8 index 3175454..dce58af 100644 --- a/man/man8/tc.8 +++ b/man/man8/tc.8 @@ -127,7 +127,7 @@ tc \- show / manipulate traffic control settings \fB[ \fB-nm \fR| \fB-nam\fR[\fIes\fR] \fB] \fR| \fB[ \fR{ \fB-cf \fR| \fB-c\fR[\fIonf\fR] \fR} \fB[ filename ] \fB] \fR \fB[ -t\fR[imestamp\fR] \fB\] \fR| \fB[ -t\fR[short\fR] \fR| \fB[ --o\fR[neline\fR] \fB]\fR } +-o\fR[neline\fR] \fB] \fR| \fB[ -echo ]\fR } .ti 8 .IR FORMAT " := {" @@ -743,6 +743,10 @@ When\fB\ tc monitor\fR\ runs, print timestamp before the event message in format When\fB\ tc monitor\fR\ runs, prints short timestamp before the event message in format: [<YYYY>-<MM>-<DD>T<hh:mm:ss>.<ms>] +.TP +.BR "\-echo" +Request the kernel to send the applied configuration back. + .SH FORMAT The show command has additional formatting options: diff --git a/misc/arpd.c b/misc/arpd.c index 1ef837c..65ac6a3 100644 --- a/misc/arpd.c +++ b/misc/arpd.c @@ -19,6 +19,7 @@ #include <fcntl.h> #include <sys/uio.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <signal.h> @@ -35,7 +36,8 @@ #include "rt_names.h" DB *dbase; -char *dbname = "/var/lib/arpd/arpd.db"; +char const default_dbname[] = ARPDDIR "/arpd.db"; +char const *dbname = default_dbname; int ifnum; int *ifvec; @@ -668,6 +670,13 @@ int main(int argc, char **argv) } } + if (strcmp(default_dbname, dbname) == 0) { + if (mkdir(ARPDDIR, 0755) != 0 && errno != EEXIST) { + perror("create_db_dir"); + exit(-1); + } + } + dbase = dbopen(dbname, O_CREAT|O_RDWR, 0644, DB_HASH, NULL); if (dbase == NULL) { perror("db_open"); diff --git a/misc/ifstat.c b/misc/ifstat.c index 7290109..9b93ded 100644 --- a/misc/ifstat.c +++ b/misc/ifstat.c @@ -51,7 +51,7 @@ int sub_type; char info_source[128]; int source_mismatch; -#define MAXS (sizeof(struct rtnl_link_stats)/sizeof(__u32)) +#define MAXS (sizeof(struct rtnl_link_stats64)/sizeof(__u64)) #define NO_SUB_TYPE 0xffff struct ifstat_ent { @@ -60,7 +60,7 @@ struct ifstat_ent { int ifindex; unsigned long long val[MAXS]; double rate[MAXS]; - __u32 ival[MAXS]; + __u64 ival[MAXS]; }; static const char *stats[MAXS] = { @@ -74,19 +74,25 @@ static const char *stats[MAXS] = { "tx_dropped", "multicast", "collisions", + "rx_length_errors", "rx_over_errors", "rx_crc_errors", "rx_frame_errors", "rx_fifo_errors", "rx_missed_errors", + "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors", "tx_heartbeat_errors", "tx_window_errors", + "rx_compressed", - "tx_compressed" + "tx_compressed", + "rx_nohandler", + + "rx_otherhost_dropped", }; struct ifstat_ent *kern_db; @@ -134,6 +140,10 @@ static int get_nlmsg_extended(struct nlmsghdr *m, void *arg) n->ifindex = ifsm->ifindex; n->name = strdup(ll_index_to_name(ifsm->ifindex)); + if (!n->name) { + free(n); + return -1; + } if (sub_type == NO_SUB_TYPE) { memcpy(&n->val, RTA_DATA(tb[filter_type]), sizeof(n->val)); @@ -174,7 +184,7 @@ static int get_nlmsg(struct nlmsghdr *m, void *arg) return 0; parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); - if (tb[IFLA_IFNAME] == NULL || tb[IFLA_STATS] == NULL) + if (tb[IFLA_IFNAME] == NULL) return 0; n = malloc(sizeof(*n)); @@ -182,10 +192,30 @@ static int get_nlmsg(struct nlmsghdr *m, void *arg) errno = ENOMEM; return -1; } + n->ifindex = ifi->ifi_index; n->name = strdup(RTA_DATA(tb[IFLA_IFNAME])); - memcpy(&n->ival, RTA_DATA(tb[IFLA_STATS]), sizeof(n->ival)); + if (!n->name) { + free(n); + return -1; + } + memset(&n->rate, 0, sizeof(n->rate)); + + if (tb[IFLA_STATS64]) { + memcpy(&n->ival, RTA_DATA(tb[IFLA_STATS64]), sizeof(n->ival)); + } else if (tb[IFLA_STATS]) { + __u32 *stats = RTA_DATA(tb[IFLA_STATS]); + + /* expand 32 bit values to 64 bit */ + for (i = 0; i < MAXS; i++) + n->ival[i] = stats[i]; + } else { + /* missing stats? */ + free(n); + return 0; + } + for (i = 0; i < MAXS; i++) n->val[i] = n->ival[i]; n->next = kern_db; @@ -379,10 +409,10 @@ static void format_rate(FILE *fp, const unsigned long long *vals, fprintf(fp, "%8llu ", vals[i]); if (rates[i] > mega) { - sprintf(temp, "%uM", (unsigned int)(rates[i]/mega)); + snprintf(temp, sizeof(temp), "%uM", (unsigned int)(rates[i]/mega)); fprintf(fp, "%-6s ", temp); } else if (rates[i] > kilo) { - sprintf(temp, "%uK", (unsigned int)(rates[i]/kilo)); + snprintf(temp, sizeof(temp), "%uK", (unsigned int)(rates[i]/kilo)); fprintf(fp, "%-6s ", temp); } else fprintf(fp, "%-6u ", (unsigned int)rates[i]); @@ -400,10 +430,10 @@ static void format_pair(FILE *fp, const unsigned long long *vals, int i, int k) fprintf(fp, "%8llu ", vals[i]); if (vals[k] > giga) { - sprintf(temp, "%uM", (unsigned int)(vals[k]/mega)); + snprintf(temp, sizeof(temp), "%uM", (unsigned int)(vals[k]/mega)); fprintf(fp, "%-6s ", temp); } else if (vals[k] > mega) { - sprintf(temp, "%uK", (unsigned int)(vals[k]/kilo)); + snprintf(temp, sizeof(temp), "%uK", (unsigned int)(vals[k]/kilo)); fprintf(fp, "%-6s ", temp); } else fprintf(fp, "%-6u ", (unsigned int)vals[k]); @@ -675,7 +705,7 @@ static void server_loop(int fd) p.fd = fd; p.events = p.revents = POLLIN; - sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d", + snprintf(info_source, sizeof(info_source), "%d.%lu sampling_interval=%d time_const=%d", getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); load_info(); @@ -893,7 +923,7 @@ int main(int argc, char *argv[]) sun.sun_family = AF_UNIX; sun.sun_path[0] = 0; - sprintf(sun.sun_path+1, "ifstat%d", getuid()); + snprintf(sun.sun_path + 1, sizeof(sun.sun_path) - 1, "ifstat%d", getuid()); if (scan_interval > 0) { if (time_constant == 0) diff --git a/misc/nstat.c b/misc/nstat.c index 2c10fea..07d010d 100644 --- a/misc/nstat.c +++ b/misc/nstat.c @@ -43,35 +43,22 @@ int npatterns; char info_source[128]; int source_mismatch; -static int generic_proc_open(const char *env, char *name) -{ - char store[128]; - char *p = getenv(env); - - if (!p) { - p = getenv("PROC_ROOT") ? : "/proc"; - snprintf(store, sizeof(store)-1, "%s/%s", p, name); - p = store; - } - return open(p, O_RDONLY); -} - -static int net_netstat_open(void) +static FILE *net_netstat_open(void) { return generic_proc_open("PROC_NET_NETSTAT", "net/netstat"); } -static int net_snmp_open(void) +static FILE *net_snmp_open(void) { return generic_proc_open("PROC_NET_SNMP", "net/snmp"); } -static int net_snmp6_open(void) +static FILE *net_snmp6_open(void) { return generic_proc_open("PROC_NET_SNMP6", "net/snmp6"); } -static int net_sctp_snmp_open(void) +static FILE *net_sctp_snmp_open(void) { return generic_proc_open("PROC_NET_SCTP_SNMP", "net/sctp/snmp"); } @@ -277,7 +264,7 @@ static void load_ugly_table(FILE *fp) static void load_sctp_snmp(void) { - FILE *fp = fdopen(net_sctp_snmp_open(), "r"); + FILE *fp = net_sctp_snmp_open(); if (fp) { load_good_table(fp); @@ -287,7 +274,7 @@ static void load_sctp_snmp(void) static void load_snmp(void) { - FILE *fp = fdopen(net_snmp_open(), "r"); + FILE *fp = net_snmp_open(); if (fp) { load_ugly_table(fp); @@ -297,7 +284,7 @@ static void load_snmp(void) static void load_snmp6(void) { - FILE *fp = fdopen(net_snmp6_open(), "r"); + FILE *fp = net_snmp6_open(); if (fp) { load_good_table(fp); @@ -307,7 +294,7 @@ static void load_snmp6(void) static void load_netstat(void) { - FILE *fp = fdopen(net_netstat_open(), "r"); + FILE *fp = net_netstat_open(); if (fp) { load_ugly_table(fp); @@ -483,7 +470,7 @@ static void server_loop(int fd) p.fd = fd; p.events = p.revents = POLLIN; - sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d", + snprintf(info_source, sizeof(info_source), "%d.%lu sampling_interval=%d time_const=%d", getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); load_netstat(); @@ -580,7 +567,7 @@ static const struct option longopts[] = { int main(int argc, char *argv[]) { - char *hist_name; + char hist_name[128]; struct sockaddr_un sun; FILE *hist_fp = NULL; int ch; @@ -636,7 +623,7 @@ int main(int argc, char *argv[]) sun.sun_family = AF_UNIX; sun.sun_path[0] = 0; - sprintf(sun.sun_path+1, "nstat%d", getuid()); + snprintf(sun.sun_path + 1, sizeof(sun.sun_path) - 1, "nstat%d", getuid()); if (scan_interval > 0) { if (time_constant == 0) @@ -668,10 +655,11 @@ int main(int argc, char *argv[]) patterns = argv; npatterns = argc; - if ((hist_name = getenv("NSTAT_HISTORY")) == NULL) { - hist_name = malloc(128); - sprintf(hist_name, "/tmp/.nstat.u%d", getuid()); - } + if (getenv("NSTAT_HISTORY")) + snprintf(hist_name, sizeof(hist_name), + "%s", getenv("NSTAT_HISTORY")); + else + snprintf(hist_name, sizeof(hist_name), "/tmp/.nstat.u%d", getuid()); if (reset_history) unlink(hist_name); @@ -52,6 +52,26 @@ #include <linux/tls.h> #include <linux/mptcp.h> +#ifdef HAVE_LIBBPF +/* If libbpf is new enough (0.5+), support for pretty-printing BPF socket-local + * storage is enabled, otherwise we emit a warning and disable it. + * ENABLE_BPF_SKSTORAGE_SUPPORT is only used to gate the socket-local storage + * feature, so this wouldn't prevent any feature relying on HAVE_LIBBPF to be + * usable. + */ +#define ENABLE_BPF_SKSTORAGE_SUPPORT + +#include <bpf/bpf.h> +#include <bpf/btf.h> +#include <bpf/libbpf.h> +#include <linux/btf.h> + +#if (LIBBPF_MAJOR_VERSION == 0) && (LIBBPF_MINOR_VERSION < 5) +#warning "libbpf version 0.5 or later is required, disabling BPF socket-local storage support" +#undef ENABLE_BPF_SKSTORAGE_SUPPORT +#endif +#endif + #if HAVE_RPC #include <rpc/rpc.h> #include <rpc/xdr.h> @@ -76,6 +96,7 @@ int preferred_family = AF_UNSPEC; static int show_options; int show_details; +static int show_queues = 1; static int show_processes; static int show_threads; static int show_mem; @@ -458,19 +479,6 @@ static void filter_merge_defaults(struct filter *f) } } -static FILE *generic_proc_open(const char *env, const char *name) -{ - const char *p = getenv(env); - char store[128]; - - if (!p) { - p = getenv("PROC_ROOT") ? : "/proc"; - snprintf(store, sizeof(store)-1, "%s/%s", p, name); - p = store; - } - - return fopen(p, "r"); -} #define net_tcp_open() generic_proc_open("PROC_NET_TCP", "net/tcp") #define net_tcp6_open() generic_proc_open("PROC_NET_TCP6", "net/tcp6") #define net_udp_open() generic_proc_open("PROC_NET_UDP", "net/udp") @@ -1035,11 +1043,10 @@ static int buf_update(int len) } /* Append content to buffer as part of the current field */ -__attribute__((format(printf, 1, 2))) -static void out(const char *fmt, ...) +static void vout(const char *fmt, va_list args) { struct column *f = current_field; - va_list args; + va_list _args; char *pos; int len; @@ -1050,18 +1057,27 @@ static void out(const char *fmt, ...) buffer.head = buf_chunk_new(); again: /* Append to buffer: if we have a new chunk, print again */ + va_copy(_args, args); pos = buffer.cur->data + buffer.cur->len; - va_start(args, fmt); /* Limit to tail room. If we hit the limit, buf_update() will tell us */ - len = vsnprintf(pos, buf_chunk_avail(buffer.tail), fmt, args); - va_end(args); + len = vsnprintf(pos, buf_chunk_avail(buffer.tail), fmt, _args); if (buf_update(len)) goto again; } +__attribute__((format(printf, 1, 2))) +static void out(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vout(fmt, args); + va_end(args); +} + static int print_left_spacing(struct column *f, int stored, int printed) { int s; @@ -1426,10 +1442,13 @@ static void sock_state_print(struct sockstat *s) out("%s", sstate_name[s->state]); } - field_set(COL_RECVQ); - out("%-6d", s->rq); - field_set(COL_SENDQ); - out("%-6d", s->wq); + if (show_queues) { + field_set(COL_RECVQ); + out("%-6d", s->rq); + field_set(COL_SENDQ); + out("%-6d", s->wq); + } + field_set(COL_ADDR); } @@ -3395,6 +3414,318 @@ static void parse_diag_msg(struct nlmsghdr *nlh, struct sockstat *s) memcpy(s->remote.data, r->id.idiag_dst, s->local.bytelen); } +#ifdef ENABLE_BPF_SKSTORAGE_SUPPORT + +#define MAX_NR_BPF_MAP_ID_OPTS 32 + +struct btf; + +static struct bpf_map_opts { + unsigned int nr_maps; + struct bpf_sk_storage_map_info { + unsigned int id; + int fd; + struct bpf_map_info info; + struct btf *btf; + struct btf_dump *dump; + } maps[MAX_NR_BPF_MAP_ID_OPTS]; + bool show_all; +} bpf_map_opts; + +static void bpf_map_opts_mixed_error(void) +{ + fprintf(stderr, + "ss: --bpf-maps and --bpf-map-id cannot be used together\n"); +} + +static int bpf_maps_opts_load_btf(struct bpf_map_info *info, struct btf **btf) +{ + if (info->btf_value_type_id) { + *btf = btf__load_from_kernel_by_id(info->btf_id); + if (!*btf) { + fprintf(stderr, "ss: failed to load BTF for map ID %u\n", + info->id); + return -1; + } + } else { + *btf = NULL; + } + + return 0; +} + +static void out_bpf_sk_storage_print_fn(void *ctx, const char *fmt, va_list args) +{ + vout(fmt, args); +} + +static int bpf_map_opts_load_info(unsigned int map_id) +{ + struct btf_dump_opts dopts = { + .sz = sizeof(struct btf_dump_opts) + }; + struct bpf_map_info info = {}; + uint32_t len = sizeof(info); + struct btf_dump *dump; + struct btf *btf; + int fd; + int r; + + if (bpf_map_opts.nr_maps == MAX_NR_BPF_MAP_ID_OPTS) { + fprintf(stderr, + "ss: too many (> %u) BPF socket-local storage maps found, skipping map ID %u\n", + MAX_NR_BPF_MAP_ID_OPTS, map_id); + return 0; + } + + fd = bpf_map_get_fd_by_id(map_id); + if (fd < 0) { + if (errno == -ENOENT) + return 0; + + fprintf(stderr, "ss: cannot get fd for BPF map ID %u%s\n", + map_id, errno == EPERM ? + ": missing root permissions, CAP_BPF, or CAP_SYS_ADMIN" : ""); + return -1; + } + + r = bpf_obj_get_info_by_fd(fd, &info, &len); + if (r) { + fprintf(stderr, "ss: failed to get info for BPF map ID %u\n", + map_id); + close(fd); + return -1; + } + + if (info.type != BPF_MAP_TYPE_SK_STORAGE) { + fprintf(stderr, + "ss: BPF map with ID %s has type ID %d, expecting %d ('sk_storage')\n", + optarg, info.type, BPF_MAP_TYPE_SK_STORAGE); + close(fd); + return -1; + } + + r = bpf_maps_opts_load_btf(&info, &btf); + if (r) { + close(fd); + return -1; + } + + dump = btf_dump__new(btf, out_bpf_sk_storage_print_fn, NULL, &dopts); + if (!dump) { + btf__free(btf); + close(fd); + fprintf(stderr, "Failed to create btf_dump object\n"); + return -1; + } + + bpf_map_opts.maps[bpf_map_opts.nr_maps].id = map_id; + bpf_map_opts.maps[bpf_map_opts.nr_maps].fd = fd; + bpf_map_opts.maps[bpf_map_opts.nr_maps].info = info; + bpf_map_opts.maps[bpf_map_opts.nr_maps].btf = btf; + bpf_map_opts.maps[bpf_map_opts.nr_maps++].dump = dump; + + return 0; +} + +static struct bpf_sk_storage_map_info *bpf_map_opts_get_info( + unsigned int map_id) +{ + unsigned int i; + int r; + + for (i = 0; i < bpf_map_opts.nr_maps; ++i) { + if (bpf_map_opts.maps[i].id == map_id) + return &bpf_map_opts.maps[i]; + } + + r = bpf_map_opts_load_info(map_id); + if (r) + return NULL; + + return &bpf_map_opts.maps[bpf_map_opts.nr_maps - 1]; +} + +static int bpf_map_opts_add_id(const char *optarg) +{ + size_t optarg_len; + unsigned long id; + char *end; + + if (bpf_map_opts.show_all) { + bpf_map_opts_mixed_error(); + return -1; + } + + optarg_len = strlen(optarg); + id = strtoul(optarg, &end, 0); + if (end != optarg + optarg_len || id == 0 || id >= UINT32_MAX) { + fprintf(stderr, "ss: invalid BPF map ID %s\n", optarg); + return -1; + } + + /* Force lazy loading of the map's data. */ + if (!bpf_map_opts_get_info(id)) + return -1; + + return 0; +} + +static void bpf_map_opts_destroy(void) +{ + int i; + + for (i = 0; i < bpf_map_opts.nr_maps; ++i) { + btf_dump__free(bpf_map_opts.maps[i].dump); + btf__free(bpf_map_opts.maps[i].btf); + close(bpf_map_opts.maps[i].fd); + } +} + +static struct rtattr *bpf_map_opts_alloc_rta(void) +{ + struct rtattr *stgs_rta, *fd_rta; + size_t total_size; + unsigned int i; + void *buf; + + /* If bpf_map_opts.show_all == true, we will send an empty message to + * the kernel, which will return all the socket-local data attached to + * a socket, no matter their map ID + */ + if (bpf_map_opts.show_all) { + total_size = RTA_LENGTH(0); + } else { + total_size = RTA_LENGTH(RTA_LENGTH(sizeof(int)) * + bpf_map_opts.nr_maps); + } + + buf = malloc(total_size); + if (!buf) + return NULL; + + stgs_rta = buf; + stgs_rta->rta_type = INET_DIAG_REQ_SK_BPF_STORAGES | NLA_F_NESTED; + stgs_rta->rta_len = total_size; + + /* If inet_show_netlink() retries fetching socket data, nr_maps might + * be different from 0, even with show_all == true, so we return early + * to avoid inserting specific map IDs into the request. + */ + if (bpf_map_opts.show_all) + return stgs_rta; + + buf = RTA_DATA(stgs_rta); + for (i = 0; i < bpf_map_opts.nr_maps; i++) { + int *fd; + + fd_rta = buf; + fd_rta->rta_type = SK_DIAG_BPF_STORAGE_REQ_MAP_FD; + fd_rta->rta_len = RTA_LENGTH(sizeof(int)); + + fd = RTA_DATA(fd_rta); + *fd = bpf_map_opts.maps[i].fd; + + buf += fd_rta->rta_len; + } + + return stgs_rta; +} + +static void out_bpf_sk_storage_oneline(struct bpf_sk_storage_map_info *info, + const void *data, size_t len) +{ + struct btf_dump_type_data_opts opts = { + .sz = sizeof(struct btf_dump_type_data_opts), + .emit_zeroes = 1, + .compact = 1 + }; + int r; + + out(" map_id:%d", info->id); + r = btf_dump__dump_type_data(info->dump, info->info.btf_value_type_id, + data, len, &opts); + if (r < 0) + out("failed to dump data: %d", r); +} + +static void out_bpf_sk_storage_multiline(struct bpf_sk_storage_map_info *info, + const void *data, size_t len) +{ + struct btf_dump_type_data_opts opts = { + .sz = sizeof(struct btf_dump_type_data_opts), + .indent_level = 2, + .emit_zeroes = 1 + }; + int r; + + out("\n\tmap_id:%d [\n", info->id); + + r = btf_dump__dump_type_data(info->dump, info->info.btf_value_type_id, + data, len, &opts); + if (r < 0) + out("\t\tfailed to dump data: %d", r); + + out("\n\t]"); +} + +static void out_bpf_sk_storage(int map_id, const void *data, size_t len) +{ + struct bpf_sk_storage_map_info *map_info; + + map_info = bpf_map_opts_get_info(map_id); + if (!map_info) { + /* The kernel might return a map we can't get info for, skip + * it but print the other ones. + */ + out("\n\tmap_id: %d failed to fetch info, skipping\n", map_id); + return; + } + + if (map_info->info.value_size != len) { + fprintf(stderr, + "map_id: %d: invalid value size, expecting %u, got %lu\n", + map_id, map_info->info.value_size, len); + return; + } + + if (oneline) + out_bpf_sk_storage_oneline(map_info, data, len); + else + out_bpf_sk_storage_multiline(map_info, data, len); +} + +static void show_sk_bpf_storages(struct rtattr *bpf_stgs) +{ + struct rtattr *tb[SK_DIAG_BPF_STORAGE_MAX + 1], *bpf_stg; + unsigned int rem, map_id; + struct rtattr *value; + + for (bpf_stg = RTA_DATA(bpf_stgs), rem = RTA_PAYLOAD(bpf_stgs); + RTA_OK(bpf_stg, rem); bpf_stg = RTA_NEXT(bpf_stg, rem)) { + + if ((bpf_stg->rta_type & NLA_TYPE_MASK) != SK_DIAG_BPF_STORAGE) + continue; + + parse_rtattr_nested(tb, SK_DIAG_BPF_STORAGE_MAX, + (struct rtattr *)bpf_stg); + + if (tb[SK_DIAG_BPF_STORAGE_MAP_ID]) { + map_id = rta_getattr_u32(tb[SK_DIAG_BPF_STORAGE_MAP_ID]); + value = tb[SK_DIAG_BPF_STORAGE_MAP_VALUE]; + + out_bpf_sk_storage(map_id, RTA_DATA(value), + RTA_PAYLOAD(value)); + } + } +} + +static bool bpf_map_opts_is_enabled(void) +{ + return bpf_map_opts.nr_maps || bpf_map_opts.show_all; +} +#endif + static int inet_show_sock(struct nlmsghdr *nlh, struct sockstat *s) { @@ -3402,8 +3733,9 @@ static int inet_show_sock(struct nlmsghdr *nlh, struct inet_diag_msg *r = NLMSG_DATA(nlh); unsigned char v6only = 0; - parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr *)(r+1), - nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + parse_rtattr_flags(tb, INET_DIAG_MAX, (struct rtattr *)(r+1), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)), + NLA_F_NESTED); if (tb[INET_DIAG_PROTOCOL]) s->type = rta_getattr_u8(tb[INET_DIAG_PROTOCOL]); @@ -3500,6 +3832,11 @@ static int inet_show_sock(struct nlmsghdr *nlh, } sctp_ino = s->ino; +#ifdef ENABLE_BPF_SKSTORAGE_SUPPORT + if (tb[INET_DIAG_SK_BPF_STORAGES]) + show_sk_bpf_storages(tb[INET_DIAG_SK_BPF_STORAGES]); +#endif + return 0; } @@ -3581,13 +3918,14 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) { struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; DIAG_REQUEST(req, struct inet_diag_req_v2 r); + struct rtattr *bpf_rta = NULL; char *bc = NULL; int bclen; __u32 proto; struct msghdr msg; struct rtattr rta_bc; struct rtattr rta_proto; - struct iovec iov[5]; + struct iovec iov[6]; int iovlen = 1; if (family == PF_UNSPEC) @@ -3640,6 +3978,20 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) iovlen += 2; } +#ifdef ENABLE_BPF_SKSTORAGE_SUPPORT + if (bpf_map_opts_is_enabled()) { + bpf_rta = bpf_map_opts_alloc_rta(); + if (!bpf_rta) { + fprintf(stderr, + "ss: cannot alloc request for --bpf-map\n"); + return -1; + } + + iov[iovlen++] = (struct iovec){ bpf_rta, bpf_rta->rta_len }; + req.nlh.nlmsg_len += bpf_rta->rta_len; + } +#endif + msg = (struct msghdr) { .msg_name = (void *)&nladdr, .msg_namelen = sizeof(nladdr), @@ -3648,10 +4000,13 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) }; if (sendmsg(fd, &msg, 0) < 0) { + free(bpf_rta); close(fd); return -1; } + free(bpf_rta); + return 0; } @@ -5372,6 +5727,10 @@ static void _usage(FILE *dest) " --tos show tos and priority information\n" " --cgroup show cgroup information\n" " -b, --bpf show bpf filter socket information\n" +#ifdef ENABLE_BPF_SKSTORAGE_SUPPORT +" --bpf-maps show all BPF socket-local storage maps\n" +" --bpf-map-id=MAP-ID show a BPF socket-local storage map\n" +#endif " -E, --events continually display sockets as they are destroyed\n" " -Z, --context display task SELinux security contexts\n" " -z, --contexts display task and socket SELinux security contexts\n" @@ -5395,6 +5754,7 @@ static void _usage(FILE *dest) "\n" " -K, --kill forcibly close sockets, display what was closed\n" " -H, --no-header Suppress header line\n" +" -Q, --no-queues Suppress sending and receiving queue columns\n" " -O, --oneline socket's data printed on a single line\n" " --inet-sockopt show various inet socket options\n" "\n" @@ -5497,6 +5857,9 @@ wrong_state: #define OPT_INET_SOCKOPT 262 +#define OPT_BPF_MAPS 263 +#define OPT_BPF_MAP_ID 264 + static const struct option long_opts[] = { { "numeric", 0, 0, 'n' }, { "resolve", 0, 0, 'r' }, @@ -5538,10 +5901,15 @@ static const struct option long_opts[] = { { "cgroup", 0, 0, OPT_CGROUP }, { "kill", 0, 0, 'K' }, { "no-header", 0, 0, 'H' }, + { "no-queues", 0, 0, 'Q' }, { "xdp", 0, 0, OPT_XDPSOCK}, { "mptcp", 0, 0, 'M' }, { "oneline", 0, 0, 'O' }, { "inet-sockopt", 0, 0, OPT_INET_SOCKOPT }, +#ifdef ENABLE_BPF_SKSTORAGE_SUPPORT + { "bpf-maps", 0, 0, OPT_BPF_MAPS}, + { "bpf-map-id", 1, 0, OPT_BPF_MAP_ID}, +#endif { 0 } }; @@ -5557,7 +5925,7 @@ int main(int argc, char *argv[]) int state_filter = 0; while ((ch = getopt_long(argc, argv, - "dhalBetuwxnro460spTbEf:mMiA:D:F:vVzZN:KHSO", + "dhalBetuwxnro460spTbEf:mMiA:D:F:vVzZN:KHQSO", long_opts, NULL)) != EOF) { switch (ch) { case 'n': @@ -5741,12 +6109,28 @@ int main(int argc, char *argv[]) case 'H': show_header = 0; break; + case 'Q': + show_queues = 0; + break; case 'O': oneline = 1; break; case OPT_INET_SOCKOPT: show_inet_sockopt = 1; break; +#ifdef ENABLE_BPF_SKSTORAGE_SUPPORT + case OPT_BPF_MAPS: + if (bpf_map_opts.nr_maps) { + bpf_map_opts_mixed_error(); + return -1; + } + bpf_map_opts.show_all = true; + break; + case OPT_BPF_MAP_ID: + if (bpf_map_opts_add_id(optarg)) + exit(1); + break; +#endif case 'h': help(); case '?': @@ -5839,6 +6223,11 @@ int main(int argc, char *argv[]) if (!show_processes) columns[COL_PROC].disabled = 1; + if (!show_queues) { + columns[COL_SENDQ].disabled = 1; + columns[COL_RECVQ].disabled = 1; + } + if (!(current_filter.dbs & (current_filter.dbs - 1))) columns[COL_NETID].disabled = 1; @@ -5881,6 +6270,10 @@ int main(int argc, char *argv[]) if (show_processes || show_threads || show_proc_ctx || show_sock_ctx) user_ent_destroy(); +#ifdef ENABLE_BPF_SKSTORAGE_SUPPORT + bpf_map_opts_destroy(); +#endif + render(); return 0; @@ -49,7 +49,7 @@ static int bpf_num_env_entries(void) return num; } -static int parse_bpf(struct exec_util *eu, int argc, char **argv) +static int parse_bpf(const struct exec_util *eu, int argc, char **argv) { char **argv_run = argv_default, **envp_run, *tmp; int ret, i, env_old, env_num, env_map; diff --git a/tc/f_basic.c b/tc/f_basic.c index 1ceb15d..a1db5ba 100644 --- a/tc/f_basic.c +++ b/tc/f_basic.c @@ -32,7 +32,7 @@ static void explain(void) "NOTE: CLASSID is parsed as hexadecimal input.\n"); } -static int basic_parse_opt(struct filter_util *qu, char *handle, +static int basic_parse_opt(const struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { struct tcmsg *t = NLMSG_DATA(n); @@ -103,7 +103,7 @@ static int basic_parse_opt(struct filter_util *qu, char *handle, return 0; } -static int basic_print_opt(struct filter_util *qu, FILE *f, +static int basic_print_opt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_BASIC_MAX+1]; @@ -71,7 +71,7 @@ static const struct bpf_cfg_ops bpf_cb_ops = { .ebpf_cb = bpf_ebpf_cb, }; -static int bpf_parse_opt(struct filter_util *qu, char *handle, +static int bpf_parse_opt(const struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { const char *bpf_obj = NULL, *bpf_uds_name = NULL; @@ -187,7 +187,7 @@ opt_bpf: return ret; } -static int bpf_print_opt(struct filter_util *qu, FILE *f, +static int bpf_print_opt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_BPF_MAX + 1]; diff --git a/tc/f_cgroup.c b/tc/f_cgroup.c index 291d6e7..4aba4ba 100644 --- a/tc/f_cgroup.c +++ b/tc/f_cgroup.c @@ -17,7 +17,7 @@ static void explain(void) fprintf(stderr, " [ action ACTION_SPEC ]\n"); } -static int cgroup_parse_opt(struct filter_util *qu, char *handle, +static int cgroup_parse_opt(const struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { struct tcmsg *t = NLMSG_DATA(n); @@ -75,7 +75,7 @@ static int cgroup_parse_opt(struct filter_util *qu, char *handle, return 0; } -static int cgroup_print_opt(struct filter_util *qu, FILE *f, +static int cgroup_print_opt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_CGROUP_MAX+1]; diff --git a/tc/f_flow.c b/tc/f_flow.c index 4a29af2..07ecb84 100644 --- a/tc/f_flow.c +++ b/tc/f_flow.c @@ -126,7 +126,7 @@ out: return 0; } -static int flow_parse_opt(struct filter_util *fu, char *handle, +static int flow_parse_opt(const struct filter_util *fu, char *handle, int argc, char **argv, struct nlmsghdr *n) { struct tcmsg *t = NLMSG_DATA(n); @@ -273,7 +273,7 @@ static const char *flow_mode2str(__u32 mode) } } -static int flow_print_opt(struct filter_util *fu, FILE *f, struct rtattr *opt, +static int flow_print_opt(const struct filter_util *fu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_FLOW_MAX+1]; diff --git a/tc/f_flower.c b/tc/f_flower.c index 53188f1..cfcd7b2 100644 --- a/tc/f_flower.c +++ b/tc/f_flower.c @@ -1535,7 +1535,7 @@ static int flower_parse_cfm(int *argc_p, char ***argv_p, __be16 eth_type, return 0; } -static int flower_parse_opt(struct filter_util *qu, char *handle, +static int flower_parse_opt(const struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { int ret; @@ -2882,7 +2882,7 @@ static void flower_print_cfm(struct rtattr *attr) close_json_object(); } -static int flower_print_opt(struct filter_util *qu, FILE *f, +static int flower_print_opt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_FLOWER_MAX + 1]; @@ -29,7 +29,7 @@ static void explain(void) " FWMASK is 0xffffffff by default.\n"); } -static int fw_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +static int fw_parse_opt(const struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tail; @@ -112,7 +112,7 @@ static int fw_parse_opt(struct filter_util *qu, char *handle, int argc, char **a return 0; } -static int fw_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +static int fw_print_opt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_FW_MAX+1]; diff --git a/tc/f_matchall.c b/tc/f_matchall.c index 38b68d7..e595ac3 100644 --- a/tc/f_matchall.c +++ b/tc/f_matchall.c @@ -31,7 +31,7 @@ static void explain(void) "NOTE: CLASSID is parsed as hexadecimal input.\n"); } -static int matchall_parse_opt(struct filter_util *qu, char *handle, +static int matchall_parse_opt(const struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { struct tcmsg *t = NLMSG_DATA(n); @@ -107,7 +107,7 @@ static int matchall_parse_opt(struct filter_util *qu, char *handle, return 0; } -static int matchall_print_opt(struct filter_util *qu, FILE *f, +static int matchall_print_opt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_MATCHALL_MAX+1]; diff --git a/tc/f_route.c b/tc/f_route.c index ca8a8dd..87d865b 100644 --- a/tc/f_route.c +++ b/tc/f_route.c @@ -30,7 +30,7 @@ static void explain(void) "NOTE: CLASSID is parsed as hexadecimal input.\n"); } -static int route_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +static int route_parse_opt(const struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tail; @@ -134,7 +134,7 @@ static int route_parse_opt(struct filter_util *qu, char *handle, int argc, char return 0; } -static int route_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +static int route_print_opt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_ROUTE4_MAX+1]; @@ -663,7 +663,7 @@ static int parse_mark(int *argc_p, char ***argv_p, struct nlmsghdr *n) struct tc_u32_mark mark; if (argc <= 1) - return -1; + missarg("mark"); if (get_u32(&mark.val, *argv, 0)) { fprintf(stderr, "Illegal \"mark\" value\n"); @@ -1018,7 +1018,7 @@ static __u32 u32_hash_fold(struct tc_u32_key *key) return ntohl(key->val & key->mask) >> fshift; } -static int u32_parse_opt(struct filter_util *qu, char *handle, +static int u32_parse_opt(const struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { struct { @@ -1232,7 +1232,7 @@ static int u32_parse_opt(struct filter_util *qu, char *handle, return 0; } -static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, +static int u32_print_opt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_U32_MAX + 1]; diff --git a/tc/m_action.c b/tc/m_action.c index f180ba0..36bb59e 100644 --- a/tc/m_action.c +++ b/tc/m_action.c @@ -41,7 +41,7 @@ static void act_usage(void) */ fprintf(stderr, "usage: tc actions <ACTSPECOP>*\n" - "Where: ACTSPECOP := ACR | GD | FL\n" + "Where: ACTSPECOP := ACR | GD | FL\n" " ACR := add | change | replace <ACTSPEC>*\n" " GD := get | delete | <ACTISPEC>*\n" " FL := ls | list | flush | <ACTNAMESPEC>\n" @@ -59,7 +59,7 @@ static void act_usage(void) exit(-1); } -static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt) +static int print_noaopt(const struct action_util *au, FILE *f, struct rtattr *opt) { if (opt && RTA_PAYLOAD(opt)) fprintf(stderr, "[Unknown action, optlen=%u] ", @@ -67,7 +67,7 @@ static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt) return 0; } -static int parse_noaopt(struct action_util *au, int *argc_p, +static int parse_noaopt(const struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n) { int argc = *argc_p; @@ -688,7 +688,16 @@ static int tc_action_gd(int cmd, unsigned int flags, req.n.nlmsg_seq = rth.dump = ++rth.seq; - if (rtnl_talk(&rth, &req.n, cmd == RTM_DELACTION ? NULL : &ans) < 0) { + if (cmd == RTM_DELACTION) { + if (echo_request) + ret = rtnl_echo_talk(&rth, &req.n, json, print_action); + else + ret = rtnl_talk(&rth, &req.n, NULL); + } else { + ret = rtnl_talk(&rth, &req.n, &ans); + } + + if (ret < 0) { fprintf(stderr, "We have an error talking to the kernel\n"); return 1; } @@ -738,7 +747,12 @@ static int tc_action_modify(int cmd, unsigned int flags, } tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; - if (rtnl_talk(&rth, &req.n, NULL) < 0) { + if (echo_request) + ret = rtnl_echo_talk(&rth, &req.n, json, print_action); + else + ret = rtnl_talk(&rth, &req.n, NULL); + + if (ret < 0) { fprintf(stderr, "We have an error talking to the kernel\n"); ret = -1; } @@ -836,7 +850,12 @@ static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event) req.n.nlmsg_type = RTM_DELACTION; req.n.nlmsg_flags |= NLM_F_ROOT; req.n.nlmsg_flags |= NLM_F_REQUEST; - if (rtnl_talk(&rth, &req.n, NULL) < 0) { + + if (echo_request) + ret = rtnl_echo_talk(&rth, &req.n, json, print_action); + else + ret = rtnl_talk(&rth, &req.n, NULL); + if (ret < 0) { fprintf(stderr, "We have an error flushing\n"); return 1; } @@ -69,7 +69,7 @@ static const struct bpf_cfg_ops bpf_cb_ops = { .ebpf_cb = bpf_ebpf_cb, }; -static int bpf_parse_opt(struct action_util *a, int *ptr_argc, char ***ptr_argv, +static int bpf_parse_opt(const struct action_util *a, int *ptr_argc, char ***ptr_argv, int tca_id, struct nlmsghdr *n) { const char *bpf_obj = NULL, *bpf_uds_name = NULL; @@ -151,7 +151,7 @@ opt_bpf: return ret; } -static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg) +static int bpf_print_opt(const struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_ACT_BPF_MAX + 1]; struct tc_act_bpf *parm; diff --git a/tc/m_connmark.c b/tc/m_connmark.c index 8506d95..8b5630f 100644 --- a/tc/m_connmark.c +++ b/tc/m_connmark.c @@ -32,7 +32,7 @@ usage(void) } static int -parse_connmark(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, +parse_connmark(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_connmark sel = {}; @@ -94,7 +94,7 @@ parse_connmark(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, return 0; } -static int print_connmark(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_connmark(const struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_CONNMARK_MAX + 1]; struct tc_connmark *ci; diff --git a/tc/m_csum.c b/tc/m_csum.c index f5fe8f5..21204e5 100644 --- a/tc/m_csum.c +++ b/tc/m_csum.c @@ -81,7 +81,7 @@ parse_csum_args(int *argc_p, char ***argv_p, struct tc_csum *sel) } static int -parse_csum(struct action_util *a, int *argc_p, +parse_csum(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_csum sel = {}; @@ -148,7 +148,7 @@ skip_args: } static int -print_csum(struct action_util *au, FILE *f, struct rtattr *arg) +print_csum(const struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_csum *sel; @@ -225,7 +225,7 @@ static int ct_parse_labels(char *str, struct nlmsghdr *n) } static int -parse_ct(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, +parse_ct(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_ct sel = {}; @@ -488,7 +488,7 @@ static void ct_print_helper(struct rtattr *family, struct rtattr *proto, struct print_string(PRINT_ANY, "helper", " helper %s", helper); } -static int print_ct(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_ct(const struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_CT_MAX + 1]; const char *commit; diff --git a/tc/m_ctinfo.c b/tc/m_ctinfo.c index 996a362..dbd5c0b 100644 --- a/tc/m_ctinfo.c +++ b/tc/m_ctinfo.c @@ -35,7 +35,7 @@ usage(void) } static int -parse_ctinfo(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, +parse_ctinfo(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { unsigned int cpmarkmask = 0, dscpmask = 0, dscpstatemask = 0; @@ -181,7 +181,7 @@ static void print_ctinfo_stats(FILE *f, struct rtattr *tb[TCA_CTINFO_MAX + 1]) rta_getattr_u64(tb[TCA_CTINFO_STATS_CPMARK_SET])); } -static int print_ctinfo(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_ctinfo(const struct action_util *au, FILE *f, struct rtattr *arg) { unsigned int cpmarkmask = ~0, dscpmask = 0, dscpstatemask = 0; struct rtattr *tb[TCA_CTINFO_MAX + 1]; diff --git a/tc/m_gact.c b/tc/m_gact.c index 225ffce..670d59f 100644 --- a/tc/m_gact.c +++ b/tc/m_gact.c @@ -66,7 +66,7 @@ usage(void) } static int -parse_gact(struct action_util *a, int *argc_p, char ***argv_p, +parse_gact(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { int argc = *argc_p; @@ -157,7 +157,7 @@ skip_args: } static int -print_gact(struct action_util *au, FILE *f, struct rtattr *arg) +print_gact(const struct action_util *au, FILE *f, struct rtattr *arg) { #ifdef CONFIG_GACT_PROB struct tc_gact_p *pp = NULL; diff --git a/tc/m_gate.c b/tc/m_gate.c index 37afa42..33ee63b 100644 --- a/tc/m_gate.c +++ b/tc/m_gate.c @@ -56,9 +56,9 @@ static void explain_entry_format(void) fprintf(stderr, "Usage: sched-entry <open | close> <interval> [ <interval ipv> <octets max bytes> ]\n"); } -static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_gate(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); -static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg); +static int print_gate(const struct action_util *au, FILE *f, struct rtattr *arg); struct action_util gate_action_util = { .id = "gate", @@ -135,7 +135,7 @@ static void free_entries(struct list_head *gate_entries) } } -static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_gate(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_gate parm = {.action = TC_ACT_PIPE}; @@ -441,7 +441,7 @@ static int print_gate_list(struct rtattr *list) return 0; } -static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_gate(const struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_gate *parm; struct rtattr *tb[TCA_GATE_MAX + 1]; @@ -44,7 +44,7 @@ static void ife_usage(void) exit(-1); } -static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_ife(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { int argc = *argc_p; @@ -211,7 +211,7 @@ skip_encode: return 0; } -static int print_ife(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_ife(const struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_ife *p; struct rtattr *tb[TCA_IFE_MAX + 1]; diff --git a/tc/m_mirred.c b/tc/m_mirred.c index e5653e6..cfecd59 100644 --- a/tc/m_mirred.c +++ b/tc/m_mirred.c @@ -24,12 +24,16 @@ static void explain(void) { fprintf(stderr, - "Usage: mirred <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME>\n" + "Usage: mirred <DIRECTION> <ACTION> [index INDEX] <TARGET>\n" "where:\n" "\tDIRECTION := <ingress | egress>\n" "\tACTION := <mirror | redirect>\n" "\tINDEX is the specific policy instance id\n" - "\tDEVICENAME is the devicename\n"); + "\tTARGET := <BLOCK | DEVICE>\n" + "\tDEVICE := dev DEVICENAME\n" + "\tDEVICENAME is the devicename\n" + "\tBLOCK := blockid BLOCKID\n" + "\tBLOCKID := 32-bit unsigned block ID\n"); } static void @@ -84,7 +88,7 @@ static const char *mirred_action(int action) } static int -parse_direction(struct action_util *a, int *argc_p, char ***argv_p, +parse_direction(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { @@ -94,6 +98,7 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p, struct tc_mirred p = {}; struct rtattr *tail; char d[IFNAMSIZ] = {}; + __u32 blockid = 0; while (argc > 0) { @@ -162,15 +167,37 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p, TCA_INGRESS_REDIR; p.action = TC_ACT_STOLEN; ok++; - } else if ((redir || mirror) && - matches(*argv, "dev") == 0) { - NEXT_ARG(); - if (strlen(d)) - duparg("dev", *argv); - - strncpy(d, *argv, sizeof(d)-1); - argc--; - argv++; + } else if ((redir || mirror)) { + if (strcmp(*argv, "blockid") == 0) { + if (strlen(d)) { + fprintf(stderr, + "blockid and device are mutually exclusive.\n"); + return -1; + } + NEXT_ARG(); + if (get_u32(&blockid, *argv, 0) || + !blockid) { + fprintf(stderr, + "invalid block ID"); + return -1; + } + argc--; + argv++; + } + if (argc && matches(*argv, "dev") == 0) { + if (blockid) { + fprintf(stderr, + "blockid and device are mutually exclusive.\n"); + return -1; + } + NEXT_ARG(); + if (strlen(d)) + duparg("dev", *argv); + + strncpy(d, *argv, sizeof(d)-1); + argc--; + argv++; + } break; @@ -220,6 +247,8 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p, tail = addattr_nest(n, MAX_MSG, tca_id); addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof(p)); + if (blockid) + addattr32(n, MAX_MSG, TCA_MIRRED_BLOCKID, blockid); addattr_nest_end(n, tail); *argc_p = argc; @@ -229,7 +258,7 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p, static int -parse_mirred(struct action_util *a, int *argc_p, char ***argv_p, +parse_mirred(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { @@ -270,7 +299,7 @@ parse_mirred(struct action_util *a, int *argc_p, char ***argv_p, } static int -print_mirred(struct action_util *au, FILE *f, struct rtattr *arg) +print_mirred(const struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_mirred *p; struct rtattr *tb[TCA_MIRRED_MAX + 1]; @@ -299,7 +328,15 @@ print_mirred(struct action_util *au, FILE *f, struct rtattr *arg) mirred_action(p->eaction)); print_string(PRINT_JSON, "direction", NULL, mirred_direction(p->eaction)); - print_string(PRINT_ANY, "to_dev", " to device %s)", dev); + if (tb[TCA_MIRRED_BLOCKID]) { + const __u32 *blockid = RTA_DATA(tb[TCA_MIRRED_BLOCKID]); + + print_uint(PRINT_ANY, "to_blockid", " to blockid %u)", + *blockid); + } else { + print_string(PRINT_ANY, "to_dev", " to device %s)", dev); + } + print_action_control(f, " ", p->action, ""); print_nl(); diff --git a/tc/m_mpls.c b/tc/m_mpls.c index dda4680..ca3a18a 100644 --- a/tc/m_mpls.c +++ b/tc/m_mpls.c @@ -72,7 +72,7 @@ static bool check_double_action(unsigned int action, const char *arg) return true; } -static int parse_mpls(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_mpls(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_mpls parm = {}; @@ -211,7 +211,7 @@ skip_args: return 0; } -static int print_mpls(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_mpls(const struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_MPLS_MAX + 1]; struct tc_mpls *parm; @@ -76,7 +76,7 @@ bad_val: } static int -parse_nat(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +parse_nat(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_nat sel = {}; @@ -136,7 +136,7 @@ skip_args: } static int -print_nat(struct action_util *au, FILE * f, struct rtattr *arg) +print_nat(const struct action_util *au, FILE * f, struct rtattr *arg) { struct tc_nat *sel; struct rtattr *tb[TCA_NAT_MAX + 1]; diff --git a/tc/m_pedit.c b/tc/m_pedit.c index 32f0341..5c84a90 100644 --- a/tc/m_pedit.c +++ b/tc/m_pedit.c @@ -620,7 +620,7 @@ static int pedit_keys_ex_addattr(struct m_pedit_sel *sel, struct nlmsghdr *n) return 0; } -static int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_pedit(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct m_pedit_sel sel = {}; @@ -745,7 +745,7 @@ static int print_pedit_location(FILE *f, return 0; } -static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_pedit(const struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_pedit_sel *sel; struct rtattr *tb[TCA_PEDIT_MAX + 1]; @@ -771,20 +771,20 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg) sel = RTA_DATA(tb[TCA_PEDIT_PARMS_EX]); if (!tb[TCA_PEDIT_KEYS_EX]) { - fprintf(f, "Netlink error\n"); + fprintf(stderr, "Netlink error\n"); return -1; } keys_ex = calloc(sel->nkeys, sizeof(*keys_ex)); if (!keys_ex) { - fprintf(f, "Out of memory\n"); + fprintf(stderr, "Out of memory\n"); return -1; } err = pedit_keys_ex_getattr(tb[TCA_PEDIT_KEYS_EX], keys_ex, sel->nkeys); if (err) { - fprintf(f, "Netlink error\n"); + fprintf(stderr, "Netlink error\n"); free(keys_ex); return -1; diff --git a/tc/m_police.c b/tc/m_police.c index 46c39a8..8d6887e 100644 --- a/tc/m_police.c +++ b/tc/m_police.c @@ -17,9 +17,9 @@ #include "utils.h" #include "tc_util.h" -static int act_parse_police(struct action_util *a, int *argc_p, +static int act_parse_police(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); -static int print_police(struct action_util *a, FILE *f, struct rtattr *tb); +static int print_police(const struct action_util *a, FILE *f, struct rtattr *tb); struct action_util police_action_util = { .id = "police", @@ -42,7 +42,7 @@ static void usage(void) exit(-1); } -static int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p, +static int act_parse_police(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { int argc = *argc_p; @@ -260,7 +260,7 @@ int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) return act_parse_police(NULL, argc_p, argv_p, tca_id, n); } -static int print_police(struct action_util *a, FILE *f, struct rtattr *arg) +static int print_police(const struct action_util *a, FILE *f, struct rtattr *arg) { SPRINT_BUF(b2); struct tc_police *p; diff --git a/tc/m_sample.c b/tc/m_sample.c index 769de14..3baf1d5 100644 --- a/tc/m_sample.c +++ b/tc/m_sample.c @@ -31,7 +31,7 @@ static void usage(void) exit(-1); } -static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_sample(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_sample p = { 0 }; @@ -45,11 +45,8 @@ static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p, __u32 group; __u32 rate; - if (argc <= 1) { - fprintf(stderr, "sample bad argument count %d\n", argc); - usage(); - return -1; - } + if (argc <= 1) + missarg("sample count"); if (matches(*argv, "sample") == 0) { NEXT_ARG(); @@ -133,7 +130,7 @@ static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p, return 0; } -static int print_sample(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_sample(const struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_SAMPLE_MAX + 1]; struct tc_sample *p; diff --git a/tc/m_simple.c b/tc/m_simple.c index fe2bca2..55f3444 100644 --- a/tc/m_simple.c +++ b/tc/m_simple.c @@ -90,7 +90,7 @@ static void usage(void) } static int -parse_simple(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, +parse_simple(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_defact sel = {}; @@ -155,7 +155,7 @@ parse_simple(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, return 0; } -static int print_simple(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_simple(const struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_defact *sel; struct rtattr *tb[TCA_DEF_MAX + 1]; @@ -179,9 +179,11 @@ static int print_simple(struct action_util *au, FILE *f, struct rtattr *arg) simpdata = RTA_DATA(tb[TCA_DEF_DATA]); - fprintf(f, "Simple <%s>\n", simpdata); - fprintf(f, "\t index %u ref %d bind %d", sel->index, - sel->refcnt, sel->bindcnt); + print_string(PRINT_ANY, "simple", "Simple <%s>", simpdata); + print_nl(); + print_uint(PRINT_ANY, "index", "\t index %u ", sel->index); + print_int(PRINT_ANY, "ref", "ref %d ", sel->refcnt); + print_int(PRINT_ANY, "bind","bind %d", sel->bindcnt); if (show_stats) { if (tb[TCA_DEF_TM]) { diff --git a/tc/m_skbedit.c b/tc/m_skbedit.c index d55a612..b55c324 100644 --- a/tc/m_skbedit.c +++ b/tc/m_skbedit.c @@ -41,7 +41,7 @@ usage(void) } static int -parse_skbedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, +parse_skbedit(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { int argc = *argc_p; @@ -177,7 +177,7 @@ parse_skbedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, return 0; } -static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_skbedit(const struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_SKBEDIT_MAX + 1]; diff --git a/tc/m_skbmod.c b/tc/m_skbmod.c index b1c8d00..af64e99 100644 --- a/tc/m_skbmod.c +++ b/tc/m_skbmod.c @@ -40,7 +40,7 @@ static void skbmod_usage(void) exit(-1); } -static int parse_skbmod(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_skbmod(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { int argc = *argc_p; @@ -156,7 +156,7 @@ static int parse_skbmod(struct action_util *a, int *argc_p, char ***argv_p, return 0; } -static int print_skbmod(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_skbmod(const struct action_util *au, FILE *f, struct rtattr *arg) { struct tc_skbmod *p; struct rtattr *tb[TCA_SKBMOD_MAX + 1]; @@ -177,43 +177,48 @@ static int print_skbmod(struct action_util *au, FILE *f, struct rtattr *arg) p = RTA_DATA(tb[TCA_SKBMOD_PARMS]); - fprintf(f, "skbmod "); + print_string(PRINT_FP, NULL, "skbmod ", NULL); print_action_control(f, "", p->action, " "); if (tb[TCA_SKBMOD_ETYPE]) { skbmod_etype = rta_getattr_u16(tb[TCA_SKBMOD_ETYPE]); has_optional = 1; - fprintf(f, "set etype 0x%X ", skbmod_etype); + print_0xhex(PRINT_ANY, "etype", "set etype 0x%X ", skbmod_etype); } if (has_optional) - fprintf(f, "\n\t "); + print_string(PRINT_FP, NULL, "%s\t ", _SL_); if (tb[TCA_SKBMOD_DMAC]) { has_optional = 1; - fprintf(f, "set dmac %s ", - ll_addr_n2a(RTA_DATA(tb[TCA_SKBMOD_DMAC]), - RTA_PAYLOAD(tb[TCA_SKBMOD_DMAC]), 0, b1, - sizeof(b1))); + print_color_string(PRINT_ANY, COLOR_MAC, "dmac", + "set dmac %s ", + ll_addr_n2a(RTA_DATA(tb[TCA_SKBMOD_DMAC]), + RTA_PAYLOAD(tb[TCA_SKBMOD_DMAC]), 0, b1, + sizeof(b1))); } if (tb[TCA_SKBMOD_SMAC]) { has_optional = 1; - fprintf(f, "set smac %s ", - ll_addr_n2a(RTA_DATA(tb[TCA_SKBMOD_SMAC]), - RTA_PAYLOAD(tb[TCA_SKBMOD_SMAC]), 0, b2, - sizeof(b2))); + print_color_string(PRINT_ANY, COLOR_MAC, "smac", + "set smac %s ", + ll_addr_n2a(RTA_DATA(tb[TCA_SKBMOD_SMAC]), + RTA_PAYLOAD(tb[TCA_SKBMOD_SMAC]), 0, b2, + sizeof(b2))); } if (p->flags & SKBMOD_F_SWAPMAC) - fprintf(f, "swap mac "); + print_null(PRINT_ANY, "swapmac", "swap mac ", NULL); if (p->flags & SKBMOD_F_ECN) - fprintf(f, "ecn "); + print_null(PRINT_ANY, "ecn", "ecn ", NULL); + + print_nl(); + print_uint(PRINT_ANY, "index", "\t index %u ", p->index); + print_int(PRINT_ANY, "ref", "ref %d ", p->refcnt); + print_int(PRINT_ANY, "bind", "bind %d", p->bindcnt); - fprintf(f, "\n\t index %u ref %d bind %d", p->index, p->refcnt, - p->bindcnt); if (show_stats) { if (tb[TCA_SKBMOD_TM]) { struct tcf_t *tm = RTA_DATA(tb[TCA_SKBMOD_TM]); @@ -222,7 +227,7 @@ static int print_skbmod(struct action_util *au, FILE *f, struct rtattr *arg) } } - fprintf(f, "\n"); + print_string(PRINT_FP, NULL, "\n", NULL); return 0; } diff --git a/tc/m_tunnel_key.c b/tc/m_tunnel_key.c index ff699cc..2032a72 100644 --- a/tc/m_tunnel_key.c +++ b/tc/m_tunnel_key.c @@ -311,7 +311,7 @@ static int tunnel_key_parse_tos_ttl(char *str, int type, struct nlmsghdr *n) return 0; } -static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_tunnel_key(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { struct tc_tunnel_key parm = {}; @@ -688,7 +688,7 @@ static void tunnel_key_print_tos_ttl(FILE *f, char *name, } } -static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_tunnel_key(const struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_TUNNEL_KEY_MAX + 1]; struct tc_tunnel_key *parm; diff --git a/tc/m_vlan.c b/tc/m_vlan.c index c1dc8b4..40d62fa 100644 --- a/tc/m_vlan.c +++ b/tc/m_vlan.c @@ -56,7 +56,7 @@ static void unexpected(const char *arg) explain(); } -static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p, +static int parse_vlan(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { int argc = *argc_p; @@ -227,7 +227,7 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p, return 0; } -static int print_vlan(struct action_util *au, FILE *f, struct rtattr *arg) +static int print_vlan(const struct action_util *au, FILE *f, struct rtattr *arg) { SPRINT_BUF(b1); struct rtattr *tb[TCA_VLAN_MAX + 1]; diff --git a/tc/q_cake.c b/tc/q_cake.c index c438b76..e2b8de5 100644 --- a/tc/q_cake.c +++ b/tc/q_cake.c @@ -88,7 +88,7 @@ static void explain(void) " (* marks defaults)\n"); } -static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int cake_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct cake_preset *preset, *preset_set = NULL; @@ -415,7 +415,7 @@ static void cake_print_mode(unsigned int value, unsigned int max, } } -static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int cake_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_CAKE_MAX + 1]; unsigned int interval = 0; @@ -614,7 +614,7 @@ static void cake_print_json_tin(struct rtattr **tstat) #undef PRINT_TSTAT_JSON } -static int cake_print_xstats(struct qdisc_util *qu, FILE *f, +static int cake_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct rtattr *st[TCA_CAKE_STATS_MAX + 1]; @@ -29,7 +29,7 @@ static void explain1(const char *arg, const char *val) fprintf(stderr, "cbs: illegal value for \"%s\": \"%s\"\n", arg, val); } -static int cbs_parse_opt(struct qdisc_util *qu, int argc, +static int cbs_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_cbs_qopt opt = {}; @@ -103,7 +103,7 @@ static int cbs_parse_opt(struct qdisc_util *qu, int argc, return 0; } -static int cbs_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int cbs_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_CBS_MAX+1]; struct tc_cbs_qopt *qopt; diff --git a/tc/q_choke.c b/tc/q_choke.c index 7653eb7..a16f5f6 100644 --- a/tc/q_choke.c +++ b/tc/q_choke.c @@ -27,7 +27,7 @@ static void explain(void) " [ min PACKETS ] [ max PACKETS ] [ burst PACKETS ]\n"); } -static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int choke_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_red_qopt opt = {}; @@ -162,7 +162,7 @@ static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int choke_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int choke_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_CHOKE_MAX+1]; const struct tc_red_qopt *qopt; @@ -203,7 +203,7 @@ static int choke_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int choke_print_xstats(struct qdisc_util *qu, FILE *f, +static int choke_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_choke_xstats *st; diff --git a/tc/q_clsact.c b/tc/q_clsact.c index 341f653..5bd9eb2 100644 --- a/tc/q_clsact.c +++ b/tc/q_clsact.c @@ -10,7 +10,7 @@ static void explain(void) fprintf(stderr, "Usage: ... clsact\n"); } -static int clsact_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int clsact_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { if (argc > 0) { @@ -22,7 +22,7 @@ static int clsact_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int clsact_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int clsact_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { return 0; } diff --git a/tc/q_codel.c b/tc/q_codel.c index 03b6f92..15029b4 100644 --- a/tc/q_codel.c +++ b/tc/q_codel.c @@ -28,7 +28,7 @@ static void explain(void) " [ ce_threshold TIME ]\n"); } -static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int codel_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { unsigned int limit = 0; @@ -95,7 +95,7 @@ static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int codel_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_CODEL_MAX + 1]; unsigned int limit; @@ -147,7 +147,7 @@ static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int codel_print_xstats(struct qdisc_util *qu, FILE *f, +static int codel_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_codel_xstats _st = {}, *st; @@ -28,7 +28,7 @@ static void explain2(void) } -static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int drr_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { while (argc) { @@ -44,7 +44,7 @@ static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int drr_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, +static int drr_parse_class_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct rtattr *tail; @@ -75,7 +75,7 @@ static int drr_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int drr_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int drr_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_DRR_MAX + 1]; @@ -90,7 +90,7 @@ static int drr_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int drr_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +static int drr_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_drr_stats *x; @@ -39,7 +39,7 @@ static void explain_clockid(const char *val) val); } -static int etf_parse_opt(struct qdisc_util *qu, int argc, +static int etf_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_etf_qopt opt = { @@ -107,7 +107,7 @@ static int etf_parse_opt(struct qdisc_util *qu, int argc, return 0; } -static int etf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int etf_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_ETF_MAX+1]; struct tc_etf_qopt *qopt; @@ -57,7 +57,7 @@ static int parse_nbands(const char *arg, __u8 *pnbands, const char *what) return 0; } -static int ets_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int ets_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { __u8 nbands = 0; @@ -182,7 +182,7 @@ parse_priomap: return 0; } -static int ets_parse_copt(struct qdisc_util *qu, int argc, char **argv, +static int ets_parse_copt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { unsigned int quantum = 0; @@ -276,7 +276,7 @@ static int ets_print_opt_priomap(struct rtattr *opt) return 0; } -static int ets_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int ets_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_ETS_MAX + 1]; __u8 nbands; @@ -310,7 +310,7 @@ static int ets_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return ets_print_opt_priomap(tb[TCA_ETS_PRIOMAP]); } -static int ets_print_copt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int ets_print_copt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_ETS_MAX + 1]; __u32 quantum; diff --git a/tc/q_fifo.c b/tc/q_fifo.c index 9b2c534..489208d 100644 --- a/tc/q_fifo.c +++ b/tc/q_fifo.c @@ -22,7 +22,7 @@ static void explain(void) fprintf(stderr, "Usage: ... <[p|b]fifo | pfifo_head_drop> [ limit NUMBER ]\n"); } -static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int fifo_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int ok = 0; @@ -52,7 +52,7 @@ static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int fifo_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int fifo_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct tc_fifo_qopt *qopt; @@ -47,7 +47,7 @@ static unsigned int ilog2(unsigned int val) return res; } -static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int fq_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_prio_qopt prio2band; @@ -337,7 +337,7 @@ static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int fq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int fq_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_FQ_MAX + 1]; unsigned int plimit, flow_plimit; @@ -490,7 +490,7 @@ static int fq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int fq_print_xstats(struct qdisc_util *qu, FILE *f, +static int fq_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_fq_qd_stats *st, _st; diff --git a/tc/q_fq_codel.c b/tc/q_fq_codel.c index 9c9d7bc..a619d2b 100644 --- a/tc/q_fq_codel.c +++ b/tc/q_fq_codel.c @@ -29,7 +29,7 @@ static void explain(void) "[ drop_batch SIZE ]\n"); } -static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int fq_codel_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { unsigned int drop_batch = 0; @@ -157,7 +157,7 @@ static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int fq_codel_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_FQ_CODEL_MAX + 1]; unsigned int limit; @@ -250,7 +250,7 @@ static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt return 0; } -static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f, +static int fq_codel_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_fq_codel_xstats _st = {}, *st; diff --git a/tc/q_fq_pie.c b/tc/q_fq_pie.c index 9cbef47..dc2710c 100644 --- a/tc/q_fq_pie.c +++ b/tc/q_fq_pie.c @@ -36,7 +36,7 @@ static void explain(void) #define ALPHA_MAX 32 #define BETA_MAX 32 -static int fq_pie_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int fq_pie_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { unsigned int limit = 0; @@ -172,7 +172,7 @@ static int fq_pie_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int fq_pie_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int fq_pie_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_FQ_PIE_MAX + 1]; unsigned int limit = 0; @@ -269,7 +269,7 @@ static int fq_pie_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int fq_pie_print_xstats(struct qdisc_util *qu, FILE *f, +static int fq_pie_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_fq_pie_xstats _st = {}, *st; diff --git a/tc/q_gred.c b/tc/q_gred.c index f6a3f05..84fc912 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -39,7 +39,7 @@ static void explain(void) " [ probability PROBABILITY ] [ bandwidth KBPS ] [ecn] [harddrop]\n"); } -static int init_gred(struct qdisc_util *qu, int argc, char **argv, +static int init_gred(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { @@ -115,7 +115,7 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv, /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ -static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) +static int gred_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct rtattr *tail, *entry, *vqs; int ok = 0; @@ -406,7 +406,7 @@ gred_print_stats(struct tc_gred_info *info, struct tc_gred_qopt *qopt) print_size(PRINT_ANY, "bytes", "(%s) ", bytes); } -static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int gred_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct tc_gred_info infos[MAX_DPs] = {}; struct rtattr *tb[TCA_GRED_MAX + 1]; diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c index 609d925..aed7130 100644 --- a/tc/q_hfsc.c +++ b/tc/q_hfsc.c @@ -65,7 +65,7 @@ explain1(char *arg) } static int -hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, +hfsc_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_hfsc_qopt qopt = {}; @@ -97,7 +97,7 @@ hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, } static int -hfsc_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +hfsc_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct tc_hfsc_qopt *qopt; @@ -114,7 +114,7 @@ hfsc_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) } static int -hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +hfsc_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_hfsc_stats *st; @@ -136,7 +136,7 @@ hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) } static int -hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, +hfsc_parse_class_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_service_curve rsc = {}, fsc = {}, usc = {}; @@ -220,7 +220,7 @@ hfsc_print_sc(FILE *f, char *name, struct tc_service_curve *sc) } static int -hfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +hfsc_print_class_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_HFSC_MAX+1]; struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL; @@ -26,7 +26,7 @@ static void explain(void) " [ non_hh_weight NUMBER ]\n"); } -static int hhf_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int hhf_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { unsigned int limit = 0; @@ -117,7 +117,7 @@ static int hhf_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int hhf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int hhf_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_HHF_MAX + 1]; unsigned int limit; @@ -179,7 +179,7 @@ static int hhf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int hhf_print_xstats(struct qdisc_util *qu, FILE *f, +static int hhf_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_hhf_xstats *st; @@ -53,7 +53,7 @@ static void explain1(char *arg) explain(); } -static int htb_parse_opt(struct qdisc_util *qu, int argc, +static int htb_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { unsigned int direct_qlen = ~0U; @@ -107,7 +107,7 @@ static int htb_parse_opt(struct qdisc_util *qu, int argc, return 0; } -static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, +static int htb_parse_class_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_htb_opt opt = {}; @@ -263,7 +263,7 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int htb_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_HTB_MAX + 1]; struct tc_htb_opt *hopt; @@ -354,7 +354,7 @@ static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int htb_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +static int htb_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_htb_xstats *st; diff --git a/tc/q_ingress.c b/tc/q_ingress.c index 3df4914..294b005 100644 --- a/tc/q_ingress.c +++ b/tc/q_ingress.c @@ -16,7 +16,7 @@ static void explain(void) fprintf(stderr, "Usage: ... ingress\n"); } -static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int ingress_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { while (argc > 0) { @@ -33,7 +33,7 @@ static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int ingress_print_opt(struct qdisc_util *qu, FILE *f, +static int ingress_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { print_string(PRINT_FP, NULL, "---------------- ", NULL); diff --git a/tc/q_mqprio.c b/tc/q_mqprio.c index 7a4417f..493c6eb 100644 --- a/tc/q_mqprio.c +++ b/tc/q_mqprio.c @@ -46,7 +46,7 @@ static void add_tc_entries(struct nlmsghdr *n, __u32 fp[TC_QOPT_MAX_QUEUE], } } -static int mqprio_parse_opt(struct qdisc_util *qu, int argc, +static int mqprio_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int idx; @@ -314,7 +314,7 @@ static void dump_tc_entries(FILE *f, struct rtattr *opt, int len) } } -static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int mqprio_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { int i; struct tc_mqprio_qopt *qopt; diff --git a/tc/q_multiq.c b/tc/q_multiq.c index b1e6c9a..63fffed 100644 --- a/tc/q_multiq.c +++ b/tc/q_multiq.c @@ -28,7 +28,7 @@ static void explain(void) fprintf(stderr, "Usage: ... multiq [help]\n"); } -static int multiq_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int multiq_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_multiq_qopt opt = {}; @@ -48,7 +48,7 @@ static int multiq_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int multiq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int multiq_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct tc_multiq_qopt *qopt; diff --git a/tc/q_netem.c b/tc/q_netem.c index 4ce9ab6..90b2613 100644 --- a/tc/q_netem.c +++ b/tc/q_netem.c @@ -170,27 +170,8 @@ static int get_distribution(const char *type, __s16 *data, int maxdata) #define NEXT_IS_SIGNED_NUMBER() \ (NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-')) -/* - * Adjust for the fact that psched_ticks aren't always usecs - * (based on kernel PSCHED_CLOCK configuration - */ -static int get_ticks(__u32 *ticks, const char *str) -{ - unsigned int t; - - if (get_time(&t, str)) - return -1; - - if (tc_core_time2big(t)) { - fprintf(stderr, "Illegal %u time (too large)\n", t); - return -1; - } - *ticks = tc_core_time2tick(t); - return 0; -} - -static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int netem_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int dist_size = 0; @@ -208,6 +189,8 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, __s16 *slot_dist_data = NULL; __u16 loss_type = NETEM_LOSS_UNSPEC; int present[__TCA_NETEM_MAX] = {}; + __s64 latency64 = 0; + __s64 jitter64 = 0; __u64 rate64 = 0; __u64 seed = 0; @@ -221,14 +204,20 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, } else if (matches(*argv, "latency") == 0 || matches(*argv, "delay") == 0) { NEXT_ARG(); - if (get_ticks(&opt.latency, *argv)) { + + /* Old latency value in opt is no longer used. */ + present[TCA_NETEM_LATENCY64] = 1; + + if (get_time64(&latency64, *argv)) { explain1("latency"); return -1; } if (NEXT_IS_NUMBER()) { NEXT_ARG(); - if (get_ticks(&opt.jitter, *argv)) { + + present[TCA_NETEM_JITTER64] = 1; + if (get_time64(&jitter64, *argv)) { explain1("latency"); return -1; } @@ -552,7 +541,7 @@ random_loss_model: tail = NLMSG_TAIL(n); if (reorder.probability) { - if (opt.latency == 0) { + if (latency64 == 0) { fprintf(stderr, "reordering not possible without specifying some delay\n"); explain(); return -1; @@ -573,7 +562,7 @@ random_loss_model: } } - if (dist_data && (opt.latency == 0 || opt.jitter == 0)) { + if (dist_data && (latency64 == 0 || jitter64 == 0)) { fprintf(stderr, "distribution specified but no latency and jitter values\n"); explain(); return -1; @@ -582,6 +571,14 @@ random_loss_model: if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0) return -1; + if (present[TCA_NETEM_LATENCY64] && + addattr_l(n, 1024, TCA_NETEM_LATENCY64, &latency64, sizeof(latency64)) < 0) + return -1; + + if (present[TCA_NETEM_JITTER64] && + addattr_l(n, 1024, TCA_NETEM_JITTER64, &jitter64, sizeof(jitter64)) < 0) + return -1; + if (present[TCA_NETEM_CORR] && addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0) return -1; @@ -661,7 +658,7 @@ random_loss_model: return 0; } -static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int netem_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { const struct tc_netem_corr *cor = NULL; const struct tc_netem_reorder *reorder = NULL; @@ -676,6 +673,8 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) __u64 seed = 0; int len; __u64 rate64 = 0; + __u64 latency64 = 0; + __u64 jitter64 = 0; SPRINT_BUF(b1); @@ -734,6 +733,18 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return -1; rate64 = rta_getattr_u64(tb[TCA_NETEM_RATE64]); } + if (tb[TCA_NETEM_LATENCY64]) { + if (RTA_PAYLOAD(tb[TCA_NETEM_LATENCY64]) < sizeof(latency64)) + return -1; + latency64 = rta_getattr_u64(tb[TCA_NETEM_LATENCY64]); + + } + if (tb[TCA_NETEM_JITTER64]) { + if (RTA_PAYLOAD(tb[TCA_NETEM_JITTER64]) < sizeof(jitter64)) + return -1; + jitter64 = rta_getattr_u64(tb[TCA_NETEM_JITTER64]); + + } if (tb[TCA_NETEM_SLOT]) { if (RTA_PAYLOAD(tb[TCA_NETEM_SLOT]) < sizeof(*slot)) return -1; @@ -749,24 +760,23 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) print_uint(PRINT_ANY, "limit", "limit %d", qopt.limit); - if (qopt.latency) { + + if (latency64 != 0) { open_json_object("delay"); - if (!is_json_context()) { - print_string(PRINT_FP, NULL, " delay %s", - sprint_ticks(qopt.latency, b1)); - if (qopt.jitter) - print_string(PRINT_FP, NULL, " %s", - sprint_ticks(qopt.jitter, b1)); - } else { + if (is_json_context()) { print_float(PRINT_JSON, "delay", NULL, - tc_core_tick2time(qopt.latency) / - 1000000.); + (double)latency64 / 1000000000.); print_float(PRINT_JSON, "jitter", NULL, - tc_core_tick2time(qopt.jitter) / - 1000000.); + (double)jitter64 / 1000000000.); + } else { + print_string(PRINT_FP, NULL, " delay %s", + sprint_time64(latency64, b1)); + if (jitter64 != 0) + print_string(PRINT_FP, NULL, " %s", + sprint_time64(jitter64, b1)); } - print_corr(qopt.jitter && cor && cor->delay_corr, + print_corr(jitter64 && cor && cor->delay_corr, cor ? cor->delay_corr : 0); close_json_object(); } @@ -30,7 +30,7 @@ static void explain(void) #define ALPHA_MAX 32 #define BETA_MAX 32 -static int pie_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int pie_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { unsigned int limit = 0; @@ -124,7 +124,7 @@ static int pie_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int pie_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int pie_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_PIE_MAX + 1]; unsigned int limit; @@ -198,7 +198,7 @@ static int pie_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int pie_print_xstats(struct qdisc_util *qu, FILE *f, +static int pie_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_pie_xstats *st; diff --git a/tc/q_plug.c b/tc/q_plug.c index 8adf9b9..257735a 100644 --- a/tc/q_plug.c +++ b/tc/q_plug.c @@ -22,7 +22,7 @@ static void explain(void) fprintf(stderr, "Usage: ... plug [block | release | release_indefinite | limit NUMBER]\n"); } -static int plug_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int plug_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_plug_qopt opt = {}; @@ -62,7 +62,7 @@ static int plug_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int plug_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int plug_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { /* dummy implementation as sch_plug does not implement a dump op */ return 0; diff --git a/tc/q_prio.c b/tc/q_prio.c index a3781ff..41bd98a 100644 --- a/tc/q_prio.c +++ b/tc/q_prio.c @@ -22,7 +22,7 @@ static void explain(void) fprintf(stderr, "Usage: ... prio bands NUMBER priomap P1 P2...[multiqueue]\n"); } -static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int prio_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int pmap_mode = 0; @@ -89,7 +89,7 @@ static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +int prio_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { int i; struct tc_prio_qopt *qopt; @@ -30,7 +30,7 @@ static void explain_class(void) fprintf(stderr, "Usage: ... qfq weight NUMBER maxpkt BYTES\n"); } -static int qfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int qfq_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { if (argc > 0) { @@ -43,7 +43,7 @@ static int qfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int qfq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, +static int qfq_parse_class_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct rtattr *tail; @@ -80,7 +80,7 @@ static int qfq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int qfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int qfq_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_QFQ_MAX + 1]; @@ -40,7 +40,7 @@ static struct qevent_util qevents[] = { {}, }; -static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int red_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct nla_bitfield32 flags_bf = { @@ -180,7 +180,7 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int red_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int red_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_RED_MAX + 1]; struct nla_bitfield32 *flags_bf; @@ -232,7 +232,7 @@ static int red_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int red_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +static int red_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { #ifdef TC_RED_ECN struct tc_red_xstats *st; @@ -252,7 +252,7 @@ static int red_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstat return 0; } -static int red_has_block(struct qdisc_util *qu, struct rtattr *opt, __u32 block_idx, bool *p_has) +static int red_has_block(const struct qdisc_util *qu, struct rtattr *opt, __u32 block_idx, bool *p_has) { struct rtattr *tb[TCA_RED_MAX + 1]; @@ -40,7 +40,7 @@ static int get_prob(__u32 *val, const char *arg) return 0; } -static int sfb_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int sfb_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_sfb_qopt opt = { @@ -131,7 +131,7 @@ static int sfb_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int sfb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int sfb_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[__TCA_SFB_MAX]; struct tc_sfb_qopt *qopt; @@ -173,7 +173,7 @@ static int sfb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int sfb_print_xstats(struct qdisc_util *qu, FILE *f, +static int sfb_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_sfb_xstats *st; @@ -30,7 +30,7 @@ static void explain(void) " [ ecn ] [ harddrop ]\n"); } -static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) +static int sfq_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int ok = 0, red = 0; struct tc_sfq_qopt_v1 opt = {}; @@ -196,7 +196,7 @@ static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl return 0; } -static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int sfq_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct tc_sfq_qopt *qopt; struct tc_sfq_qopt_v1 *qopt_ext = NULL; @@ -255,7 +255,7 @@ static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int sfq_print_xstats(struct qdisc_util *qu, FILE *f, +static int sfq_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct tc_sfq_xstats *st; diff --git a/tc/q_skbprio.c b/tc/q_skbprio.c index b0ba180..910ea99 100644 --- a/tc/q_skbprio.c +++ b/tc/q_skbprio.c @@ -23,7 +23,7 @@ static void explain(void) fprintf(stderr, "Usage: ... <skbprio> [ limit NUMBER ]\n"); } -static int skbprio_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int skbprio_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int ok = 0; @@ -58,7 +58,7 @@ static int skbprio_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int skbprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int skbprio_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct tc_skbprio_qopt *qopt; diff --git a/tc/q_taprio.c b/tc/q_taprio.c index c47fe24..416a222 100644 --- a/tc/q_taprio.c +++ b/tc/q_taprio.c @@ -134,7 +134,7 @@ static void add_tc_entries(struct nlmsghdr *n, __u32 max_sdu[TC_QOPT_MAX_QUEUE], } } -static int taprio_parse_opt(struct qdisc_util *qu, int argc, +static int taprio_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { __u32 max_sdu[TC_QOPT_MAX_QUEUE] = { }; @@ -545,7 +545,7 @@ static void dump_tc_entries(FILE *f, struct rtattr *opt) } } -static int taprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int taprio_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_TAPRIO_ATTR_MAX + 1]; struct tc_mqprio_qopt *qopt = 0; @@ -623,7 +623,7 @@ static int taprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return 0; } -static int taprio_print_xstats(struct qdisc_util *qu, FILE *f, +static int taprio_print_xstats(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats) { struct rtattr *tb[TCA_TAPRIO_OFFLOAD_STATS_MAX + 1], *nla; @@ -31,7 +31,7 @@ static void explain1(const char *arg, const char *val) } -static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, +static int tbf_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { struct tc_tbf_qopt opt = {}; @@ -245,7 +245,7 @@ static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +static int tbf_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_TBF_MAX+1]; struct tc_tbf_qopt *qopt; @@ -38,6 +38,8 @@ int json; int oneline; int brief; +int echo_request; + static char *conf_file; struct rtnl_handle rth; @@ -46,7 +48,7 @@ static void *BODY; /* cached handle dlopen(NULL) */ static struct qdisc_util *qdisc_list; static struct filter_util *filter_list; -static int print_noqopt(struct qdisc_util *qu, FILE *f, +static int print_noqopt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) { if (opt && RTA_PAYLOAD(opt)) @@ -55,7 +57,7 @@ static int print_noqopt(struct qdisc_util *qu, FILE *f, return 0; } -static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, +static int parse_noqopt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { if (argc) { @@ -67,7 +69,7 @@ static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, return 0; } -static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle) +static int print_nofopt(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle) { if (opt && RTA_PAYLOAD(opt)) fprintf(f, "fh %08x [Unknown filter, optlen=%u] ", @@ -77,7 +79,7 @@ static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u return 0; } -static int parse_nofopt(struct filter_util *qu, char *fhandle, +static int parse_nofopt(const struct filter_util *qu, char *fhandle, int argc, char **argv, struct nlmsghdr *n) { __u32 handle; @@ -100,7 +102,7 @@ static int parse_nofopt(struct filter_util *qu, char *fhandle, return 0; } -struct qdisc_util *get_qdisc_kind(const char *str) +const struct qdisc_util *get_qdisc_kind(const char *str) { void *dlh; char buf[256]; @@ -144,7 +146,7 @@ noexist: } -struct filter_util *get_filter_kind(const char *str) +const struct filter_util *get_filter_kind(const char *str) { void *dlh; char buf[256]; @@ -196,7 +198,7 @@ static void usage(void) " -o[neline] | -j[son] | -p[retty] | -c[olor]\n" " -b[atch] [filename] | -n[etns] name | -N[umeric] |\n" " -nm | -nam[es] | { -cf | -conf } path\n" - " -br[ief] }\n"); + " -br[ief] | -echo }\n"); } static int do_cmd(int argc, char **argv) @@ -287,7 +289,7 @@ int main(int argc, char **argv) } else if (matches(argv[1], "-batch") == 0) { argc--; argv++; if (argc <= 1) - usage(); + missarg("batch file"); batch_file = argv[1]; } else if (matches(argv[1], "-netns") == 0) { NEXT_ARG(); @@ -314,6 +316,8 @@ int main(int argc, char **argv) ++oneline; } else if (matches(argv[1], "-brief") == 0) { ++brief; + } else if (strcmp(argv[1], "-echo") == 0) { + ++echo_request; } else { fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", diff --git a/tc/tc_class.c b/tc/tc_class.c index f6a3d13..6d707d8 100644 --- a/tc/tc_class.c +++ b/tc/tc_class.c @@ -61,7 +61,7 @@ static int tc_class_modify(int cmd, unsigned int flags, int argc, char **argv) .n.nlmsg_type = cmd, .t.tcm_family = AF_UNSPEC, }; - struct qdisc_util *q = NULL; + const struct qdisc_util *q = NULL; struct tc_estimator est = {}; char d[IFNAMSIZ] = {}; char k[FILTER_NAMESZ] = {}; @@ -213,7 +213,7 @@ static void graph_cls_show(FILE *fp, char *buf, struct hlist_head *root_list, struct hlist_node *n, *tmp_cls; char cls_id_str[256] = {}; struct rtattr *tb[TCA_MAX + 1]; - struct qdisc_util *q; + const struct qdisc_util *q; char str[300] = {}; hlist_for_each_safe(n, tmp_cls, root_list) { @@ -298,7 +298,7 @@ int print_class(struct nlmsghdr *n, void *arg) struct tcmsg *t = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[TCA_MAX + 1]; - struct qdisc_util *q; + const struct qdisc_util *q; char abuf[256]; if (n->nlmsg_type != RTM_NEWTCLASS && n->nlmsg_type != RTM_DELTCLASS) { diff --git a/tc/tc_core.c b/tc/tc_core.c index 871ceb4..37547e9 100644 --- a/tc/tc_core.c +++ b/tc/tc_core.c @@ -23,23 +23,14 @@ static double tick_in_usec = 1; static double clock_factor = 1; -int tc_core_time2big(unsigned int time) +static unsigned int tc_core_time2tick(unsigned int time) { - __u64 t = time; - - t *= tick_in_usec; - return (t >> 32) != 0; -} - - -unsigned int tc_core_time2tick(unsigned int time) -{ - return time*tick_in_usec; + return time * tick_in_usec; } unsigned int tc_core_tick2time(unsigned int tick) { - return tick/tick_in_usec; + return tick / tick_in_usec; } unsigned int tc_core_time2ktime(unsigned int time) diff --git a/tc/tc_core.h b/tc/tc_core.h index 6dab272..7a986ac 100644 --- a/tc/tc_core.h +++ b/tc/tc_core.h @@ -12,8 +12,6 @@ enum link_layer { }; -int tc_core_time2big(unsigned time); -unsigned tc_core_time2tick(unsigned time); unsigned tc_core_tick2time(unsigned tick); unsigned tc_core_time2ktime(unsigned time); unsigned tc_core_ktime2time(unsigned ktime); diff --git a/tc/tc_exec.c b/tc/tc_exec.c index 182fbb4..fe9fdb1 100644 --- a/tc/tc_exec.c +++ b/tc/tc_exec.c @@ -26,7 +26,7 @@ static void usage(void) "OPTIONS := ... try tc exec <desired EXEC_KIND> help\n"); } -static int parse_noeopt(struct exec_util *eu, int argc, char **argv) +static int parse_noeopt(const struct exec_util *eu, int argc, char **argv) { if (argc) { fprintf(stderr, "Unknown exec \"%s\", hence option \"%s\" is unparsable\n", diff --git a/tc/tc_filter.c b/tc/tc_filter.c index eb45c58..7db850b 100644 --- a/tc/tc_filter.c +++ b/tc/tc_filter.c @@ -65,7 +65,7 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv) .n.nlmsg_type = cmd, .t.tcm_family = AF_UNSPEC, }; - struct filter_util *q = NULL; + const struct filter_util *q = NULL; __u32 prio = 0; __u32 protocol = 0; int protocol_set = 0; @@ -76,6 +76,7 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv) char d[IFNAMSIZ] = {}; char k[FILTER_NAMESZ] = {}; struct tc_estimator est = {}; + int ret; if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE) protocol = htons(ETH_P_ALL); @@ -221,7 +222,12 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv) if (est.ewma_log) addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); - if (rtnl_talk(&rth, &req.n, NULL) < 0) { + if (echo_request) + ret = rtnl_echo_talk(&rth, &req.n, json, print_filter); + else + ret = rtnl_talk(&rth, &req.n, NULL); + + if (ret < 0) { fprintf(stderr, "We have an error talking to the kernel\n"); return 2; } @@ -244,7 +250,7 @@ int print_filter(struct nlmsghdr *n, void *arg) struct tcmsg *t = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[TCA_MAX+1]; - struct filter_util *q; + const struct filter_util *q; char abuf[256]; if (n->nlmsg_type != RTM_NEWTFILTER && @@ -392,7 +398,7 @@ static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv) .t.tcm_family = AF_UNSPEC, }; struct nlmsghdr *answer; - struct filter_util *q = NULL; + const struct filter_util *q = NULL; __u32 prio = 0; __u32 protocol = 0; int protocol_set = 0; diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c index 84fd659..7eb9a31 100644 --- a/tc/tc_qdisc.c +++ b/tc/tc_qdisc.c @@ -42,7 +42,7 @@ static int usage(void) static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv) { - struct qdisc_util *q = NULL; + const struct qdisc_util *q = NULL; struct tc_estimator est = {}; struct { struct tc_sizespec szopts; @@ -217,7 +217,7 @@ int print_qdisc(struct nlmsghdr *n, void *arg) struct tcmsg *t = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[TCA_MAX+1]; - struct qdisc_util *q; + const struct qdisc_util *q; char abuf[256]; if (n->nlmsg_type != RTM_NEWQDISC && n->nlmsg_type != RTM_DELQDISC) { @@ -476,7 +476,7 @@ static int tc_qdisc_block_exists_cb(struct nlmsghdr *n, void *arg) struct tcmsg *t = NLMSG_DATA(n); struct rtattr *tb[TCA_MAX+1]; int len = n->nlmsg_len; - struct qdisc_util *q; + const struct qdisc_util *q; const char *kind; int err; diff --git a/tc/tc_util.c b/tc/tc_util.c index aa7cf60..a347a2d 100644 --- a/tc/tc_util.c +++ b/tc/tc_util.c @@ -257,11 +257,6 @@ tc_print_rate(enum output_type t, const char *key, const char *fmt, print_rate(use_iec, t, key, fmt, rate); } -char *sprint_ticks(__u32 ticks, char *buf) -{ - return sprint_time(tc_core_tick2time(ticks), buf); -} - int get_size_and_cell(unsigned int *size, int *cell_log, char *str) { char *slash = strchr(str, '/'); @@ -804,28 +799,30 @@ void print_tcstats_attr(FILE *fp, struct rtattr *tb[], const char *prefix, memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st))); - fprintf(fp, - "%sSent %llu bytes %u pkts (dropped %u, overlimits %u) ", - prefix, (unsigned long long)st.bytes, - st.packets, st.drops, st.overlimits); + print_string(PRINT_FP, NULL, "%s", prefix); + print_lluint(PRINT_ANY, "bytes", "Sent %llu bytes", + (unsigned long long)st.bytes); + print_uint(PRINT_ANY, "packets", " %u pkts", st.packets); + print_uint(PRINT_ANY, "dropped", " (dropped %u,", st.drops); + print_uint(PRINT_ANY, "overlimits", " overlimits %u) ", st.overlimits); if (st.bps || st.pps || st.qlen || st.backlog) { - fprintf(fp, "\n%s", prefix); + print_nl(); + print_string(PRINT_FP, NULL, "%s", prefix); + if (st.bps || st.pps) { - fprintf(fp, "rate "); + print_string(PRINT_FP, NULL, "rate ", NULL); if (st.bps) - tc_print_rate(PRINT_FP, NULL, "%s ", - st.bps); + tc_print_rate(PRINT_ANY, "rate", "%s ", st.bps); if (st.pps) - fprintf(fp, "%upps ", st.pps); + print_uint(PRINT_ANY, "pps", "%upps ", st.pps); } if (st.qlen || st.backlog) { - fprintf(fp, "backlog "); + print_string(PRINT_FP, NULL, "backlog ", NULL); if (st.backlog) - print_size(PRINT_FP, NULL, "%s ", - st.backlog); + print_size(PRINT_ANY, "backlog", "%s ", st.backlog); if (st.qlen) - fprintf(fp, "%up ", st.qlen); + print_uint(PRINT_ANY, "qlen", "%up ", st.qlen); } } } diff --git a/tc/tc_util.h b/tc/tc_util.h index 623d988..250cf33 100644 --- a/tc/tc_util.h +++ b/tc/tc_util.h @@ -29,51 +29,52 @@ enum #define FILTER_NAMESZ 16 struct qdisc_util { - struct qdisc_util *next; + struct qdisc_util *next; const char *id; - int (*parse_qopt)(struct qdisc_util *qu, int argc, + int (*parse_qopt)(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev); - int (*print_qopt)(struct qdisc_util *qu, + int (*print_qopt)(const struct qdisc_util *qu, FILE *f, struct rtattr *opt); - int (*print_xstats)(struct qdisc_util *qu, + int (*print_xstats)(const struct qdisc_util *qu, FILE *f, struct rtattr *xstats); - int (*parse_copt)(struct qdisc_util *qu, int argc, + int (*parse_copt)(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev); - int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); - int (*has_block)(struct qdisc_util *qu, struct rtattr *opt, __u32 block_idx, bool *p_has); + int (*print_copt)(const struct qdisc_util *qu, FILE *f, struct rtattr *opt); + int (*has_block)(const struct qdisc_util *qu, struct rtattr *opt, + __u32 block_idx, bool *p_has); }; extern __u16 f_proto; struct filter_util { struct filter_util *next; char id[FILTER_NAMESZ]; - int (*parse_fopt)(struct filter_util *qu, char *fhandle, + int (*parse_fopt)(const struct filter_util *qu, char *fhandle, int argc, char **argv, struct nlmsghdr *n); - int (*print_fopt)(struct filter_util *qu, + int (*print_fopt)(const struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle); }; struct action_util { struct action_util *next; char id[FILTER_NAMESZ]; - int (*parse_aopt)(struct action_util *a, int *argc, + int (*parse_aopt)(const struct action_util *a, int *argc, char ***argv, int code, struct nlmsghdr *n); - int (*print_aopt)(struct action_util *au, FILE *f, struct rtattr *opt); - int (*print_xstats)(struct action_util *au, + int (*print_aopt)(const struct action_util *au, FILE *f, struct rtattr *opt); + int (*print_xstats)(const struct action_util *au, FILE *f, struct rtattr *xstats); }; struct exec_util { struct exec_util *next; char id[FILTER_NAMESZ]; - int (*parse_eopt)(struct exec_util *eu, int argc, char **argv); + int (*parse_eopt)(const struct exec_util *eu, int argc, char **argv); }; const char *get_tc_lib(void); -struct qdisc_util *get_qdisc_kind(const char *str); -struct filter_util *get_filter_kind(const char *str); +const struct qdisc_util *get_qdisc_kind(const char *str); +const struct filter_util *get_filter_kind(const char *str); int get_qdisc_handle(__u32 *h, const char *str); int get_percent_rate(unsigned int *rate, const char *str, const char *dev); @@ -86,7 +87,6 @@ void tc_print_rate(enum output_type t, const char *key, const char *fmt, void print_devname(enum output_type type, int ifindex); char *sprint_tc_classid(__u32 h, char *buf); -char *sprint_ticks(__u32 ticks, char *buf); char *sprint_linklayer(unsigned int linklayer, char *buf); void print_tcstats_attr(FILE *fp, struct rtattr *tb[], @@ -111,11 +111,11 @@ int parse_action_control_slash(int *argc_p, char ***argv_p, int *result1_p, int *result2_p, bool allow_num); void print_action_control(FILE *f, const char *prefix, int action, const char *suffix); -int police_print_xstats(struct action_util *a, FILE *f, struct rtattr *tb); +int police_print_xstats(const struct action_util *a, FILE *f, struct rtattr *tb); int tc_print_action(FILE *f, const struct rtattr *tb, unsigned short tot_acts); int parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); void print_tm(FILE *f, const struct tcf_t *tm); -int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt); +int prio_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt); int cls_names_init(char *path); void cls_names_uninit(void); diff --git a/vdpa/include/uapi/linux/vdpa.h b/vdpa/include/uapi/linux/vdpa.h index e319fa7..265347b 100644 --- a/vdpa/include/uapi/linux/vdpa.h +++ b/vdpa/include/uapi/linux/vdpa.h @@ -56,6 +56,23 @@ enum vdpa_attr { /* virtio features that are provisioned to the vDPA device */ VDPA_ATTR_DEV_FEATURES, /* u64 */ + VDPA_ATTR_DEV_BLK_CFG_CAPACITY, /* u64 */ + VDPA_ATTR_DEV_BLK_CFG_SIZE_MAX, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_BLK_SIZE, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_SEG_MAX, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_NUM_QUEUES, /* u16 */ + VDPA_ATTR_DEV_BLK_CFG_PHY_BLK_EXP, /* u8 */ + VDPA_ATTR_DEV_BLK_CFG_ALIGN_OFFSET, /* u8 */ + VDPA_ATTR_DEV_BLK_CFG_MIN_IO_SIZE, /* u16 */ + VDPA_ATTR_DEV_BLK_CFG_OPT_IO_SIZE, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_MAX_DISCARD_SEC, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_MAX_DISCARD_SEG, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_DISCARD_SEC_ALIGN,/* u32 */ + VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEC, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEG, /* u32 */ + VDPA_ATTR_DEV_BLK_READ_ONLY, /* u8 */ + VDPA_ATTR_DEV_BLK_FLUSH, /* u8 */ + /* new attributes must be added above here */ VDPA_ATTR_MAX, }; |