From ad55e5d571a033e040287fc662161abe8eb2b4ff Mon Sep 17 00:00:00 2001 From: CN_SZTL Date: Tue, 21 Jan 2020 20:55:04 +0800 Subject: [PATCH] iproute2: roll back to 4.18.0 --- package/network/utils/iproute2/Makefile | 37 +- .../001-fix-print_0xhex-on-32-bit.patch | 343 ---- ...x-xtables-incorrect-usage-of-LDFLAGS.patch | 35 - .../patches/140-allow_pfifo_fast.patch | 4 +- .../utils/iproute2/patches/170-ip_tiny.patch | 8 +- .../patches/180-drop_FAILED_POLICY.patch | 2 +- .../iproute2/patches/190-add-cake-to-tc.patch | 1662 +++++++++++++++++ .../patches/190-link-libelf-to-tc-only.patch | 60 - 8 files changed, 1687 insertions(+), 464 deletions(-) delete mode 100644 package/network/utils/iproute2/patches/001-fix-print_0xhex-on-32-bit.patch delete mode 100644 package/network/utils/iproute2/patches/002-tc-fix-xtables-incorrect-usage-of-LDFLAGS.patch create mode 100644 package/network/utils/iproute2/patches/190-add-cake-to-tc.patch delete mode 100644 package/network/utils/iproute2/patches/190-link-libelf-to-tc-only.patch diff --git a/package/network/utils/iproute2/Makefile b/package/network/utils/iproute2/Makefile index c647331918..9dbd04784f 100644 --- a/package/network/utils/iproute2/Makefile +++ b/package/network/utils/iproute2/Makefile @@ -8,12 +8,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=iproute2 -PKG_VERSION:=4.19.0 -PKG_RELEASE:=8 +PKG_VERSION:=4.18.0 +PKG_RELEASE:=4 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=@KERNEL/linux/utils/net/iproute2 -PKG_HASH:=d9ec5ca1f47d8a85416fa26e7dc1cbf5d067640eb60e90bdc1c7e5bdc6a29984 +PKG_HASH:=a9e6c70c95f513871c5e1f4e452c04fcb3c4d8a05be651bd794cd994a52daa45 PKG_BUILD_PARALLEL:=1 PKG_BUILD_DEPENDS:=iptables PKG_LICENSE:=GPL-2.0 @@ -23,7 +23,6 @@ PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSI include $(INCLUDE_DIR)/kernel.mk include $(INCLUDE_DIR)/package.mk -include $(INCLUDE_DIR)/nls.mk define Package/iproute2/Default SECTION:=net @@ -38,8 +37,8 @@ $(call Package/iproute2/Default) TITLE:=Routing control utility (Minimal) VARIANT:=tiny PROVIDES:=ip - ALTERNATIVES:=200:/sbin/ip:/usr/libexec/ip-tiny - DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl + ALTERNATIVES:=200:/sbin/ip:/sbin/ip-tiny + DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl +libelf endef define Package/ip-full @@ -47,50 +46,50 @@ $(call Package/iproute2/Default) TITLE:=Routing control utility (Full) VARIANT:=full PROVIDES:=ip - ALTERNATIVES:=300:/sbin/ip:/usr/libexec/ip-full - DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl + ALTERNATIVES:=300:/sbin/ip:/sbin/ip-full + DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl +libelf endef define Package/tc $(call Package/iproute2/Default) TITLE:=Traffic control utility - DEPENDS:=+kmod-sched-core +(PACKAGE_devlink||PACKAGE_rdma):libmnl +libelf1 + DEPENDS:=+kmod-sched-core +(PACKAGE_devlink||PACKAGE_rdma):libmnl +libelf endef define Package/genl $(call Package/iproute2/Default) TITLE:=General netlink utility frontend - DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl + DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl +libelf endef define Package/ip-bridge $(call Package/iproute2/Default) TITLE:=Bridge configuration utility from iproute2 - DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl + DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl +libelf endef define Package/ss $(call Package/iproute2/Default) TITLE:=Socket statistics utility - DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl + DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl +libelf endef define Package/nstat $(call Package/iproute2/Default) TITLE:=Network statistics utility - DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl + DEPENDS:=+libnl-tiny +(PACKAGE_devlink||PACKAGE_rdma):libmnl +libelf endef define Package/devlink $(call Package/iproute2/Default) TITLE:=Network devlink utility - DEPENDS:=+libmnl + DEPENDS:=+libmnl +libelf endef define Package/rdma $(call Package/iproute2/Default) TITLE:=Network rdma utility - DEPENDS:=+libmnl + DEPENDS:=+libmnl +libelf endef ifeq ($(BUILD_VARIANT),tiny) @@ -139,13 +138,13 @@ define Build/InstallDev endef define Package/ip-tiny/install - $(INSTALL_DIR) $(1)/usr/libexec - $(INSTALL_BIN) $(PKG_BUILD_DIR)/ip/ip $(1)/usr/libexec/ip-tiny + $(INSTALL_DIR) $(1)/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/ip/ip $(1)/sbin/ip-tiny endef define Package/ip-full/install - $(INSTALL_DIR) $(1)/usr/libexec - $(INSTALL_BIN) $(PKG_BUILD_DIR)/ip/ip $(1)/usr/libexec/ip-full + $(INSTALL_DIR) $(1)/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/ip/ip $(1)/sbin/ip-full endef define Package/tc/install diff --git a/package/network/utils/iproute2/patches/001-fix-print_0xhex-on-32-bit.patch b/package/network/utils/iproute2/patches/001-fix-print_0xhex-on-32-bit.patch deleted file mode 100644 index 25994b4173..0000000000 --- a/package/network/utils/iproute2/patches/001-fix-print_0xhex-on-32-bit.patch +++ /dev/null @@ -1,343 +0,0 @@ -From 90c5c969f0b9a2fbb0016b955fecc359aa884220 Mon Sep 17 00:00:00 2001 -From: Stephen Hemminger -Date: Mon, 10 Dec 2018 14:20:32 -0800 -Subject: [PATCH] fix print_0xhex on 32 bit - -The argument to print_0xhex is converted to unsigned long long -so the format string give for normal printout has to be some -variant of %llx. Otherwise, bogus values will be printed on -32 bit platforms. - -Signed-off-by: Stephen Hemminger ---- - bridge/link.c | 2 +- - ip/ipaddress.c | 2 +- - ip/iplink_bridge.c | 2 +- - ip/iplink_bridge_slave.c | 6 +++--- - ip/iplink_geneve.c | 5 ++--- - ip/iplink_vxlan.c | 8 +++----- - ip/ipntable.c | 2 +- - ip/iproute.c | 8 ++++---- - ip/iproute_lwtunnel.c | 2 +- - ip/iprule.c | 6 +++--- - ip/iptuntap.c | 2 +- - ip/link_gre.c | 6 +++--- - ip/link_gre6.c | 4 ++-- - ip/link_ip6tnl.c | 2 +- - ip/link_iptnl.c | 4 ++-- - ip/link_vti.c | 2 +- - ip/link_vti6.c | 2 +- - tc/m_ife.c | 2 +- - tc/q_htb.c | 2 +- - tc/q_taprio.c | 2 +- - 20 files changed, 34 insertions(+), 37 deletions(-) - ---- a/bridge/link.c -+++ b/bridge/link.c -@@ -90,7 +90,7 @@ static void print_hwmode(__u16 mode) - { - if (mode >= ARRAY_SIZE(hw_mode)) - print_0xhex(PRINT_ANY, "hwmode", -- "hwmode %#hx ", mode); -+ "hwmode %#llx ", mode); - else - print_string(PRINT_ANY, "hwmode", - "hwmode %s ", hw_mode[mode]); ---- a/ip/ipaddress.c -+++ b/ip/ipaddress.c -@@ -129,7 +129,7 @@ static void print_operstate(FILE *f, __u - if (is_json_context()) - print_uint(PRINT_JSON, "operstate_index", NULL, state); - else -- print_0xhex(PRINT_FP, NULL, "state %#x", state); -+ print_0xhex(PRINT_FP, NULL, "state %#llx", state); - } else if (brief) { - print_color_string(PRINT_ANY, - oper_state_color(state), ---- a/ip/iplink_bridge.c -+++ b/ip/iplink_bridge.c -@@ -524,7 +524,7 @@ static void bridge_print_opt(struct link - if (tb[IFLA_BR_GROUP_FWD_MASK]) - print_0xhex(PRINT_ANY, - "group_fwd_mask", -- "group_fwd_mask %#x ", -+ "group_fwd_mask %#llx ", - rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK])); - - if (tb[IFLA_BR_GROUP_ADDR]) { ---- a/ip/iplink_bridge_slave.c -+++ b/ip/iplink_bridge_slave.c -@@ -167,11 +167,11 @@ static void bridge_slave_print_opt(struc - rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD])); - - if (tb[IFLA_BRPORT_ID]) -- print_0xhex(PRINT_ANY, "id", "port_id 0x%x ", -+ print_0xhex(PRINT_ANY, "id", "port_id %#llx ", - rta_getattr_u16(tb[IFLA_BRPORT_ID])); - - if (tb[IFLA_BRPORT_NO]) -- print_0xhex(PRINT_ANY, "no", "port_no 0x%x ", -+ print_0xhex(PRINT_ANY, "no", "port_no %#llx ", - rta_getattr_u16(tb[IFLA_BRPORT_NO])); - - if (tb[IFLA_BRPORT_DESIGNATED_PORT]) -@@ -266,7 +266,7 @@ static void bridge_slave_print_opt(struc - - fwd_mask = rta_getattr_u16(tb[IFLA_BRPORT_GROUP_FWD_MASK]); - print_0xhex(PRINT_ANY, "group_fwd_mask", -- "group_fwd_mask 0x%x ", fwd_mask); -+ "group_fwd_mask %#llx ", fwd_mask); - _bitmask2str(fwd_mask, convbuf, sizeof(convbuf), fwd_mask_tbl); - print_string(PRINT_ANY, "group_fwd_mask_str", - "group_fwd_mask_str %s ", convbuf); ---- a/ip/iplink_geneve.c -+++ b/ip/iplink_geneve.c -@@ -276,7 +276,7 @@ static void geneve_print_opt(struct link - tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]); - if (tos) { - if (is_json_context() || tos != 1) -- print_0xhex(PRINT_ANY, "tos", "tos 0x%x ", tos); -+ print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos); - else - print_string(PRINT_FP, NULL, "tos %s ", "inherit"); - } -@@ -286,8 +286,7 @@ static void geneve_print_opt(struct link - - if (label) - print_0xhex(PRINT_ANY, -- "label", -- "flowlabel %#x ", -+ "label", "flowlabel %#llx ", - ntohl(label)); - } - ---- a/ip/iplink_vxlan.c -+++ b/ip/iplink_vxlan.c -@@ -522,7 +522,7 @@ static void vxlan_print_opt(struct link_ - tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]); - if (tos) { - if (is_json_context() || tos != 1) -- print_0xhex(PRINT_ANY, "tos", "tos 0x%x ", tos); -+ print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos); - else - print_string(PRINT_FP, NULL, "tos %s ", "inherit"); - } -@@ -542,10 +542,8 @@ static void vxlan_print_opt(struct link_ - __u32 label = rta_getattr_u32(tb[IFLA_VXLAN_LABEL]); - - if (label) -- print_0xhex(PRINT_ANY, -- "label", -- "flowlabel %#x ", -- ntohl(label)); -+ print_0xhex(PRINT_ANY, "label", -+ "flowlabel %#llx ", ntohl(label)); - } - - if (tb[IFLA_VXLAN_AGEING]) { ---- a/ip/ipntable.c -+++ b/ip/ipntable.c -@@ -360,7 +360,7 @@ static void print_ndtconfig(const struct - print_uint(PRINT_ANY, "hash_rnd", - " hash_rnd %u ", ndtc->ndtc_hash_rnd); - print_0xhex(PRINT_ANY, "hash_mask", -- "hash_mask %08x ", ndtc->ndtc_hash_mask); -+ "hash_mask %08llx ", ndtc->ndtc_hash_mask); - - print_uint(PRINT_ANY, "hash_chain_gc", - "hash_chain_gc %u ", ndtc->ndtc_hash_chain_gc); ---- a/ip/iproute.c -+++ b/ip/iproute.c -@@ -346,7 +346,7 @@ static void print_rtax_features(FILE *fp - - if (features) - print_0xhex(PRINT_ANY, -- "features", "0x%x ", of); -+ "features", "%#llx ", of); - } - - static void print_rt_flags(FILE *fp, unsigned int flags) -@@ -483,10 +483,10 @@ static void print_rta_cacheinfo(FILE *fp - } - if (ci->rta_id) - print_0xhex(PRINT_ANY, "ipid", -- "ipid 0x%04x ", ci->rta_id); -+ "ipid 0x%04llx ", ci->rta_id); - if (ci->rta_ts || ci->rta_tsage) { - print_0xhex(PRINT_ANY, "ts", -- "ts 0x%x", ci->rta_ts); -+ "ts 0x%llx", ci->rta_ts); - print_uint(PRINT_ANY, "tsage", - "tsage %usec ", ci->rta_tsage); - } -@@ -885,7 +885,7 @@ int print_route(const struct sockaddr_nl - print_uint(PRINT_JSON, "mark", NULL, mark); - else if (mark >= 16) - print_0xhex(PRINT_FP, NULL, -- "mark 0x%x ", mark); -+ "mark 0x%llx ", mark); - else - print_uint(PRINT_FP, NULL, - "mark %u ", mark); ---- a/ip/iproute_lwtunnel.c -+++ b/ip/iproute_lwtunnel.c -@@ -115,7 +115,7 @@ static void print_srh(FILE *fp, struct i - - tlv = (struct sr6_tlv_hmac *)((char *)srh + offset); - print_0xhex(PRINT_ANY, "hmac", -- "hmac 0x%X ", ntohl(tlv->hmackeyid)); -+ "hmac %llX ", ntohl(tlv->hmackeyid)); - } - } - ---- a/ip/iprule.c -+++ b/ip/iprule.c -@@ -263,10 +263,10 @@ int print_rule(const struct sockaddr_nl - - if (tb[FRA_FWMASK] && - (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF) { -- print_0xhex(PRINT_ANY, "fwmark", "fwmark 0x%x", mark); -- print_0xhex(PRINT_ANY, "fwmask", "/0x%x ", mask); -+ print_0xhex(PRINT_ANY, "fwmark", "fwmark %#llx", mark); -+ print_0xhex(PRINT_ANY, "fwmask", "/%#llx ", mask); - } else { -- print_0xhex(PRINT_ANY, "fwmark", "fwmark 0x%x ", mark); -+ print_0xhex(PRINT_ANY, "fwmark", "fwmark %#llx ", mark); - } - } - ---- a/ip/iptuntap.c -+++ b/ip/iptuntap.c -@@ -254,7 +254,7 @@ static void print_flags(long flags) - flags &= ~(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | - IFF_VNET_HDR | IFF_PERSIST | IFF_NOFILTER); - if (flags) -- print_0xhex(PRINT_ANY, NULL, "%#x", flags); -+ print_0xhex(PRINT_ANY, NULL, "%#llx", flags); - - close_json_array(PRINT_JSON, NULL); - } ---- a/ip/link_gre.c -+++ b/ip/link_gre.c -@@ -463,7 +463,7 @@ static void gre_print_opt(struct link_ut - tos = rta_getattr_u8(tb[IFLA_GRE_TOS]); - if (tos) { - if (is_json_context() || tos != 1) -- print_0xhex(PRINT_ANY, "tos", "tos 0x%x ", tos); -+ print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos); - else - print_string(PRINT_FP, NULL, "tos %s ", "inherit"); - } -@@ -508,7 +508,7 @@ static void gre_print_opt(struct link_ut - - if (fwmark) { - print_0xhex(PRINT_ANY, -- "fwmark", "fwmark 0x%x ", fwmark); -+ "fwmark", "fwmark %#llx ", fwmark); - } - } - -@@ -541,7 +541,7 @@ static void gre_print_opt(struct link_ut - __u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]); - - print_0xhex(PRINT_ANY, -- "erspan_hwid", "erspan_hwid 0x%x ", erspan_hwid); -+ "erspan_hwid", "erspan_hwid %#llx ", erspan_hwid); - } - - tnl_print_encap(tb, ---- a/ip/link_gre6.c -+++ b/ip/link_gre6.c -@@ -576,7 +576,7 @@ static void gre_print_opt(struct link_ut - - if (fwmark) { - print_0xhex(PRINT_ANY, -- "fwmark", "fwmark 0x%x ", fwmark); -+ "fwmark", "fwmark %#llx ", fwmark); - } - } - -@@ -609,7 +609,7 @@ static void gre_print_opt(struct link_ut - __u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]); - - print_0xhex(PRINT_ANY, -- "erspan_hwid", "erspan_hwid 0x%x ", erspan_hwid); -+ "erspan_hwid", "erspan_hwid %#llx ", erspan_hwid); - } - - tnl_print_encap(tb, ---- a/ip/link_ip6tnl.c -+++ b/ip/link_ip6tnl.c -@@ -457,7 +457,7 @@ static void ip6tunnel_print_opt(struct l - - if (fwmark) { - print_0xhex(PRINT_ANY, -- "fwmark", "fwmark 0x%x ", fwmark); -+ "fwmark", "fwmark %#llx ", fwmark); - } - } - ---- a/ip/link_iptnl.c -+++ b/ip/link_iptnl.c -@@ -418,7 +418,7 @@ static void iptunnel_print_opt(struct li - tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]); - if (tos) { - if (is_json_context() || tos != 1) -- print_0xhex(PRINT_ANY, "tos", "tos 0x%x ", tos); -+ print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos); - else - print_string(PRINT_FP, NULL, "tos %s ", "inherit"); - } -@@ -476,7 +476,7 @@ static void iptunnel_print_opt(struct li - - if (fwmark) { - print_0xhex(PRINT_ANY, -- "fwmark", "fwmark 0x%x ", fwmark); -+ "fwmark", "fwmark %#llx ", fwmark); - } - } - ---- a/ip/link_vti.c -+++ b/ip/link_vti.c -@@ -208,7 +208,7 @@ static void vti_print_opt(struct link_ut - - if (fwmark) { - print_0xhex(PRINT_ANY, -- "fwmark", "fwmark 0x%x ", fwmark); -+ "fwmark", "fwmark %#llx ", fwmark); - } - } - } ---- a/ip/link_vti6.c -+++ b/ip/link_vti6.c -@@ -210,7 +210,7 @@ static void vti6_print_opt(struct link_u - - if (fwmark) { - print_0xhex(PRINT_ANY, -- "fwmark", "fwmark 0x%x ", fwmark); -+ "fwmark", "fwmark %#llx ", fwmark); - } - } - } ---- a/tc/m_ife.c -+++ b/tc/m_ife.c -@@ -247,7 +247,7 @@ static int print_ife(struct action_util - if (tb[TCA_IFE_TYPE]) { - ife_type = rta_getattr_u16(tb[TCA_IFE_TYPE]); - has_optional = 1; -- print_0xhex(PRINT_ANY, "type", "type 0x%X ", ife_type); -+ print_0xhex(PRINT_ANY, "type", "type %#llX ", ife_type); - } - - if (has_optional) ---- a/tc/q_htb.c -+++ b/tc/q_htb.c -@@ -332,7 +332,7 @@ static int htb_print_opt(struct qdisc_ut - if (RTA_PAYLOAD(tb[TCA_HTB_INIT]) < sizeof(*gopt)) return -1; - - print_int(PRINT_ANY, "r2q", "r2q %d", gopt->rate2quantum); -- print_0xhex(PRINT_ANY, "default", " default %x", gopt->defcls); -+ print_0xhex(PRINT_ANY, "default", " default %#llx", gopt->defcls); - print_uint(PRINT_ANY, "direct_packets_stat", - " direct_packets_stat %u", gopt->direct_pkts); - if (show_details) { diff --git a/package/network/utils/iproute2/patches/002-tc-fix-xtables-incorrect-usage-of-LDFLAGS.patch b/package/network/utils/iproute2/patches/002-tc-fix-xtables-incorrect-usage-of-LDFLAGS.patch deleted file mode 100644 index d5981360a9..0000000000 --- a/package/network/utils/iproute2/patches/002-tc-fix-xtables-incorrect-usage-of-LDFLAGS.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 6ddb36c3a9686df1cca2f4d06518395f1eb9d5cc Mon Sep 17 00:00:00 2001 -From: Syrone Wong -Date: Wed, 12 Dec 2018 19:35:08 +0800 -Subject: [PATCH] tc: fix xtables incorrect usage of LDFLAGS - -The incorrect setting of LDFLAGS causes error below: - -> em_ipt.o: In function `em_ipt_print_epot': -> em_ipt.c:(.text.em_ipt_print_epot+0x2e): undefined reference to -> `xtables_init_all' - -em_ipt.c gets involved when TC_CONFIG_XT=y, which requires xtables, -while tc/Makefile doesn't pass flags correctly. It adds '-lxtables' -to LDFLAGS instead of LDLIBS. - -Fixes: dd296215 ("tc: add em_ipt ematch for calling xtables matches from tc matching context") - -Signed-off-by: Syrone Wong -Acked-by: Eyal Birger -Signed-off-by: Stephen Hemminger ---- - tc/Makefile | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/tc/Makefile -+++ b/tc/Makefile -@@ -170,7 +170,7 @@ em_ipset.o: CFLAGS += $$($(PKG_CONFIG) x - em_ipt.o: CFLAGS += $$($(PKG_CONFIG) xtables --cflags) - - ifeq ($(TC_CONFIG_XT),y) -- LDFLAGS += $$($(PKG_CONFIG) xtables --libs) -+ LDLIBS += $$($(PKG_CONFIG) xtables --libs) - endif - - %.yacc.c: %.y diff --git a/package/network/utils/iproute2/patches/140-allow_pfifo_fast.patch b/package/network/utils/iproute2/patches/140-allow_pfifo_fast.patch index bb898a40d6..caf889e123 100644 --- a/package/network/utils/iproute2/patches/140-allow_pfifo_fast.patch +++ b/package/network/utils/iproute2/patches/140-allow_pfifo_fast.patch @@ -1,7 +1,7 @@ --- a/tc/q_fifo.c +++ b/tc/q_fifo.c -@@ -99,5 +99,6 @@ struct qdisc_util pfifo_head_drop_qdisc_ - +@@ -97,5 +97,6 @@ struct qdisc_util pfifo_head_drop_qdisc_ + extern int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt); struct qdisc_util pfifo_fast_qdisc_util = { .id = "pfifo_fast", + .parse_qopt = fifo_parse_opt, diff --git a/package/network/utils/iproute2/patches/170-ip_tiny.patch b/package/network/utils/iproute2/patches/170-ip_tiny.patch index dc0547a256..5c8af0b0f1 100644 --- a/package/network/utils/iproute2/patches/170-ip_tiny.patch +++ b/package/network/utils/iproute2/patches/170-ip_tiny.patch @@ -25,7 +25,7 @@ sed -n '/'$$s'[^ ]* =/{s:.* \([^ ]*'$$s'[^ ]*\) .*:extern char \1[] __attribute__((weak)); if (!strcmp(sym, "\1")) return \1;:;p}' $$files ; \ --- a/ip/ip.c +++ b/ip/ip.c -@@ -47,10 +47,16 @@ static void usage(void) +@@ -48,10 +48,16 @@ static void usage(void) fprintf(stderr, "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" " ip [ -force ] -batch filename\n" @@ -42,7 +42,7 @@ " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" " -h[uman-readable] | -iec | -j[son] | -p[retty] |\n" " -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |\n" -@@ -72,32 +78,44 @@ static const struct cmd { +@@ -73,32 +79,44 @@ static const struct cmd { int (*func)(int argc, char **argv); } cmds[] = { { "address", do_ipaddr }, @@ -89,7 +89,7 @@ { "help", do_help }, --- a/lib/utils.c +++ b/lib/utils.c -@@ -1012,6 +1012,7 @@ const char *rt_addr_n2a_r(int af, int le +@@ -1010,6 +1010,7 @@ const char *rt_addr_n2a_r(int af, int le return inet_ntop(af, addr, buf, buflen); case AF_MPLS: return mpls_ntop(af, addr, buf, buflen); @@ -97,7 +97,7 @@ case AF_IPX: return ipx_ntop(af, addr, buf, buflen); case AF_DECnet: -@@ -1021,6 +1022,7 @@ const char *rt_addr_n2a_r(int af, int le +@@ -1019,6 +1020,7 @@ const char *rt_addr_n2a_r(int af, int le memcpy(dna.a_addr, addr, 2); return dnet_ntop(af, &dna, buf, buflen); } diff --git a/package/network/utils/iproute2/patches/180-drop_FAILED_POLICY.patch b/package/network/utils/iproute2/patches/180-drop_FAILED_POLICY.patch index 5de663f5c6..9c063791d1 100644 --- a/package/network/utils/iproute2/patches/180-drop_FAILED_POLICY.patch +++ b/package/network/utils/iproute2/patches/180-drop_FAILED_POLICY.patch @@ -31,7 +31,7 @@ Subject: [PATCH] add support for dropping with FAILED_POLICY if (!end || end == arg || *end || res > 255) --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h -@@ -228,6 +228,7 @@ enum { +@@ -221,6 +221,7 @@ enum { RTN_THROW, /* Not in this table */ RTN_NAT, /* Translate this address */ RTN_XRESOLVE, /* Use external resolver */ diff --git a/package/network/utils/iproute2/patches/190-add-cake-to-tc.patch b/package/network/utils/iproute2/patches/190-add-cake-to-tc.patch new file mode 100644 index 0000000000..a05d8776ee --- /dev/null +++ b/package/network/utils/iproute2/patches/190-add-cake-to-tc.patch @@ -0,0 +1,1662 @@ +--- a/include/uapi/linux/pkt_sched.h ++++ b/include/uapi/linux/pkt_sched.h +@@ -934,4 +934,118 @@ enum { + + #define TCA_CBS_MAX (__TCA_CBS_MAX - 1) + ++/* CAKE */ ++enum { ++ TCA_CAKE_UNSPEC, ++ TCA_CAKE_PAD, ++ TCA_CAKE_BASE_RATE64, ++ TCA_CAKE_DIFFSERV_MODE, ++ TCA_CAKE_ATM, ++ TCA_CAKE_FLOW_MODE, ++ TCA_CAKE_OVERHEAD, ++ TCA_CAKE_RTT, ++ TCA_CAKE_TARGET, ++ TCA_CAKE_AUTORATE, ++ TCA_CAKE_MEMORY, ++ TCA_CAKE_NAT, ++ TCA_CAKE_RAW, // was _ETHERNET ++ TCA_CAKE_WASH, ++ TCA_CAKE_MPU, ++ TCA_CAKE_INGRESS, ++ TCA_CAKE_ACK_FILTER, ++ TCA_CAKE_SPLIT_GSO, ++ __TCA_CAKE_MAX ++}; ++#define TCA_CAKE_MAX (__TCA_CAKE_MAX - 1) ++ ++enum { ++ __TCA_CAKE_STATS_INVALID, ++ TCA_CAKE_STATS_PAD, ++ TCA_CAKE_STATS_CAPACITY_ESTIMATE64, ++ TCA_CAKE_STATS_MEMORY_LIMIT, ++ TCA_CAKE_STATS_MEMORY_USED, ++ TCA_CAKE_STATS_AVG_NETOFF, ++ TCA_CAKE_STATS_MIN_NETLEN, ++ TCA_CAKE_STATS_MAX_NETLEN, ++ TCA_CAKE_STATS_MIN_ADJLEN, ++ TCA_CAKE_STATS_MAX_ADJLEN, ++ TCA_CAKE_STATS_TIN_STATS, ++ TCA_CAKE_STATS_DEFICIT, ++ TCA_CAKE_STATS_COBALT_COUNT, ++ TCA_CAKE_STATS_DROPPING, ++ TCA_CAKE_STATS_DROP_NEXT_US, ++ TCA_CAKE_STATS_P_DROP, ++ TCA_CAKE_STATS_BLUE_TIMER_US, ++ __TCA_CAKE_STATS_MAX ++}; ++#define TCA_CAKE_STATS_MAX (__TCA_CAKE_STATS_MAX - 1) ++ ++enum { ++ __TCA_CAKE_TIN_STATS_INVALID, ++ TCA_CAKE_TIN_STATS_PAD, ++ TCA_CAKE_TIN_STATS_SENT_PACKETS, ++ TCA_CAKE_TIN_STATS_SENT_BYTES64, ++ TCA_CAKE_TIN_STATS_DROPPED_PACKETS, ++ TCA_CAKE_TIN_STATS_DROPPED_BYTES64, ++ TCA_CAKE_TIN_STATS_ACKS_DROPPED_PACKETS, ++ TCA_CAKE_TIN_STATS_ACKS_DROPPED_BYTES64, ++ TCA_CAKE_TIN_STATS_ECN_MARKED_PACKETS, ++ TCA_CAKE_TIN_STATS_ECN_MARKED_BYTES64, ++ TCA_CAKE_TIN_STATS_BACKLOG_PACKETS, ++ TCA_CAKE_TIN_STATS_BACKLOG_BYTES, ++ TCA_CAKE_TIN_STATS_THRESHOLD_RATE64, ++ TCA_CAKE_TIN_STATS_TARGET_US, ++ TCA_CAKE_TIN_STATS_INTERVAL_US, ++ TCA_CAKE_TIN_STATS_WAY_INDIRECT_HITS, ++ TCA_CAKE_TIN_STATS_WAY_MISSES, ++ TCA_CAKE_TIN_STATS_WAY_COLLISIONS, ++ TCA_CAKE_TIN_STATS_PEAK_DELAY_US, ++ TCA_CAKE_TIN_STATS_AVG_DELAY_US, ++ TCA_CAKE_TIN_STATS_BASE_DELAY_US, ++ TCA_CAKE_TIN_STATS_SPARSE_FLOWS, ++ TCA_CAKE_TIN_STATS_BULK_FLOWS, ++ TCA_CAKE_TIN_STATS_UNRESPONSIVE_FLOWS, ++ TCA_CAKE_TIN_STATS_MAX_SKBLEN, ++ TCA_CAKE_TIN_STATS_FLOW_QUANTUM, ++ __TCA_CAKE_TIN_STATS_MAX ++}; ++#define TCA_CAKE_TIN_STATS_MAX (__TCA_CAKE_TIN_STATS_MAX - 1) ++#define TC_CAKE_MAX_TINS (8) ++ ++enum { ++ CAKE_FLOW_NONE = 0, ++ CAKE_FLOW_SRC_IP, ++ CAKE_FLOW_DST_IP, ++ CAKE_FLOW_HOSTS, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_DST_IP */ ++ CAKE_FLOW_FLOWS, ++ CAKE_FLOW_DUAL_SRC, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_FLOWS */ ++ CAKE_FLOW_DUAL_DST, /* = CAKE_FLOW_DST_IP | CAKE_FLOW_FLOWS */ ++ CAKE_FLOW_TRIPLE, /* = CAKE_FLOW_HOSTS | CAKE_FLOW_FLOWS */ ++ CAKE_FLOW_MAX, ++}; ++ ++enum { ++ CAKE_DIFFSERV_DIFFSERV3 = 0, ++ CAKE_DIFFSERV_DIFFSERV4, ++ CAKE_DIFFSERV_DIFFSERV8, ++ CAKE_DIFFSERV_BESTEFFORT, ++ CAKE_DIFFSERV_PRECEDENCE, ++ CAKE_DIFFSERV_MAX ++}; ++ ++enum { ++ CAKE_ACK_NONE = 0, ++ CAKE_ACK_FILTER, ++ CAKE_ACK_AGGRESSIVE, ++ CAKE_ACK_MAX ++}; ++ ++enum { ++ CAKE_ATM_NONE = 0, ++ CAKE_ATM_ATM, ++ CAKE_ATM_PTM, ++ CAKE_ATM_MAX ++}; ++ ++ + #endif +--- /dev/null ++++ b/man/man8/tc-cake.8 +@@ -0,0 +1,710 @@ ++.TH CAKE 8 "19 July 2018" "iproute2" "Linux" ++.SH NAME ++CAKE \- Common Applications Kept Enhanced (CAKE) ++.SH SYNOPSIS ++.B tc qdisc ... cake ++.br ++[ ++.BR bandwidth ++RATE | ++.BR unlimited* ++| ++.BR autorate-ingress ++] ++.br ++[ ++.BR rtt ++TIME | ++.BR datacentre ++| ++.BR lan ++| ++.BR metro ++| ++.BR regional ++| ++.BR internet* ++| ++.BR oceanic ++| ++.BR satellite ++| ++.BR interplanetary ++] ++.br ++[ ++.BR besteffort ++| ++.BR diffserv8 ++| ++.BR diffserv4 ++| ++.BR diffserv3* ++] ++.br ++[ ++.BR flowblind ++| ++.BR srchost ++| ++.BR dsthost ++| ++.BR hosts ++| ++.BR flows ++| ++.BR dual-srchost ++| ++.BR dual-dsthost ++| ++.BR triple-isolate* ++] ++.br ++[ ++.BR nat ++| ++.BR nonat* ++] ++.br ++[ ++.BR wash ++| ++.BR nowash* ++] ++.br ++[ ++.BR split-gso* ++| ++.BR no-split-gso ++] ++.br ++[ ++.BR ack-filter ++| ++.BR ack-filter-aggressive ++| ++.BR no-ack-filter* ++] ++.br ++[ ++.BR memlimit ++LIMIT ] ++.br ++[ ++.BR ptm ++| ++.BR atm ++| ++.BR noatm* ++] ++.br ++[ ++.BR overhead ++N | ++.BR conservative ++| ++.BR raw* ++] ++.br ++[ ++.BR mpu ++N ] ++.br ++[ ++.BR ingress ++| ++.BR egress* ++] ++.br ++(* marks defaults) ++ ++ ++.SH DESCRIPTION ++CAKE (Common Applications Kept Enhanced) is a shaping-capable queue discipline ++which uses both AQM and FQ. It combines COBALT, which is an AQM algorithm ++combining Codel and BLUE, a shaper which operates in deficit mode, and a variant ++of DRR++ for flow isolation. 8-way set-associative hashing is used to virtually ++eliminate hash collisions. Priority queuing is available through a simplified ++diffserv implementation. Overhead compensation for various encapsulation ++schemes is tightly integrated. ++ ++All settings are optional; the default settings are chosen to be sensible in ++most common deployments. Most people will only need to set the ++.B bandwidth ++parameter to get useful results, but reading the ++.B Overhead Compensation ++and ++.B Round Trip Time ++sections is strongly encouraged. ++ ++.SH SHAPER PARAMETERS ++CAKE uses a deficit-mode shaper, which does not exhibit the initial burst ++typical of token-bucket shapers. It will automatically burst precisely as much ++as required to maintain the configured throughput. As such, it is very ++straightforward to configure. ++.PP ++.B unlimited ++(default) ++.br ++ No limit on the bandwidth. ++.PP ++.B bandwidth ++RATE ++.br ++ Set the shaper bandwidth. See ++.BR tc(8) ++or examples below for details of the RATE value. ++.PP ++.B autorate-ingress ++.br ++ Automatic capacity estimation based on traffic arriving at this qdisc. ++This is most likely to be useful with cellular links, which tend to change ++quality randomly. A ++.B bandwidth ++parameter can be used in conjunction to specify an initial estimate. The shaper ++will periodically be set to a bandwidth slightly below the estimated rate. This ++estimator cannot estimate the bandwidth of links downstream of itself. ++ ++.SH OVERHEAD COMPENSATION PARAMETERS ++The size of each packet on the wire may differ from that seen by Linux. The ++following parameters allow CAKE to compensate for this difference by internally ++considering each packet to be bigger than Linux informs it. To assist users who ++are not expert network engineers, keywords have been provided to represent a ++number of common link technologies. ++ ++.SS Manual Overhead Specification ++.B overhead ++BYTES ++.br ++ Adds BYTES to the size of each packet. BYTES may be negative; values ++between -64 and 256 (inclusive) are accepted. ++.PP ++.B mpu ++BYTES ++.br ++ Rounds each packet (including overhead) up to a minimum length ++BYTES. BYTES may not be negative; values between 0 and 256 (inclusive) ++are accepted. ++.PP ++.B atm ++.br ++ Compensates for ATM cell framing, which is normally found on ADSL links. ++This is performed after the ++.B overhead ++parameter above. ATM uses fixed 53-byte cells, each of which can carry 48 bytes ++payload. ++.PP ++.B ptm ++.br ++ Compensates for PTM encoding, which is normally found on VDSL2 links and ++uses a 64b/65b encoding scheme. It is even more efficient to simply ++derate the specified shaper bandwidth by a factor of 64/65 or 0.984. See ++ITU G.992.3 Annex N and IEEE 802.3 Section 61.3 for details. ++.PP ++.B noatm ++.br ++ Disables ATM and PTM compensation. ++ ++.SS Failsafe Overhead Keywords ++These two keywords are provided for quick-and-dirty setup. Use them if you ++can't be bothered to read the rest of this section. ++.PP ++.B raw ++(default) ++.br ++ Turns off all overhead compensation in CAKE. The packet size reported ++by Linux will be used directly. ++.PP ++ Other overhead keywords may be added after "raw". The effect of this is ++to make the overhead compensation operate relative to the reported packet size, ++not the underlying IP packet size. ++.PP ++.B conservative ++.br ++ Compensates for more overhead than is likely to occur on any ++widely-deployed link technology. ++.br ++ Equivalent to ++.B overhead 48 atm. ++ ++.SS ADSL Overhead Keywords ++Most ADSL modems have a way to check which framing scheme is in use. Often this ++is also specified in the settings document provided by the ISP. The keywords in ++this section are intended to correspond with these sources of information. All ++of them implicitly set the ++.B atm ++flag. ++.PP ++.B pppoa-vcmux ++.br ++ Equivalent to ++.B overhead 10 atm ++.PP ++.B pppoa-llc ++.br ++ Equivalent to ++.B overhead 14 atm ++.PP ++.B pppoe-vcmux ++.br ++ Equivalent to ++.B overhead 32 atm ++.PP ++.B pppoe-llcsnap ++.br ++ Equivalent to ++.B overhead 40 atm ++.PP ++.B bridged-vcmux ++.br ++ Equivalent to ++.B overhead 24 atm ++.PP ++.B bridged-llcsnap ++.br ++ Equivalent to ++.B overhead 32 atm ++.PP ++.B ipoa-vcmux ++.br ++ Equivalent to ++.B overhead 8 atm ++.PP ++.B ipoa-llcsnap ++.br ++ Equivalent to ++.B overhead 16 atm ++.PP ++See also the Ethernet Correction Factors section below. ++ ++.SS VDSL2 Overhead Keywords ++ATM was dropped from VDSL2 in favour of PTM, which is a much more ++straightforward framing scheme. Some ISPs retained PPPoE for compatibility with ++their existing back-end systems. ++.PP ++.B pppoe-ptm ++.br ++ Equivalent to ++.B overhead 30 ptm ++ ++.br ++ PPPoE: 2B PPP + 6B PPPoE + ++.br ++ ETHERNET: 6B dest MAC + 6B src MAC + 2B ethertype + 4B Frame Check Sequence + ++.br ++ PTM: 1B Start of Frame (S) + 1B End of Frame (Ck) + 2B TC-CRC (PTM-FCS) ++.br ++.PP ++.B bridged-ptm ++.br ++ Equivalent to ++.B overhead 22 ptm ++.br ++ ETHERNET: 6B dest MAC + 6B src MAC + 2B ethertype + 4B Frame Check Sequence + ++.br ++ PTM: 1B Start of Frame (S) + 1B End of Frame (Ck) + 2B TC-CRC (PTM-FCS) ++.br ++.PP ++See also the Ethernet Correction Factors section below. ++ ++.SS DOCSIS Cable Overhead Keyword ++DOCSIS is the universal standard for providing Internet service over cable-TV ++infrastructure. ++ ++In this case, the actual on-wire overhead is less important than the packet size ++the head-end equipment uses for shaping and metering. This is specified to be ++an Ethernet frame including the CRC (aka FCS). ++.PP ++.B docsis ++.br ++ Equivalent to ++.B overhead 18 mpu 64 noatm ++ ++.SS Ethernet Overhead Keywords ++.PP ++.B ethernet ++.br ++ Accounts for Ethernet's preamble, inter-frame gap, and Frame Check ++Sequence. Use this keyword when the bottleneck being shaped for is an ++actual Ethernet cable. ++.br ++ Equivalent to ++.B overhead 38 mpu 84 noatm ++.PP ++.B ether-vlan ++.br ++ Adds 4 bytes to the overhead compensation, accounting for an IEEE 802.1Q ++VLAN header appended to the Ethernet frame header. NB: Some ISPs use one or ++even two of these within PPPoE; this keyword may be repeated as necessary to ++express this. ++ ++.SH ROUND TRIP TIME PARAMETERS ++Active Queue Management (AQM) consists of embedding congestion signals in the ++packet flow, which receivers use to instruct senders to slow down when the queue ++is persistently occupied. CAKE uses ECN signalling when available, and packet ++drops otherwise, according to a combination of the Codel and BLUE AQM algorithms ++called COBALT. ++ ++Very short latencies require a very rapid AQM response to adequately control ++latency. However, such a rapid response tends to impair throughput when the ++actual RTT is relatively long. CAKE allows specifying the RTT it assumes for ++tuning various parameters. Actual RTTs within an order of magnitude of this ++will generally work well for both throughput and latency management. ++ ++At the 'lan' setting and below, the time constants are similar in magnitude to ++the jitter in the Linux kernel itself, so congestion might be signalled ++prematurely. The flows will then become sparse and total throughput reduced, ++leaving little or no back-pressure for the fairness logic to work against. Use ++the "metro" setting for local lans unless you have a custom kernel. ++.PP ++.B rtt ++TIME ++.br ++ Manually specify an RTT. ++.PP ++.B datacentre ++.br ++ For extremely high-performance 10GigE+ networks only. Equivalent to ++.B rtt 100us. ++.PP ++.B lan ++.br ++ For pure Ethernet (not Wi-Fi) networks, at home or in the office. Don't ++use this when shaping for an Internet access link. Equivalent to ++.B rtt 1ms. ++.PP ++.B metro ++.br ++ For traffic mostly within a single city. Equivalent to ++.B rtt 10ms. ++.PP ++.B regional ++.br ++ For traffic mostly within a European-sized country. Equivalent to ++.B rtt 30ms. ++.PP ++.B internet ++(default) ++.br ++ This is suitable for most Internet traffic. Equivalent to ++.B rtt 100ms. ++.PP ++.B oceanic ++.br ++ For Internet traffic with generally above-average latency, such as that ++suffered by Australasian residents. Equivalent to ++.B rtt 300ms. ++.PP ++.B satellite ++.br ++ For traffic via geostationary satellites. Equivalent to ++.B rtt 1000ms. ++.PP ++.B interplanetary ++.br ++ So named because Jupiter is about 1 light-hour from Earth. Use this to ++(almost) completely disable AQM actions. Equivalent to ++.B rtt 3600s. ++ ++.SH FLOW ISOLATION PARAMETERS ++With flow isolation enabled, CAKE places packets from different flows into ++different queues, each of which carries its own AQM state. Packets from each ++queue are then delivered fairly, according to a DRR++ algorithm which minimises ++latency for "sparse" flows. CAKE uses a set-associative hashing algorithm to ++minimise flow collisions. ++ ++These keywords specify whether fairness based on source address, destination ++address, individual flows, or any combination of those is desired. ++.PP ++.B flowblind ++.br ++ Disables flow isolation; all traffic passes through a single queue for ++each tin. ++.PP ++.B srchost ++.br ++ Flows are defined only by source address. Could be useful on the egress ++path of an ISP backhaul. ++.PP ++.B dsthost ++.br ++ Flows are defined only by destination address. Could be useful on the ++ingress path of an ISP backhaul. ++.PP ++.B hosts ++.br ++ Flows are defined by source-destination host pairs. This is host ++isolation, rather than flow isolation. ++.PP ++.B flows ++.br ++ Flows are defined by the entire 5-tuple of source address, destination ++address, transport protocol, source port and destination port. This is the type ++of flow isolation performed by SFQ and fq_codel. ++.PP ++.B dual-srchost ++.br ++ Flows are defined by the 5-tuple, and fairness is applied first over ++source addresses, then over individual flows. Good for use on egress traffic ++from a LAN to the internet, where it'll prevent any one LAN host from ++monopolising the uplink, regardless of the number of flows they use. ++.PP ++.B dual-dsthost ++.br ++ Flows are defined by the 5-tuple, and fairness is applied first over ++destination addresses, then over individual flows. Good for use on ingress ++traffic to a LAN from the internet, where it'll prevent any one LAN host from ++monopolising the downlink, regardless of the number of flows they use. ++.PP ++.B triple-isolate ++(default) ++.br ++ Flows are defined by the 5-tuple, and fairness is applied over source ++*and* destination addresses intelligently (ie. not merely by host-pairs), and ++also over individual flows. Use this if you're not certain whether to use ++dual-srchost or dual-dsthost; it'll do both jobs at once, preventing any one ++host on *either* side of the link from monopolising it with a large number of ++flows. ++.PP ++.B nat ++.br ++ Instructs Cake to perform a NAT lookup before applying flow-isolation ++rules, to determine the true addresses and port numbers of the packet, to ++improve fairness between hosts "inside" the NAT. This has no practical effect ++in "flowblind" or "flows" modes, or if NAT is performed on a different host. ++.PP ++.B nonat ++(default) ++.br ++ Cake will not perform a NAT lookup. Flow isolation will be performed ++using the addresses and port numbers directly visible to the interface Cake is ++attached to. ++ ++.SH PRIORITY QUEUE PARAMETERS ++CAKE can divide traffic into "tins" based on the Diffserv field. Each tin has ++its own independent set of flow-isolation queues, and is serviced based on a WRR ++algorithm. To avoid perverse Diffserv marking incentives, tin weights have a ++"priority sharing" value when bandwidth used by that tin is below a threshold, ++and a lower "bandwidth sharing" value when above. Bandwidth is compared against ++the threshold using the same algorithm as the deficit-mode shaper. ++ ++Detailed customisation of tin parameters is not provided. The following presets ++perform all necessary tuning, relative to the current shaper bandwidth and RTT ++settings. ++.PP ++.B besteffort ++.br ++ Disables priority queuing by placing all traffic in one tin. ++.PP ++.B precedence ++.br ++ Enables legacy interpretation of TOS "Precedence" field. Use of this ++preset on the modern Internet is firmly discouraged. ++.PP ++.B diffserv4 ++.br ++ Provides a general-purpose Diffserv implementation with four tins: ++.br ++ Bulk (CS1), 6.25% threshold, generally low priority. ++.br ++ Best Effort (general), 100% threshold. ++.br ++ Video (AF4x, AF3x, CS3, AF2x, CS2, TOS4, TOS1), 50% threshold. ++.br ++ Voice (CS7, CS6, EF, VA, CS5, CS4), 25% threshold. ++.PP ++.B diffserv3 ++(default) ++.br ++ Provides a simple, general-purpose Diffserv implementation with three tins: ++.br ++ Bulk (CS1), 6.25% threshold, generally low priority. ++.br ++ Best Effort (general), 100% threshold. ++.br ++ Voice (CS7, CS6, EF, VA, TOS4), 25% threshold, reduced Codel interval. ++ ++.SH OTHER PARAMETERS ++.B memlimit ++LIMIT ++.br ++ Limit the memory consumed by Cake to LIMIT bytes. Note that this does ++not translate directly to queue size (so do not size this based on bandwidth ++delay product considerations, but rather on worst case acceptable memory ++consumption), as there is some overhead in the data structures containing the ++packets, especially for small packets. ++ ++ By default, the limit is calculated based on the bandwidth and RTT ++settings. ++ ++.PP ++.B wash ++ ++.br ++ Traffic entering your diffserv domain is frequently mis-marked in ++transit from the perspective of your network, and traffic exiting yours may be ++mis-marked from the perspective of the transiting provider. ++ ++Apply the wash option to clear all extra diffserv (but not ECN bits), after ++priority queuing has taken place. ++ ++If you are shaping inbound, and cannot trust the diffserv markings (as is the ++case for Comcast Cable, among others), it is best to use a single queue ++"besteffort" mode with wash. ++ ++.PP ++.B split-gso ++ ++.br ++ This option controls whether CAKE will split General Segmentation ++Offload (GSO) super-packets into their on-the-wire components and ++dequeue them individually. ++ ++.br ++Super-packets are created by the networking stack to improve efficiency. ++However, because they are larger they take longer to dequeue, which ++translates to higher latency for competing flows, especially at lower ++bandwidths. CAKE defaults to splitting GSO packets to achieve the lowest ++possible latency. At link speeds higher than 10 Gbps, setting the ++no-split-gso parameter can increase the maximum achievable throughput by ++retaining the full GSO packets. ++ ++.SH OVERRIDING CLASSIFICATION WITH TC FILTERS ++ ++CAKE supports overriding of its internal classification of packets through the ++tc filter mechanism. Packets can be assigned to different priority tins by ++setting the ++.B priority ++field on the skb, and the flow hashing can be overridden by setting the ++.B classid ++parameter. ++ ++.PP ++.B Tin override ++ ++.br ++ To assign a priority tin, the major number of the priority field needs ++to match the qdisc handle of the cake instance; if it does, the minor number ++will be interpreted as the tin index. For example, to classify all ICMP packets ++as 'bulk', the following filter can be used: ++ ++.br ++ # tc qdisc replace dev eth0 handle 1: root cake diffserv3 ++ # tc filter add dev eth0 parent 1: protocol ip prio 1 \\ ++ u32 match icmp type 0 0 action skbedit priority 1:1 ++ ++.PP ++.B Flow hash override ++ ++.br ++ To override flow hashing, the classid can be set. CAKE will interpret ++the major number of the classid as the host hash used in host isolation mode, ++and the minor number as the flow hash used for flow-based queueing. One or both ++of those can be set, and will be used if the relevant flow isolation parameter ++is set (i.e., the major number will be ignored if CAKE is not configured in ++hosts mode, and the minor number will be ignored if CAKE is not configured in ++flows mode). ++ ++.br ++This example will assign all ICMP packets to the first queue: ++ ++.br ++ # tc qdisc replace dev eth0 handle 1: root cake ++ # tc filter add dev eth0 parent 1: protocol ip prio 1 \\ ++ u32 match icmp type 0 0 classid 0:1 ++ ++.br ++If only one of the host and flow overrides is set, CAKE will compute the other ++hash from the packet as normal. Note, however, that the host isolation mode ++works by assigning a host ID to the flow queue; so if overriding both host and ++flow, the same flow cannot have more than one host assigned. In addition, it is ++not possible to assign different source and destination host IDs through the ++override mechanism; if a host ID is assigned, it will be used as both source and ++destination host. ++ ++ ++ ++.SH EXAMPLES ++# tc qdisc delete root dev eth0 ++.br ++# tc qdisc add root dev eth0 cake bandwidth 100Mbit ethernet ++.br ++# tc -s qdisc show dev eth0 ++.br ++qdisc cake 1: root refcnt 2 bandwidth 100Mbit diffserv3 triple-isolate rtt 100.0ms noatm overhead 38 mpu 84 ++ Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) ++ backlog 0b 0p requeues 0 ++ memory used: 0b of 5000000b ++ capacity estimate: 100Mbit ++ min/max network layer size: 65535 / 0 ++ min/max overhead-adjusted size: 65535 / 0 ++ average network hdr offset: 0 ++ ++ Bulk Best Effort Voice ++ thresh 6250Kbit 100Mbit 25Mbit ++ target 5.0ms 5.0ms 5.0ms ++ interval 100.0ms 100.0ms 100.0ms ++ pk_delay 0us 0us 0us ++ av_delay 0us 0us 0us ++ sp_delay 0us 0us 0us ++ pkts 0 0 0 ++ bytes 0 0 0 ++ way_inds 0 0 0 ++ way_miss 0 0 0 ++ way_cols 0 0 0 ++ drops 0 0 0 ++ marks 0 0 0 ++ ack_drop 0 0 0 ++ sp_flows 0 0 0 ++ bk_flows 0 0 0 ++ un_flows 0 0 0 ++ max_len 0 0 0 ++ quantum 300 1514 762 ++ ++After some use: ++.br ++# tc -s qdisc show dev eth0 ++ ++qdisc cake 1: root refcnt 2 bandwidth 100Mbit diffserv3 triple-isolate rtt 100.0ms noatm overhead 38 mpu 84 ++ Sent 44709231 bytes 31931 pkt (dropped 45, overlimits 93782 requeues 0) ++ backlog 33308b 22p requeues 0 ++ memory used: 292352b of 5000000b ++ capacity estimate: 100Mbit ++ min/max network layer size: 28 / 1500 ++ min/max overhead-adjusted size: 84 / 1538 ++ average network hdr offset: 14 ++ ++ Bulk Best Effort Voice ++ thresh 6250Kbit 100Mbit 25Mbit ++ target 5.0ms 5.0ms 5.0ms ++ interval 100.0ms 100.0ms 100.0ms ++ pk_delay 8.7ms 6.9ms 5.0ms ++ av_delay 4.9ms 5.3ms 3.8ms ++ sp_delay 727us 1.4ms 511us ++ pkts 2590 21271 8137 ++ bytes 3081804 30302659 11426206 ++ way_inds 0 46 0 ++ way_miss 3 17 4 ++ way_cols 0 0 0 ++ drops 20 15 10 ++ marks 0 0 0 ++ ack_drop 0 0 0 ++ sp_flows 2 4 1 ++ bk_flows 1 2 1 ++ un_flows 0 0 0 ++ max_len 1514 1514 1514 ++ quantum 300 1514 762 ++ ++.SH SEE ALSO ++.BR tc (8), ++.BR tc-codel (8), ++.BR tc-fq_codel (8), ++.BR tc-htb (8) ++ ++.SH AUTHORS ++Cake's principal author is Jonathan Morton, with contributions from ++Tony Ambardar, Kevin Darbyshire-Bryant, Toke Høiland-Jørgensen, ++Sebastian Moeller, Ryan Mounce, Dean Scarff, Nils Andreas Svee, and Dave Täht. ++ ++This manual page was written by Loganaden Velvindron. Please report corrections ++to the Linux Networking mailing list . +--- a/man/man8/tc.8 ++++ b/man/man8/tc.8 +@@ -795,6 +795,7 @@ was written by Alexey N. Kuznetsov and a + .BR tc-basic (8), + .BR tc-bfifo (8), + .BR tc-bpf (8), ++.BR tc-cake (8), + .BR tc-cbq (8), + .BR tc-cgroup (8), + .BR tc-choke (8), +--- a/tc/Makefile ++++ b/tc/Makefile +@@ -66,6 +66,7 @@ TCMODULES += q_codel.o + TCMODULES += q_fq_codel.o + TCMODULES += q_fq.o + TCMODULES += q_pie.o ++TCMODULES += q_cake.o + TCMODULES += q_hhf.o + TCMODULES += q_clsact.o + TCMODULES += e_bpf.o +--- /dev/null ++++ b/tc/q_cake.c +@@ -0,0 +1,805 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++ ++/* ++ * Common Applications Kept Enhanced -- CAKE ++ * ++ * Copyright (C) 2014-2018 Jonathan Morton ++ * Copyright (C) 2017-2018 Toke Høiland-Jørgensen ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "utils.h" ++#include "tc_util.h" ++ ++struct cake_preset { ++ char *name; ++ unsigned int target; ++ unsigned int interval; ++}; ++ ++static struct cake_preset presets[] = { ++ {"datacentre", 5, 100}, ++ {"lan", 50, 1000}, ++ {"metro", 500, 10000}, ++ {"regional", 1500, 30000}, ++ {"internet", 5000, 100000}, ++ {"oceanic", 15000, 300000}, ++ {"satellite", 50000, 1000000}, ++ {"interplanetary", 50000000, 1000000000}, ++}; ++ ++static const char * diffserv_names[CAKE_DIFFSERV_MAX] = { ++ [CAKE_DIFFSERV_DIFFSERV3] = "diffserv3", ++ [CAKE_DIFFSERV_DIFFSERV4] = "diffserv4", ++ [CAKE_DIFFSERV_DIFFSERV8] = "diffserv8", ++ [CAKE_DIFFSERV_BESTEFFORT] = "besteffort", ++ [CAKE_DIFFSERV_PRECEDENCE] = "precedence", ++}; ++ ++static const char * flowmode_names[CAKE_FLOW_MAX] = { ++ [CAKE_FLOW_NONE] = "flowblind", ++ [CAKE_FLOW_SRC_IP] = "srchost", ++ [CAKE_FLOW_DST_IP] = "dsthost", ++ [CAKE_FLOW_HOSTS] = "hosts", ++ [CAKE_FLOW_FLOWS] = "flows", ++ [CAKE_FLOW_DUAL_SRC] = "dual-srchost", ++ [CAKE_FLOW_DUAL_DST] = "dual-dsthost", ++ [CAKE_FLOW_TRIPLE] = "triple-isolate", ++}; ++ ++static struct cake_preset *find_preset(char *argv) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(presets); i++) ++ if (!strcmp(argv, presets[i].name)) ++ return &presets[i]; ++ return NULL; ++} ++ ++static void explain(void) ++{ ++ fprintf(stderr, ++"Usage: ... cake [ bandwidth RATE | unlimited* | autorate-ingress ]\n" ++" [ rtt TIME | datacentre | lan | metro | regional |\n" ++" internet* | oceanic | satellite | interplanetary ]\n" ++" [ besteffort | diffserv8 | diffserv4 | diffserv3* ]\n" ++" [ flowblind | srchost | dsthost | hosts | flows |\n" ++" dual-srchost | dual-dsthost | triple-isolate* ]\n" ++" [ nat | nonat* ]\n" ++" [ wash | nowash* ]\n" ++" [ split-gso* | no-split-gso ]\n" ++" [ ack-filter | ack-filter-aggressive | no-ack-filter* ]\n" ++" [ memlimit LIMIT ]\n" ++" [ ptm | atm | noatm* ] [ overhead N | conservative | raw* ]\n" ++" [ mpu N ] [ ingress | egress* ]\n" ++" (* marks defaults)\n"); ++} ++ ++static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv, ++ struct nlmsghdr *n, const char *dev) ++{ ++ struct cake_preset *preset, *preset_set = NULL; ++ bool overhead_override = false; ++ bool overhead_set = false; ++ unsigned int interval = 0; ++ unsigned int diffserv = 0; ++ unsigned int memlimit = 0; ++ unsigned int target = 0; ++ __u64 bandwidth = 0; ++ int ack_filter = -1; ++ struct rtattr *tail; ++ int split_gso = -1; ++ int unlimited = 0; ++ int flowmode = -1; ++ int autorate = -1; ++ int ingress = -1; ++ int overhead = 0; ++ int wash = -1; ++ int nat = -1; ++ int atm = -1; ++ int mpu = 0; ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "bandwidth") == 0) { ++ NEXT_ARG(); ++ if (get_rate64(&bandwidth, *argv)) { ++ fprintf(stderr, "Illegal \"bandwidth\"\n"); ++ return -1; ++ } ++ unlimited = 0; ++ autorate = 0; ++ } else if (strcmp(*argv, "unlimited") == 0) { ++ bandwidth = 0; ++ unlimited = 1; ++ autorate = 0; ++ } else if (strcmp(*argv, "autorate-ingress") == 0) { ++ autorate = 1; ++ } else if (strcmp(*argv, "rtt") == 0) { ++ NEXT_ARG(); ++ if (get_time(&interval, *argv)) { ++ fprintf(stderr, "Illegal \"rtt\"\n"); ++ return -1; ++ } ++ target = interval / 20; ++ if (!target) ++ target = 1; ++ } else if ((preset = find_preset(*argv))) { ++ if (preset_set) ++ duparg(*argv, preset_set->name); ++ preset_set = preset; ++ target = preset->target; ++ interval = preset->interval; ++ } else if (strcmp(*argv, "besteffort") == 0) { ++ diffserv = CAKE_DIFFSERV_BESTEFFORT; ++ } else if (strcmp(*argv, "precedence") == 0) { ++ diffserv = CAKE_DIFFSERV_PRECEDENCE; ++ } else if (strcmp(*argv, "diffserv8") == 0) { ++ diffserv = CAKE_DIFFSERV_DIFFSERV8; ++ } else if (strcmp(*argv, "diffserv4") == 0) { ++ diffserv = CAKE_DIFFSERV_DIFFSERV4; ++ } else if (strcmp(*argv, "diffserv") == 0) { ++ diffserv = CAKE_DIFFSERV_DIFFSERV4; ++ } else if (strcmp(*argv, "diffserv3") == 0) { ++ diffserv = CAKE_DIFFSERV_DIFFSERV3; ++ } else if (strcmp(*argv, "nowash") == 0) { ++ wash = 0; ++ } else if (strcmp(*argv, "wash") == 0) { ++ wash = 1; ++ } else if (strcmp(*argv, "split-gso") == 0) { ++ split_gso = 1; ++ } else if (strcmp(*argv, "no-split-gso") == 0) { ++ split_gso = 0; ++ } else if (strcmp(*argv, "flowblind") == 0) { ++ flowmode = CAKE_FLOW_NONE; ++ } else if (strcmp(*argv, "srchost") == 0) { ++ flowmode = CAKE_FLOW_SRC_IP; ++ } else if (strcmp(*argv, "dsthost") == 0) { ++ flowmode = CAKE_FLOW_DST_IP; ++ } else if (strcmp(*argv, "hosts") == 0) { ++ flowmode = CAKE_FLOW_HOSTS; ++ } else if (strcmp(*argv, "flows") == 0) { ++ flowmode = CAKE_FLOW_FLOWS; ++ } else if (strcmp(*argv, "dual-srchost") == 0) { ++ flowmode = CAKE_FLOW_DUAL_SRC; ++ } else if (strcmp(*argv, "dual-dsthost") == 0) { ++ flowmode = CAKE_FLOW_DUAL_DST; ++ } else if (strcmp(*argv, "triple-isolate") == 0) { ++ flowmode = CAKE_FLOW_TRIPLE; ++ } else if (strcmp(*argv, "nat") == 0) { ++ nat = 1; ++ } else if (strcmp(*argv, "nonat") == 0) { ++ nat = 0; ++ } else if (strcmp(*argv, "ptm") == 0) { ++ atm = CAKE_ATM_PTM; ++ } else if (strcmp(*argv, "atm") == 0) { ++ atm = CAKE_ATM_ATM; ++ } else if (strcmp(*argv, "noatm") == 0) { ++ atm = CAKE_ATM_NONE; ++ } else if (strcmp(*argv, "raw") == 0) { ++ atm = CAKE_ATM_NONE; ++ overhead = 0; ++ overhead_set = true; ++ overhead_override = true; ++ } else if (strcmp(*argv, "conservative") == 0) { ++ /* ++ * Deliberately over-estimate overhead: ++ * one whole ATM cell plus ATM framing. ++ * A safe choice if the actual overhead is unknown. ++ */ ++ atm = CAKE_ATM_ATM; ++ overhead = 48; ++ overhead_set = true; ++ ++ /* Various ADSL framing schemes, all over ATM cells */ ++ } else if (strcmp(*argv, "ipoa-vcmux") == 0) { ++ atm = CAKE_ATM_ATM; ++ overhead += 8; ++ overhead_set = true; ++ } else if (strcmp(*argv, "ipoa-llcsnap") == 0) { ++ atm = CAKE_ATM_ATM; ++ overhead += 16; ++ overhead_set = true; ++ } else if (strcmp(*argv, "bridged-vcmux") == 0) { ++ atm = CAKE_ATM_ATM; ++ overhead += 24; ++ overhead_set = true; ++ } else if (strcmp(*argv, "bridged-llcsnap") == 0) { ++ atm = CAKE_ATM_ATM; ++ overhead += 32; ++ overhead_set = true; ++ } else if (strcmp(*argv, "pppoa-vcmux") == 0) { ++ atm = CAKE_ATM_ATM; ++ overhead += 10; ++ overhead_set = true; ++ } else if (strcmp(*argv, "pppoa-llc") == 0) { ++ atm = CAKE_ATM_ATM; ++ overhead += 14; ++ overhead_set = true; ++ } else if (strcmp(*argv, "pppoe-vcmux") == 0) { ++ atm = CAKE_ATM_ATM; ++ overhead += 32; ++ overhead_set = true; ++ } else if (strcmp(*argv, "pppoe-llcsnap") == 0) { ++ atm = CAKE_ATM_ATM; ++ overhead += 40; ++ overhead_set = true; ++ ++ /* Typical VDSL2 framing schemes, both over PTM */ ++ /* PTM has 64b/65b coding which absorbs some bandwidth */ ++ } else if (strcmp(*argv, "pppoe-ptm") == 0) { ++ /* 2B PPP + 6B PPPoE + 6B dest MAC + 6B src MAC ++ * + 2B ethertype + 4B Frame Check Sequence ++ * + 1B Start of Frame (S) + 1B End of Frame (Ck) ++ * + 2B TC-CRC (PTM-FCS) = 30B ++ */ ++ atm = CAKE_ATM_PTM; ++ overhead += 30; ++ overhead_set = true; ++ } else if (strcmp(*argv, "bridged-ptm") == 0) { ++ /* 6B dest MAC + 6B src MAC + 2B ethertype ++ * + 4B Frame Check Sequence ++ * + 1B Start of Frame (S) + 1B End of Frame (Ck) ++ * + 2B TC-CRC (PTM-FCS) = 22B ++ */ ++ atm = CAKE_ATM_PTM; ++ overhead += 22; ++ overhead_set = true; ++ } else if (strcmp(*argv, "via-ethernet") == 0) { ++ /* ++ * We used to use this flag to manually compensate for ++ * Linux including the Ethernet header on Ethernet-type ++ * interfaces, but not on IP-type interfaces. ++ * ++ * It is no longer needed, because Cake now adjusts for ++ * that automatically, and is thus ignored. ++ * ++ * It would be deleted entirely, but it appears in the ++ * stats output when the automatic compensation is ++ * active. ++ */ ++ } else if (strcmp(*argv, "ethernet") == 0) { ++ /* ethernet pre-amble & interframe gap & FCS ++ * you may need to add vlan tag ++ */ ++ overhead += 38; ++ overhead_set = true; ++ mpu = 84; ++ ++ /* Additional Ethernet-related overhead used by some ISPs */ ++ } else if (strcmp(*argv, "ether-vlan") == 0) { ++ /* 802.1q VLAN tag - may be repeated */ ++ overhead += 4; ++ overhead_set = true; ++ ++ /* ++ * DOCSIS cable shapers account for Ethernet frame with FCS, ++ * but not interframe gap or preamble. ++ */ ++ } else if (strcmp(*argv, "docsis") == 0) { ++ atm = CAKE_ATM_NONE; ++ overhead += 18; ++ overhead_set = true; ++ mpu = 64; ++ } else if (strcmp(*argv, "overhead") == 0) { ++ char *p = NULL; ++ ++ NEXT_ARG(); ++ overhead = strtol(*argv, &p, 10); ++ if (!p || *p || !*argv || ++ overhead < -64 || overhead > 256) { ++ fprintf(stderr, ++ "Illegal \"overhead\", valid range is -64 to 256\\n"); ++ return -1; ++ } ++ overhead_set = true; ++ ++ } else if (strcmp(*argv, "mpu") == 0) { ++ char *p = NULL; ++ ++ NEXT_ARG(); ++ mpu = strtol(*argv, &p, 10); ++ if (!p || *p || !*argv || mpu < 0 || mpu > 256) { ++ fprintf(stderr, ++ "Illegal \"mpu\", valid range is 0 to 256\\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "ingress") == 0) { ++ ingress = 1; ++ } else if (strcmp(*argv, "egress") == 0) { ++ ingress = 0; ++ } else if (strcmp(*argv, "no-ack-filter") == 0) { ++ ack_filter = CAKE_ACK_NONE; ++ } else if (strcmp(*argv, "ack-filter") == 0) { ++ ack_filter = CAKE_ACK_FILTER; ++ } else if (strcmp(*argv, "ack-filter-aggressive") == 0) { ++ ack_filter = CAKE_ACK_AGGRESSIVE; ++ } else if (strcmp(*argv, "memlimit") == 0) { ++ NEXT_ARG(); ++ if (get_size(&memlimit, *argv)) { ++ fprintf(stderr, ++ "Illegal value for \"memlimit\": \"%s\"\n", *argv); ++ return -1; ++ } ++ } else if (strcmp(*argv, "help") == 0) { ++ explain(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ explain(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ if (bandwidth || unlimited) ++ addattr_l(n, 1024, TCA_CAKE_BASE_RATE64, &bandwidth, ++ sizeof(bandwidth)); ++ if (diffserv) ++ addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, ++ sizeof(diffserv)); ++ if (atm != -1) ++ addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm)); ++ if (flowmode != -1) ++ addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, ++ sizeof(flowmode)); ++ if (overhead_set) ++ addattr_l(n, 1024, TCA_CAKE_OVERHEAD, &overhead, ++ sizeof(overhead)); ++ if (overhead_override) { ++ unsigned int zero = 0; ++ ++ addattr_l(n, 1024, TCA_CAKE_RAW, &zero, sizeof(zero)); ++ } ++ if (mpu > 0) ++ addattr_l(n, 1024, TCA_CAKE_MPU, &mpu, sizeof(mpu)); ++ if (interval) ++ addattr_l(n, 1024, TCA_CAKE_RTT, &interval, sizeof(interval)); ++ if (target) ++ addattr_l(n, 1024, TCA_CAKE_TARGET, &target, sizeof(target)); ++ if (autorate != -1) ++ addattr_l(n, 1024, TCA_CAKE_AUTORATE, &autorate, ++ sizeof(autorate)); ++ if (memlimit) ++ addattr_l(n, 1024, TCA_CAKE_MEMORY, &memlimit, ++ sizeof(memlimit)); ++ if (nat != -1) ++ addattr_l(n, 1024, TCA_CAKE_NAT, &nat, sizeof(nat)); ++ if (wash != -1) ++ addattr_l(n, 1024, TCA_CAKE_WASH, &wash, sizeof(wash)); ++ if (split_gso != -1) ++ addattr_l(n, 1024, TCA_CAKE_SPLIT_GSO, &split_gso, ++ sizeof(split_gso)); ++ if (ingress != -1) ++ addattr_l(n, 1024, TCA_CAKE_INGRESS, &ingress, sizeof(ingress)); ++ if (ack_filter != -1) ++ addattr_l(n, 1024, TCA_CAKE_ACK_FILTER, &ack_filter, ++ sizeof(ack_filter)); ++ ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ return 0; ++} ++ ++static void cake_print_mode(unsigned int value, unsigned int max, ++ const char *key, const char **table) ++{ ++ if (value < max && table[value]) { ++ print_string(PRINT_ANY, key, "%s ", table[value]); ++ } else { ++ print_string(PRINT_JSON, key, NULL, "unknown"); ++ print_string(PRINT_FP, NULL, "(?%s?)", key); ++ } ++} ++ ++static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_CAKE_MAX + 1]; ++ unsigned int interval = 0; ++ unsigned int memlimit = 0; ++ __u64 bandwidth = 0; ++ int ack_filter = 0; ++ int split_gso = 0; ++ int overhead = 0; ++ int autorate = 0; ++ int ingress = 0; ++ int wash = 0; ++ int raw = 0; ++ int mpu = 0; ++ int atm = 0; ++ int nat = 0; ++ ++ SPRINT_BUF(b1); ++ SPRINT_BUF(b2); ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_CAKE_MAX, opt); ++ ++ if (tb[TCA_CAKE_BASE_RATE64] && ++ RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE64]) >= sizeof(bandwidth)) { ++ bandwidth = rta_getattr_u64(tb[TCA_CAKE_BASE_RATE64]); ++ if (bandwidth) { ++ print_uint(PRINT_JSON, "bandwidth", NULL, bandwidth); ++ print_string(PRINT_FP, NULL, "bandwidth %s ", ++ sprint_rate(bandwidth, b1)); ++ } else ++ print_string(PRINT_ANY, "bandwidth", "bandwidth %s ", ++ "unlimited"); ++ } ++ if (tb[TCA_CAKE_AUTORATE] && ++ RTA_PAYLOAD(tb[TCA_CAKE_AUTORATE]) >= sizeof(__u32)) { ++ autorate = rta_getattr_u32(tb[TCA_CAKE_AUTORATE]); ++ if (autorate == 1) ++ print_string(PRINT_ANY, "autorate", "%s ", ++ "autorate-ingress"); ++ else if (autorate) ++ print_string(PRINT_ANY, "autorate", "(?autorate?) ", ++ "unknown"); ++ } ++ if (tb[TCA_CAKE_DIFFSERV_MODE] && ++ RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) { ++ cake_print_mode(rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]), ++ CAKE_DIFFSERV_MAX, "diffserv", diffserv_names); ++ } ++ if (tb[TCA_CAKE_FLOW_MODE] && ++ RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) { ++ cake_print_mode(rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]), ++ CAKE_FLOW_MAX, "flowmode", flowmode_names); ++ } ++ ++ if (tb[TCA_CAKE_NAT] && ++ RTA_PAYLOAD(tb[TCA_CAKE_NAT]) >= sizeof(__u32)) { ++ nat = rta_getattr_u32(tb[TCA_CAKE_NAT]); ++ } ++ ++ if (nat) ++ print_string(PRINT_FP, NULL, "nat ", NULL); ++ else ++ print_string(PRINT_FP, NULL, "nonat ", NULL); ++ print_bool(PRINT_JSON, "nat", NULL, nat); ++ ++ if (tb[TCA_CAKE_WASH] && ++ RTA_PAYLOAD(tb[TCA_CAKE_WASH]) >= sizeof(__u32)) { ++ wash = rta_getattr_u32(tb[TCA_CAKE_WASH]); ++ } ++ if (tb[TCA_CAKE_ATM] && ++ RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) { ++ atm = rta_getattr_u32(tb[TCA_CAKE_ATM]); ++ } ++ if (tb[TCA_CAKE_OVERHEAD] && ++ RTA_PAYLOAD(tb[TCA_CAKE_OVERHEAD]) >= sizeof(__s32)) { ++ overhead = *(__s32 *) RTA_DATA(tb[TCA_CAKE_OVERHEAD]); ++ } ++ if (tb[TCA_CAKE_MPU] && ++ RTA_PAYLOAD(tb[TCA_CAKE_MPU]) >= sizeof(__u32)) { ++ mpu = rta_getattr_u32(tb[TCA_CAKE_MPU]); ++ } ++ if (tb[TCA_CAKE_INGRESS] && ++ RTA_PAYLOAD(tb[TCA_CAKE_INGRESS]) >= sizeof(__u32)) { ++ ingress = rta_getattr_u32(tb[TCA_CAKE_INGRESS]); ++ } ++ if (tb[TCA_CAKE_ACK_FILTER] && ++ RTA_PAYLOAD(tb[TCA_CAKE_ACK_FILTER]) >= sizeof(__u32)) { ++ ack_filter = rta_getattr_u32(tb[TCA_CAKE_ACK_FILTER]); ++ } ++ if (tb[TCA_CAKE_SPLIT_GSO] && ++ RTA_PAYLOAD(tb[TCA_CAKE_SPLIT_GSO]) >= sizeof(__u32)) { ++ split_gso = rta_getattr_u32(tb[TCA_CAKE_SPLIT_GSO]); ++ } ++ if (tb[TCA_CAKE_RAW]) { ++ raw = 1; ++ } ++ if (tb[TCA_CAKE_RTT] && ++ RTA_PAYLOAD(tb[TCA_CAKE_RTT]) >= sizeof(__u32)) { ++ interval = rta_getattr_u32(tb[TCA_CAKE_RTT]); ++ } ++ ++ if (wash) ++ print_string(PRINT_FP, NULL, "wash ", NULL); ++ else ++ print_string(PRINT_FP, NULL, "nowash ", NULL); ++ print_bool(PRINT_JSON, "wash", NULL, wash); ++ ++ if (ingress) ++ print_string(PRINT_FP, NULL, "ingress ", NULL); ++ print_bool(PRINT_JSON, "ingress", NULL, ingress); ++ ++ if (ack_filter == CAKE_ACK_AGGRESSIVE) ++ print_string(PRINT_ANY, "ack-filter", "ack-filter-%s ", ++ "aggressive"); ++ else if (ack_filter == CAKE_ACK_FILTER) ++ print_string(PRINT_ANY, "ack-filter", "ack-filter ", "enabled"); ++ else ++ print_string(PRINT_ANY, "ack-filter", "no-ack-filter ", "disabled"); ++ ++ if (split_gso) ++ print_string(PRINT_FP, NULL, "split-gso ", NULL); ++ else ++ print_string(PRINT_FP, NULL, "no-split-gso ", NULL); ++ print_bool(PRINT_JSON, "split_gso", NULL, split_gso); ++ ++ if (interval) ++ print_string(PRINT_FP, NULL, "rtt %s ", ++ sprint_time(interval, b2)); ++ print_uint(PRINT_JSON, "rtt", NULL, interval); ++ ++ if (raw) ++ print_string(PRINT_FP, NULL, "raw ", NULL); ++ print_bool(PRINT_JSON, "raw", NULL, raw); ++ ++ if (atm == CAKE_ATM_ATM) ++ print_string(PRINT_ANY, "atm", "%s ", "atm"); ++ else if (atm == CAKE_ATM_PTM) ++ print_string(PRINT_ANY, "atm", "%s ", "ptm"); ++ else if (!raw) ++ print_string(PRINT_ANY, "atm", "%s ", "noatm"); ++ ++ print_int(PRINT_ANY, "overhead", "overhead %d ", overhead); ++ ++ if (mpu) ++ print_uint(PRINT_ANY, "mpu", "mpu %u ", mpu); ++ ++ if (memlimit) { ++ print_uint(PRINT_JSON, "memlimit", NULL, memlimit); ++ print_string(PRINT_FP, NULL, "memlimit %s", ++ sprint_size(memlimit, b1)); ++ } ++ ++ return 0; ++} ++ ++static void cake_print_json_tin(struct rtattr **tstat) ++{ ++#define PRINT_TSTAT_JSON(type, name, attr) if (tstat[TCA_CAKE_TIN_STATS_ ## attr]) \ ++ print_u64(PRINT_JSON, name, NULL, \ ++ rta_getattr_ ## type((struct rtattr *) \ ++ tstat[TCA_CAKE_TIN_STATS_ ## attr])) ++ ++ open_json_object(NULL); ++ PRINT_TSTAT_JSON(u64, "threshold_rate", THRESHOLD_RATE64); ++ PRINT_TSTAT_JSON(u64, "sent_bytes", SENT_BYTES64); ++ PRINT_TSTAT_JSON(u32, "backlog_bytes", BACKLOG_BYTES); ++ PRINT_TSTAT_JSON(u32, "target_us", TARGET_US); ++ PRINT_TSTAT_JSON(u32, "interval_us", INTERVAL_US); ++ PRINT_TSTAT_JSON(u32, "peak_delay_us", PEAK_DELAY_US); ++ PRINT_TSTAT_JSON(u32, "avg_delay_us", AVG_DELAY_US); ++ PRINT_TSTAT_JSON(u32, "base_delay_us", BASE_DELAY_US); ++ PRINT_TSTAT_JSON(u32, "sent_packets", SENT_PACKETS); ++ PRINT_TSTAT_JSON(u32, "way_indirect_hits", WAY_INDIRECT_HITS); ++ PRINT_TSTAT_JSON(u32, "way_misses", WAY_MISSES); ++ PRINT_TSTAT_JSON(u32, "way_collisions", WAY_COLLISIONS); ++ PRINT_TSTAT_JSON(u32, "drops", DROPPED_PACKETS); ++ PRINT_TSTAT_JSON(u32, "ecn_mark", ECN_MARKED_PACKETS); ++ PRINT_TSTAT_JSON(u32, "ack_drops", ACKS_DROPPED_PACKETS); ++ PRINT_TSTAT_JSON(u32, "sparse_flows", SPARSE_FLOWS); ++ PRINT_TSTAT_JSON(u32, "bulk_flows", BULK_FLOWS); ++ PRINT_TSTAT_JSON(u32, "unresponsive_flows", UNRESPONSIVE_FLOWS); ++ PRINT_TSTAT_JSON(u32, "max_pkt_len", MAX_SKBLEN); ++ PRINT_TSTAT_JSON(u32, "flow_quantum", FLOW_QUANTUM); ++ close_json_object(); ++ ++#undef PRINT_TSTAT_JSON ++} ++ ++static int cake_print_xstats(struct qdisc_util *qu, FILE *f, ++ struct rtattr *xstats) ++{ ++ struct rtattr *st[TCA_CAKE_STATS_MAX + 1]; ++ SPRINT_BUF(b1); ++ int i; ++ ++ if (xstats == NULL) ++ return 0; ++ ++#define GET_STAT_U32(attr) rta_getattr_u32(st[TCA_CAKE_STATS_ ## attr]) ++#define GET_STAT_S32(attr) (*(__s32 *)RTA_DATA(st[TCA_CAKE_STATS_ ## attr])) ++#define GET_STAT_U64(attr) rta_getattr_u64(st[TCA_CAKE_STATS_ ## attr]) ++ ++ parse_rtattr_nested(st, TCA_CAKE_STATS_MAX, xstats); ++ ++ if (st[TCA_CAKE_STATS_MEMORY_USED] && ++ st[TCA_CAKE_STATS_MEMORY_LIMIT]) { ++ print_string(PRINT_FP, NULL, " memory used: %s", ++ sprint_size(GET_STAT_U32(MEMORY_USED), b1)); ++ ++ print_string(PRINT_FP, NULL, " of %s\n", ++ sprint_size(GET_STAT_U32(MEMORY_LIMIT), b1)); ++ ++ print_uint(PRINT_JSON, "memory_used", NULL, ++ GET_STAT_U32(MEMORY_USED)); ++ print_uint(PRINT_JSON, "memory_limit", NULL, ++ GET_STAT_U32(MEMORY_LIMIT)); ++ } ++ ++ if (st[TCA_CAKE_STATS_CAPACITY_ESTIMATE64]) { ++ print_string(PRINT_FP, NULL, " capacity estimate: %s\n", ++ sprint_rate(GET_STAT_U64(CAPACITY_ESTIMATE64), b1)); ++ print_uint(PRINT_JSON, "capacity_estimate", NULL, ++ GET_STAT_U64(CAPACITY_ESTIMATE64)); ++ } ++ ++ if (st[TCA_CAKE_STATS_MIN_NETLEN] && ++ st[TCA_CAKE_STATS_MAX_NETLEN]) { ++ print_uint(PRINT_ANY, "min_network_size", ++ " min/max network layer size: %12u", ++ GET_STAT_U32(MIN_NETLEN)); ++ print_uint(PRINT_ANY, "max_network_size", ++ " /%8u\n", GET_STAT_U32(MAX_NETLEN)); ++ } ++ ++ if (st[TCA_CAKE_STATS_MIN_ADJLEN] && ++ st[TCA_CAKE_STATS_MAX_ADJLEN]) { ++ print_uint(PRINT_ANY, "min_adj_size", ++ " min/max overhead-adjusted size: %8u", ++ GET_STAT_U32(MIN_ADJLEN)); ++ print_uint(PRINT_ANY, "max_adj_size", ++ " /%8u\n", GET_STAT_U32(MAX_ADJLEN)); ++ } ++ ++ if (st[TCA_CAKE_STATS_AVG_NETOFF]) ++ print_uint(PRINT_ANY, "avg_hdr_offset", ++ " average network hdr offset: %12u\n\n", ++ GET_STAT_U32(AVG_NETOFF)); ++ ++ /* class stats */ ++ if (st[TCA_CAKE_STATS_DEFICIT]) ++ print_int(PRINT_ANY, "deficit", " deficit %u", ++ GET_STAT_S32(DEFICIT)); ++ if (st[TCA_CAKE_STATS_COBALT_COUNT]) ++ print_uint(PRINT_ANY, "count", " count %u", ++ GET_STAT_U32(COBALT_COUNT)); ++ ++ if (st[TCA_CAKE_STATS_DROPPING] && GET_STAT_U32(DROPPING)) { ++ print_bool(PRINT_ANY, "dropping", " dropping", true); ++ if (st[TCA_CAKE_STATS_DROP_NEXT_US]) { ++ int drop_next = GET_STAT_S32(DROP_NEXT_US); ++ ++ if (drop_next < 0) { ++ print_string(PRINT_FP, NULL, " drop_next -%s", ++ sprint_time(drop_next, b1)); ++ } else { ++ print_uint(PRINT_JSON, "drop_next", NULL, ++ drop_next); ++ print_string(PRINT_FP, NULL, " drop_next %s", ++ sprint_time(drop_next, b1)); ++ } ++ } ++ } ++ ++ if (st[TCA_CAKE_STATS_P_DROP]) { ++ print_uint(PRINT_ANY, "blue_prob", " blue_prob %u", ++ GET_STAT_U32(P_DROP)); ++ if (st[TCA_CAKE_STATS_BLUE_TIMER_US]) { ++ int blue_timer = GET_STAT_S32(BLUE_TIMER_US); ++ ++ if (blue_timer < 0) { ++ print_string(PRINT_FP, NULL, " blue_timer -%s", ++ sprint_time(blue_timer, b1)); ++ } else { ++ print_uint(PRINT_JSON, "blue_timer", NULL, ++ blue_timer); ++ print_string(PRINT_FP, NULL, " blue_timer %s", ++ sprint_time(blue_timer, b1)); ++ } ++ } ++ } ++ ++#undef GET_STAT_U32 ++#undef GET_STAT_S32 ++#undef GET_STAT_U64 ++ ++ if (st[TCA_CAKE_STATS_TIN_STATS]) { ++ struct rtattr *tstat[TC_CAKE_MAX_TINS][TCA_CAKE_TIN_STATS_MAX + 1]; ++ struct rtattr *tins[TC_CAKE_MAX_TINS + 1]; ++ int num_tins = 0; ++ ++ parse_rtattr_nested(tins, TC_CAKE_MAX_TINS, ++ st[TCA_CAKE_STATS_TIN_STATS]); ++ ++ for (i = 1; i <= TC_CAKE_MAX_TINS && tins[i]; i++) { ++ parse_rtattr_nested(tstat[i-1], TCA_CAKE_TIN_STATS_MAX, ++ tins[i]); ++ num_tins++; ++ } ++ ++ if (!num_tins) ++ return 0; ++ ++ if (is_json_context()) { ++ open_json_array(PRINT_JSON, "tins"); ++ for (i = 0; i < num_tins; i++) ++ cake_print_json_tin(tstat[i]); ++ close_json_array(PRINT_JSON, NULL); ++ ++ return 0; ++ } ++ ++ ++ switch (num_tins) { ++ case 3: ++ fprintf(f, " Bulk Best Effort Voice\n"); ++ break; ++ ++ case 4: ++ fprintf(f, " Bulk Best Effort Video Voice\n"); ++ break; ++ ++ default: ++ fprintf(f, " "); ++ for (i = 0; i < num_tins; i++) ++ fprintf(f, " Tin %u", i); ++ fprintf(f, "\n"); ++ }; ++ ++#define GET_TSTAT(i, attr) (tstat[i][TCA_CAKE_TIN_STATS_ ## attr]) ++#define PRINT_TSTAT(name, attr, fmts, val) do { \ ++ if (GET_TSTAT(0, attr)) { \ ++ fprintf(f, name); \ ++ for (i = 0; i < num_tins; i++) \ ++ fprintf(f, " %12" fmts, val); \ ++ fprintf(f, "\n"); \ ++ } \ ++ } while (0) ++ ++#define SPRINT_TSTAT(pfunc, type, name, attr) PRINT_TSTAT( \ ++ name, attr, "s", sprint_ ## pfunc( \ ++ rta_getattr_ ## type(GET_TSTAT(i, attr)), b1)) ++ ++#define PRINT_TSTAT_U32(name, attr) PRINT_TSTAT( \ ++ name, attr, "u", rta_getattr_u32(GET_TSTAT(i, attr))) ++ ++#define PRINT_TSTAT_U64(name, attr) PRINT_TSTAT( \ ++ name, attr, "llu", rta_getattr_u64(GET_TSTAT(i, attr))) ++ ++ SPRINT_TSTAT(rate, u64, " thresh ", THRESHOLD_RATE64); ++ SPRINT_TSTAT(time, u32, " target ", TARGET_US); ++ SPRINT_TSTAT(time, u32, " interval", INTERVAL_US); ++ SPRINT_TSTAT(time, u32, " pk_delay", PEAK_DELAY_US); ++ SPRINT_TSTAT(time, u32, " av_delay", AVG_DELAY_US); ++ SPRINT_TSTAT(time, u32, " sp_delay", BASE_DELAY_US); ++ SPRINT_TSTAT(size, u32, " backlog ", BACKLOG_BYTES); ++ ++ PRINT_TSTAT_U32(" pkts ", SENT_PACKETS); ++ PRINT_TSTAT_U64(" bytes ", SENT_BYTES64); ++ ++ PRINT_TSTAT_U32(" way_inds", WAY_INDIRECT_HITS); ++ PRINT_TSTAT_U32(" way_miss", WAY_MISSES); ++ PRINT_TSTAT_U32(" way_cols", WAY_COLLISIONS); ++ PRINT_TSTAT_U32(" drops ", DROPPED_PACKETS); ++ PRINT_TSTAT_U32(" marks ", ECN_MARKED_PACKETS); ++ PRINT_TSTAT_U32(" ack_drop", ACKS_DROPPED_PACKETS); ++ PRINT_TSTAT_U32(" sp_flows", SPARSE_FLOWS); ++ PRINT_TSTAT_U32(" bk_flows", BULK_FLOWS); ++ PRINT_TSTAT_U32(" un_flows", UNRESPONSIVE_FLOWS); ++ PRINT_TSTAT_U32(" max_len ", MAX_SKBLEN); ++ PRINT_TSTAT_U32(" quantum ", FLOW_QUANTUM); ++ ++#undef GET_STAT ++#undef PRINT_TSTAT ++#undef SPRINT_TSTAT ++#undef PRINT_TSTAT_U32 ++#undef PRINT_TSTAT_U64 ++ } ++ return 0; ++} ++ ++struct qdisc_util cake_qdisc_util = { ++ .id = "cake", ++ .parse_qopt = cake_parse_opt, ++ .print_qopt = cake_print_opt, ++ .print_xstats = cake_print_xstats, ++}; diff --git a/package/network/utils/iproute2/patches/190-link-libelf-to-tc-only.patch b/package/network/utils/iproute2/patches/190-link-libelf-to-tc-only.patch deleted file mode 100644 index 1c44616253..0000000000 --- a/package/network/utils/iproute2/patches/190-link-libelf-to-tc-only.patch +++ /dev/null @@ -1,60 +0,0 @@ ---- a/configure -+++ b/configure -@@ -257,8 +257,9 @@ check_elf() - echo "HAVE_ELF:=y" >>$CONFIG - echo "yes" - -- echo 'CFLAGS += -DHAVE_ELF' `${PKG_CONFIG} libelf --cflags` >> $CONFIG -- echo 'LDLIBS += ' `${PKG_CONFIG} libelf --libs` >>$CONFIG -+ echo 'CFLAGS += -DHAVE_ELF' >> $CONFIG -+ echo 'ELF_CFLAGS += ' `${PKG_CONFIG} libelf --cflags` >> $CONFIG -+ echo 'ELF_LDLIBS += ' `${PKG_CONFIG} libelf --libs` >>$CONFIG - else - echo "no" - fi ---- a/lib/Makefile -+++ b/lib/Makefile -@@ -11,9 +11,17 @@ UTILOBJ = utils.o rt_names.o ll_map.o ll - inet_proto.o namespace.o json_writer.o json_print.o \ - names.o color.o bpf.o exec.o fs.o - -+ELFOBJ=$(patsubst %.o,%.elf.o,$(UTILOBJ)) -+ - NLOBJ=libgenl.o libnetlink.o - --all: libnetlink.a libutil.a -+all: libnetlink.a libutil.a libutil-elf.a -+ -+%.o: %.c -+ $(QUIET_CC)$(CC) $(CFLAGS) -UHAVE_ELF $(EXTRA_CFLAGS) -c -o $@ $< -+ -+%.elf.o: %.c -+ $(QUIET_CC)$(CC) $(CFLAGS) $(ELF_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< - - libnetlink.a: $(NLOBJ) - $(QUIET_AR)$(AR) rcs $@ $^ -@@ -21,7 +29,10 @@ libnetlink.a: $(NLOBJ) - libutil.a: $(UTILOBJ) $(ADDLIB) - $(QUIET_AR)$(AR) rcs $@ $^ - -+libutil-elf.a: $(ELFOBJ) $(ADDLIB) -+ $(QUIET_AR)$(AR) rcs $@ $^ -+ - install: - - clean: -- rm -f $(NLOBJ) $(UTILOBJ) $(ADDLIB) libnetlink.a libutil.a -+ rm -f $(NLOBJ) $(UTILOBJ) $(ELFOBJ) $(ADDLIB) libnetlink.a libutil.a libutil-elf.a ---- a/tc/Makefile -+++ b/tc/Makefile -@@ -132,8 +132,8 @@ MODDESTDIR := $(DESTDIR)$(LIBDIR)/tc - - all: tc $(TCSO) - --tc: $(TCOBJ) $(LIBNETLINK) libtc.a -- $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ -+tc: $(TCOBJ) $(subst libutil.a,libutil-elf.a,$(LIBNETLINK)) libtc.a -+ $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) $(ELF_LDLIBS) -o $@ - - libtc.a: $(TCLIB) - $(QUIET_AR)$(AR) rcs $@ $^