From 058fd55efa7b2662be372cd577239774998d32e0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 05:52:54 +0900 Subject: [PATCH 01/12] network: tc: drop unused QDisc::family element --- src/network/tc/qdisc.c | 5 +---- src/network/tc/qdisc.h | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 803c999855b..7b41730fd17 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -50,7 +50,6 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) { *qdisc = (QDisc) { .meta.kind = TC_KIND_QDISC, - .family = AF_UNSPEC, .parent = TC_H_ROOT, .kind = kind, }; @@ -60,7 +59,6 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) { return -ENOMEM; qdisc->meta.kind = TC_KIND_QDISC, - qdisc->family = AF_UNSPEC; qdisc->parent = TC_H_ROOT; qdisc->kind = kind; @@ -115,7 +113,6 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns return r; if (q) { - qdisc->family = q->family; qdisc->handle = q->handle; qdisc->parent = q->parent; qdisc->tca_kind = TAKE_PTR(q->tca_kind); @@ -182,7 +179,7 @@ int qdisc_configure(Link *link, QDisc *qdisc) { assert(link->manager->rtnl); assert(link->ifindex > 0); - r = sd_rtnl_message_new_qdisc(link->manager->rtnl, &req, RTM_NEWQDISC, qdisc->family, link->ifindex); + r = sd_rtnl_message_new_qdisc(link->manager->rtnl, &req, RTM_NEWQDISC, AF_UNSPEC, link->ifindex); if (r < 0) return log_link_debug_errno(link, r, "Could not create RTM_NEWQDISC message: %m"); diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index e37c4806e32..8329d18b4f5 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -40,7 +40,6 @@ typedef struct QDisc { ConfigSection *section; Network *network; - int family; uint32_t handle; uint32_t parent; From 55c6f705bada3ab2b0d81b1bdc74253436d57ebc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 06:42:23 +0900 Subject: [PATCH 02/12] network: tc: drop unnecessary conditions As TC_H_UNSPEC is 0. --- src/network/tc/qdisc.c | 14 +++++--------- src/network/tc/tclass.c | 8 +++----- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 7b41730fd17..c7a7bad1686 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -187,11 +187,9 @@ int qdisc_configure(Link *link, QDisc *qdisc) { if (r < 0) return r; - if (qdisc->handle != TC_H_UNSPEC) { - r = sd_rtnl_message_set_qdisc_handle(req, qdisc->handle); - if (r < 0) - return r; - } + r = sd_rtnl_message_set_qdisc_handle(req, qdisc->handle); + if (r < 0) + return r; if (QDISC_VTABLE(qdisc)) { if (QDISC_VTABLE(qdisc)->fill_tca_kind) { @@ -290,11 +288,9 @@ int config_parse_qdisc_parent( return 0; } - if (streq(rvalue, "root")) { + if (streq(rvalue, "root")) qdisc->parent = TC_H_ROOT; - if (qdisc->handle == 0) - qdisc->handle = TC_H_UNSPEC; - } else if (streq(rvalue, "clsact")) { + else if (streq(rvalue, "clsact")) { qdisc->parent = TC_H_CLSACT; qdisc->handle = TC_H_MAKE(TC_H_CLSACT, 0); } else if (streq(rvalue, "ingress")) { diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index 6a36ac1c1cd..4e1386b48ff 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -145,11 +145,9 @@ int tclass_configure(Link *link, TClass *tclass) { if (r < 0) return r; - if (tclass->classid != TC_H_UNSPEC) { - r = sd_rtnl_message_set_tclass_handle(req, tclass->classid); - if (r < 0) - return r; - } + r = sd_rtnl_message_set_tclass_handle(req, tclass->classid); + if (r < 0) + return r; r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind); if (r < 0) From 89346ac6f6ca8b761cf47e72b57aa431d2bcdf6f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 14:28:17 +0900 Subject: [PATCH 03/12] network: coding style fixes --- src/network/netdev/l2tp-tunnel.c | 2 +- src/network/networkd-dhcp-common.c | 12 ++++++------ src/network/networkd-ipv4ll.c | 2 +- src/network/networkd-nexthop.c | 2 +- src/network/networkd-queue.c | 9 ++++----- src/network/networkd-wifi.c | 4 ++-- src/network/tc/fifo.c | 4 ++-- src/network/tc/tc.c | 4 ++-- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/network/netdev/l2tp-tunnel.c b/src/network/netdev/l2tp-tunnel.c index 419541ebdf3..50f52f5d9b7 100644 --- a/src/network/netdev/l2tp-tunnel.c +++ b/src/network/netdev/l2tp-tunnel.c @@ -117,7 +117,7 @@ static int netdev_l2tp_create_message_tunnel(NetDev *netdev, union in_addr_union if (r < 0) return r; - switch(t->l2tp_encap_type) { + switch (t->l2tp_encap_type) { case NETDEV_L2TP_ENCAPTYPE_IP: encap_type = L2TP_ENCAPTYPE_IP; break; diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 5331cdcead9..f825c8b1857 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -337,7 +337,7 @@ int config_parse_dhcp_or_ra_route_metric( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_route_metric = metric; network->dhcp_route_metric_set = true; @@ -388,7 +388,7 @@ int config_parse_dhcp_use_dns( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_use_dns = r; network->dhcp_use_dns_set = true; @@ -439,7 +439,7 @@ int config_parse_dhcp_use_domains( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_use_domains = d; network->dhcp_use_domains_set = true; @@ -490,7 +490,7 @@ int config_parse_dhcp_use_ntp( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_use_ntp = r; network->dhcp_use_ntp_set = true; @@ -542,7 +542,7 @@ int config_parse_dhcp_or_ra_route_table( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_route_table = rt; network->dhcp_route_table_set = true; @@ -777,7 +777,7 @@ int config_parse_dhcp_send_option( return 0; } - switch(type) { + switch (type) { case DHCP_OPTION_DATA_UINT8:{ r = safe_atou8(p, &uint8_data); if (r < 0) { diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index d05182fde7d..f1d40fb99d4 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -117,7 +117,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - switch(event) { + switch (event) { case SD_IPV4LL_EVENT_STOP: r = ipv4ll_address_lost(link); if (r < 0) { diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index b2bc0dc570f..f2049fa50a0 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -1220,7 +1220,7 @@ int config_parse_nexthop_family( return 0; } - switch(a) { + switch (a) { case ADDRESS_FAMILY_IPV4: n->family = AF_INET; break; diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 14dc1cf4926..1aa4bbae554 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -18,7 +18,7 @@ #include "networkd-setlink.h" static void request_free_object(RequestType type, void *object) { - switch(type) { + switch (type) { case REQUEST_TYPE_ACTIVATE_LINK: break; case REQUEST_TYPE_ADDRESS: @@ -100,7 +100,7 @@ static void request_hash_func(const Request *req, struct siphash *state) { siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state); siphash24_compress(&req->type, sizeof(req->type), state); - switch(req->type) { + switch (req->type) { case REQUEST_TYPE_ACTIVATE_LINK: break; case REQUEST_TYPE_ADDRESS: @@ -140,10 +140,9 @@ static void request_hash_func(const Request *req, struct siphash *state) { case REQUEST_TYPE_ROUTING_POLICY_RULE: routing_policy_rule_hash_func(req->rule, state); break; - case REQUEST_TYPE_SET_LINK: { + case REQUEST_TYPE_SET_LINK: trivial_hash_func(req->set_link_operation_ptr, state); break; - } case REQUEST_TYPE_UP_DOWN: break; default: @@ -296,7 +295,7 @@ int manager_process_requests(sd_event_source *s, void *userdata) { Request *req; ORDERED_SET_FOREACH(req, manager->request_queue) { - switch(req->type) { + switch (req->type) { case REQUEST_TYPE_ACTIVATE_LINK: r = request_process_activation(req); break; diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c index 8fd1cf5039f..996e600492f 100644 --- a/src/network/networkd-wifi.c +++ b/src/network/networkd-wifi.c @@ -132,7 +132,7 @@ int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *me strna(nl80211_cmd_to_string(cmd)), cmd, strna(nl80211_iftype_to_string(wlan_iftype)), strna(ssid)); - switch(cmd) { + switch (cmd) { case NL80211_CMD_SET_INTERFACE: case NL80211_CMD_NEW_INTERFACE: link->wlan_iftype = wlan_iftype; @@ -200,7 +200,7 @@ int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *mess return 0; } - switch(cmd) { + switch (cmd) { case NL80211_CMD_NEW_STATION: case NL80211_CMD_DEL_STATION: { struct ether_addr bssid; diff --git a/src/network/tc/fifo.c b/src/network/tc/fifo.c index 4f6e8fa613a..948d0718630 100644 --- a/src/network/tc/fifo.c +++ b/src/network/tc/fifo.c @@ -18,7 +18,7 @@ static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) assert(qdisc); assert(req); - switch(qdisc->kind) { + switch (qdisc->kind) { case QDISC_KIND_PFIFO: assert_se(fifo = PFIFO(qdisc)); break; @@ -71,7 +71,7 @@ int config_parse_pfifo_size( return 0; } - switch(qdisc->kind) { + switch (qdisc->kind) { case QDISC_KIND_PFIFO: fifo = PFIFO(qdisc); break; diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c index 8f76809c7d3..c0f6989b1b6 100644 --- a/src/network/tc/tc.c +++ b/src/network/tc/tc.c @@ -25,7 +25,7 @@ static int traffic_control_configure(Link *link, TrafficControl *tc) { assert(link); assert(tc); - switch(tc->kind) { + switch (tc->kind) { case TC_KIND_QDISC: return qdisc_configure(link, TC_TO_QDISC(tc)); case TC_KIND_TCLASS: @@ -66,7 +66,7 @@ int link_configure_traffic_control(Link *link) { static int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) { assert(tc); - switch(tc->kind) { + switch (tc->kind) { case TC_KIND_QDISC: return qdisc_section_verify(TC_TO_QDISC(tc), qdisc_has_root, qdisc_has_clsact); case TC_KIND_TCLASS: From f50b93fed75e2f8a583e7a91160104d2d6aa8eb8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 06:00:55 +0900 Subject: [PATCH 04/12] sd-netlink: unify sd_rtnl_message_new_qdisc() and sd_rtnl_message_new_tclass() As the netlink message header types for both qdisc and tclass are equivalent. --- .../sd-netlink/netlink-message-rtnl.c | 79 +++---------------- src/libsystemd/sd-netlink/netlink-util.h | 10 +-- src/network/tc/qdisc.c | 11 +-- src/network/tc/tclass.c | 11 +-- src/systemd/sd-netlink.h | 9 +-- 5 files changed, 21 insertions(+), 99 deletions(-) diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c index d15ca06aa85..888d097d986 100644 --- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c @@ -1043,92 +1043,35 @@ int sd_rtnl_message_routing_policy_rule_get_fib_src_prefixlen(sd_netlink_message return 0; } -int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) { +int sd_rtnl_message_new_traffic_control( + sd_netlink *rtnl, + sd_netlink_message **ret, + uint16_t nlmsg_type, + int ifindex, + uint32_t handle, + uint32_t parent) { + struct tcmsg *tcm; int r; - assert_return(rtnl_message_type_is_qdisc(nlmsg_type), -EINVAL); + assert_return(rtnl_message_type_is_traffic_control(nlmsg_type), -EINVAL); assert_return(ret, -EINVAL); r = message_new(rtnl, ret, nlmsg_type); if (r < 0) return r; - if (nlmsg_type == RTM_NEWQDISC) + if (IN_SET(nlmsg_type, RTM_NEWQDISC, RTM_NEWTCLASS)) (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; tcm = NLMSG_DATA((*ret)->hdr); - tcm->tcm_family = tcm_family; - tcm->tcm_ifindex = tcm_ifindex; - - return 0; -} - -int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent) { - struct tcmsg *tcm; - - assert_return(rtnl_message_type_is_qdisc(m->hdr->nlmsg_type), -EINVAL); - - tcm = NLMSG_DATA(m->hdr); - tcm->tcm_parent = parent; - - return 0; -} - -int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle) { - struct tcmsg *tcm; - - assert_return(rtnl_message_type_is_qdisc(m->hdr->nlmsg_type), -EINVAL); - - tcm = NLMSG_DATA(m->hdr); + tcm->tcm_ifindex = ifindex; tcm->tcm_handle = handle; - - return 0; -} - -int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) { - struct tcmsg *tcm; - int r; - - assert_return(rtnl_message_type_is_tclass(nlmsg_type), -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_NEWTCLASS) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; - - tcm = NLMSG_DATA((*ret)->hdr); - tcm->tcm_family = tcm_family; - tcm->tcm_ifindex = tcm_ifindex; - - return 0; -} - -int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent) { - struct tcmsg *tcm; - - assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL); - - tcm = NLMSG_DATA(m->hdr); tcm->tcm_parent = parent; return 0; } -int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle) { - struct tcmsg *tcm; - - assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL); - - tcm = NLMSG_DATA(m->hdr); - tcm->tcm_handle = handle; - - return 0; -} - int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex) { struct br_port_msg *bpm; int r; diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 9024c00830a..097483c4351 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -59,12 +59,10 @@ static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) { return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE); } -static inline bool rtnl_message_type_is_qdisc(uint16_t type) { - return IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC); -} - -static inline bool rtnl_message_type_is_tclass(uint16_t type) { - return IN_SET(type, RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS); +static inline bool rtnl_message_type_is_traffic_control(uint16_t type) { + return IN_SET(type, + RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC, + RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS); } static inline bool rtnl_message_type_is_mdb(uint16_t type) { diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index c7a7bad1686..f0e643b9aed 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -179,18 +179,11 @@ int qdisc_configure(Link *link, QDisc *qdisc) { assert(link->manager->rtnl); assert(link->ifindex > 0); - r = sd_rtnl_message_new_qdisc(link->manager->rtnl, &req, RTM_NEWQDISC, AF_UNSPEC, link->ifindex); + r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &req, RTM_NEWQDISC, + link->ifindex, qdisc->handle, qdisc->parent); if (r < 0) return log_link_debug_errno(link, r, "Could not create RTM_NEWQDISC message: %m"); - r = sd_rtnl_message_set_qdisc_parent(req, qdisc->parent); - if (r < 0) - return r; - - r = sd_rtnl_message_set_qdisc_handle(req, qdisc->handle); - if (r < 0) - return r; - if (QDISC_VTABLE(qdisc)) { if (QDISC_VTABLE(qdisc)->fill_tca_kind) { r = QDISC_VTABLE(qdisc)->fill_tca_kind(link, qdisc, req); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index 4e1386b48ff..860d8c997a9 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -137,18 +137,11 @@ int tclass_configure(Link *link, TClass *tclass) { assert(link->manager->rtnl); assert(link->ifindex > 0); - r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex); + r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &req, RTM_NEWTCLASS, + link->ifindex, tclass->classid, tclass->parent); if (r < 0) return log_link_debug_errno(link, r, "Could not create RTM_NEWTCLASS message: %m"); - r = sd_rtnl_message_set_tclass_parent(req, tclass->parent); - if (r < 0) - return r; - - r = sd_rtnl_message_set_tclass_handle(req, tclass->classid); - if (r < 0) - return r; - r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind); if (r < 0) return r; diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index c166cc16716..6b1fcd45219 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -202,13 +202,8 @@ int sd_rtnl_message_routing_policy_rule_get_fib_type(sd_netlink_message *m, uint int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, uint32_t flags); int sd_rtnl_message_routing_policy_rule_get_flags(sd_netlink_message *m, uint32_t *flags); -int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex); -int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent); -int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle); - -int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex); -int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent); -int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle); +int sd_rtnl_message_new_traffic_control(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, + int ifindex, uint32_t handle, uint32_t parent); int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex); From 1f38b6f9c6ee53eb1c8036a585909c344b64dd42 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 06:04:16 +0900 Subject: [PATCH 05/12] sd-netlink: introduce sd_rtnl_message_traffic_control_get_{ifindex,parent,handle}() --- .../sd-netlink/netlink-message-rtnl.c | 42 +++++++++++++++++++ src/systemd/sd-netlink.h | 3 ++ 2 files changed, 45 insertions(+) diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c index 888d097d986..56fb74727e3 100644 --- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c @@ -1072,6 +1072,48 @@ int sd_rtnl_message_new_traffic_control( return 0; } +int sd_rtnl_message_traffic_control_get_ifindex(sd_netlink_message *m, int *ret) { + struct tcmsg *tcm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_traffic_control(m->hdr->nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); + + tcm = NLMSG_DATA(m->hdr); + *ret = tcm->tcm_ifindex; + + return 0; +} + +int sd_rtnl_message_traffic_control_get_handle(sd_netlink_message *m, uint32_t *ret) { + struct tcmsg *tcm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_traffic_control(m->hdr->nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); + + tcm = NLMSG_DATA(m->hdr); + *ret = tcm->tcm_handle; + + return 0; +} + +int sd_rtnl_message_traffic_control_get_parent(sd_netlink_message *m, uint32_t *ret) { + struct tcmsg *tcm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_traffic_control(m->hdr->nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); + + tcm = NLMSG_DATA(m->hdr); + *ret = tcm->tcm_parent; + + return 0; +} + int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex) { struct br_port_msg *bpm; int r; diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 6b1fcd45219..6030d9f9e82 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -204,6 +204,9 @@ int sd_rtnl_message_routing_policy_rule_get_flags(sd_netlink_message *m, uint32_ int sd_rtnl_message_new_traffic_control(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifindex, uint32_t handle, uint32_t parent); +int sd_rtnl_message_traffic_control_get_ifindex(sd_netlink_message *m, int *ret); +int sd_rtnl_message_traffic_control_get_handle(sd_netlink_message *m, uint32_t *ret); +int sd_rtnl_message_traffic_control_get_parent(sd_netlink_message *m, uint32_t *ret); int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex); From 27e93a4b663f4feb1e41353fa03844d361b29135 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 09:53:40 +0900 Subject: [PATCH 06/12] sd-netlink: make traffic control related message can be monitored --- src/libsystemd/sd-netlink/sd-netlink.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 9de13f4c46d..4cd4e734055 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -977,7 +977,8 @@ int sd_netlink_add_match( neighbor_groups[] = { RTNLGRP_NEIGH, }, nexthop_groups[] = { RTNLGRP_NEXTHOP, }, route_groups[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, }, - rule_groups[] = { RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, }; + rule_groups[] = { RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, }, + tc_groups[] = { RTNLGRP_TC }; const uint32_t *groups; size_t n_groups; @@ -1016,6 +1017,13 @@ int sd_netlink_add_match( groups = nexthop_groups; n_groups = ELEMENTSOF(nexthop_groups); break; + case RTM_NEWQDISC: + case RTM_DELQDISC: + case RTM_NEWTCLASS: + case RTM_DELTCLASS: + groups = tc_groups; + n_groups = ELEMENTSOF(tc_groups); + break; default: return -EOPNOTSUPP; } From e8b8b3eacb19bc05308574b0d31784d9c6e0b84a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 10:24:50 +0900 Subject: [PATCH 07/12] sd-netlink: allow to dump qdisc and tclass --- src/libsystemd/sd-netlink/netlink-message.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 182c74ed3d8..30d159ec98f 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -119,7 +119,8 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { assert_return(m->protocol != NETLINK_ROUTE || IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, - RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL); + RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP, RTM_GETQDISC, RTM_GETTCLASS), + -EINVAL); SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); From 828a81a93783248be15683c9639b85098f331328 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 08:42:22 +0900 Subject: [PATCH 08/12] network: tc: monitor qdisc and tclass --- src/network/networkd-link.c | 4 +- src/network/networkd-link.h | 1 + src/network/networkd-manager.c | 54 ++++++++ src/network/tc/qdisc.c | 192 ++++++++++++++++++++++++++++ src/network/tc/qdisc.h | 12 +- src/network/tc/tc.c | 77 +++++++++++ src/network/tc/tc.h | 24 +++- src/network/tc/tclass.c | 225 +++++++++++++++++++++++++++++++-- src/network/tc/tclass.h | 13 +- 9 files changed, 581 insertions(+), 21 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index addf0d4afee..db55b467f80 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -269,12 +269,10 @@ static Link *link_free(Link *link) { link_dns_settings_clear(link); link->routes = set_free(link->routes); - link->nexthops = set_free(link->nexthops); - link->neighbors = set_free(link->neighbors); - link->addresses = set_free(link->addresses); + link->traffic_control = set_free(link->traffic_control); link->dhcp_pd_prefixes = set_free(link->dhcp_pd_prefixes); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 7ccb31df791..23a893449f2 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -108,6 +108,7 @@ typedef struct Link { Set *neighbors; Set *routes; Set *nexthops; + Set *traffic_control; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 3261f289e91..fab0b4094c3 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -45,12 +45,14 @@ #include "ordered-set.h" #include "path-lookup.h" #include "path-util.h" +#include "qdisc.h" #include "selinux-util.h" #include "set.h" #include "signal-util.h" #include "stat-util.h" #include "strv.h" #include "sysctl-util.h" +#include "tclass.h" #include "tmpfile-util.h" /* use 128 MB for receive socket kernel queue. */ @@ -311,6 +313,22 @@ static int manager_connect_rtnl(Manager *m) { if (r < 0) return r; + r = netlink_add_match(m->rtnl, NULL, RTM_NEWQDISC, &manager_rtnl_process_qdisc, NULL, m, "network-rtnl_process_qdisc"); + if (r < 0) + return r; + + r = netlink_add_match(m->rtnl, NULL, RTM_DELQDISC, &manager_rtnl_process_qdisc, NULL, m, "network-rtnl_process_qdisc"); + if (r < 0) + return r; + + r = netlink_add_match(m->rtnl, NULL, RTM_NEWTCLASS, &manager_rtnl_process_tclass, NULL, m, "network-rtnl_process_tclass"); + if (r < 0) + return r; + + r = netlink_add_match(m->rtnl, NULL, RTM_DELTCLASS, &manager_rtnl_process_tclass, NULL, m, "network-rtnl_process_tclass"); + if (r < 0) + return r; + r = netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address"); if (r < 0) return r; @@ -671,6 +689,34 @@ static int manager_enumerate_links(Manager *m) { return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link); } +static int manager_enumerate_qdisc(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_traffic_control(m->rtnl, &req, RTM_GETQDISC, 0, 0, 0); + if (r < 0) + return r; + + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_qdisc); +} + +static int manager_enumerate_tclass(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_traffic_control(m->rtnl, &req, RTM_GETTCLASS, 0, 0, 0); + if (r < 0) + return r; + + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_tclass); +} + static int manager_enumerate_addresses(Manager *m) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -797,6 +843,14 @@ int manager_enumerate(Manager *m) { if (r < 0) return log_error_errno(r, "Could not enumerate links: %m"); + r = manager_enumerate_qdisc(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate QDisc: %m"); + + r = manager_enumerate_tclass(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate TClass: %m"); + r = manager_enumerate_addresses(m); if (r < 0) return log_error_errno(r, "Could not enumerate addresses: %m"); diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index f0e643b9aed..11c092d4bf7 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -54,6 +54,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) { .kind = kind, }; } else { + assert(kind >= 0 && kind < _QDISC_KIND_MAX); qdisc = malloc0(qdisc_vtable[kind]->object_size); if (!qdisc) return -ENOMEM; @@ -140,10 +141,94 @@ QDisc* qdisc_free(QDisc *qdisc) { config_section_free(qdisc->section); + if (qdisc->link) + set_remove(qdisc->link->traffic_control, TC(qdisc)); + free(qdisc->tca_kind); return mfree(qdisc); } +static const char *qdisc_get_tca_kind(const QDisc *qdisc) { + assert(qdisc); + + return (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->tca_kind) ? + QDISC_VTABLE(qdisc)->tca_kind : qdisc->tca_kind; +} + +void qdisc_hash_func(const QDisc *qdisc, struct siphash *state) { + assert(qdisc); + assert(state); + + siphash24_compress(&qdisc->handle, sizeof(qdisc->handle), state); + siphash24_compress(&qdisc->parent, sizeof(qdisc->parent), state); + siphash24_compress_string(qdisc_get_tca_kind(qdisc), state); +} + +int qdisc_compare_func(const QDisc *a, const QDisc *b) { + int r; + + assert(a); + assert(b); + + r = CMP(a->handle, b->handle); + if (r != 0) + return r; + + r = CMP(a->parent, b->parent); + if (r != 0) + return r; + + return strcmp_ptr(qdisc_get_tca_kind(a), qdisc_get_tca_kind(b)); +} + +static int qdisc_get(Link *link, const QDisc *in, QDisc **ret) { + TrafficControl *existing; + int r; + + assert(link); + assert(in); + + r = traffic_control_get(link, TC(in), &existing); + if (r < 0) + return r; + + if (ret) + *ret = TC_TO_QDISC(existing); + return 0; +} + +static int qdisc_add(Link *link, QDisc *qdisc) { + int r; + + assert(link); + assert(qdisc); + + r = traffic_control_add(link, TC(qdisc)); + if (r < 0) + return r; + + qdisc->link = link; + return 0; +} + +static void log_qdisc_debug(QDisc *qdisc, Link *link, const char *str) { + _cleanup_free_ char *state = NULL; + + assert(qdisc); + assert(str); + + if (!DEBUG_LOGGING) + return; + + (void) network_config_state_to_string_alloc(qdisc->state, &state); + + log_link_debug(link, "%s %s QDisc (%s): handle=%"PRIx32":%"PRIx32", parent=%"PRIx32":%"PRIx32", kind=%s", + str, strna(network_config_source_to_string(qdisc->source)), strna(state), + TC_H_MAJ(qdisc->handle) >> 16, TC_H_MIN(qdisc->handle), + TC_H_MAJ(qdisc->parent) >> 16, TC_H_MIN(qdisc->parent), + strna(qdisc_get_tca_kind(qdisc))); +} + static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -216,6 +301,113 @@ int qdisc_configure(Link *link, QDisc *qdisc) { return 0; } +int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + _cleanup_(qdisc_freep) QDisc *tmp = NULL; + QDisc *qdisc = NULL; + Link *link; + uint16_t type; + int ifindex, r; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive QDisc message, ignoring"); + + return 0; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); + return 0; + } else if (!IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC)) { + log_warning("rtnl: received unexpected message type %u when processing QDisc, ignoring.", type); + return 0; + } + + r = sd_rtnl_message_traffic_control_get_ifindex(message, &ifindex); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received QDisc message with invalid ifindex %d, ignoring.", ifindex); + return 0; + } + + if (link_get_by_index(m, ifindex, &link) < 0) { + if (!m->enumerating) + log_warning("rtnl: received QDisc for link '%d' we don't know about, ignoring.", ifindex); + return 0; + } + + r = qdisc_new(_QDISC_KIND_INVALID, &tmp); + if (r < 0) + return log_oom(); + + r = sd_rtnl_message_traffic_control_get_handle(message, &tmp->handle); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received QDisc message without handle, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_traffic_control_get_parent(message, &tmp->parent); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received QDisc message without parent, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_string_strdup(message, TCA_KIND, &tmp->tca_kind); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received QDisc message without kind, ignoring: %m"); + return 0; + } + + (void) qdisc_get(link, tmp, &qdisc); + + switch (type) { + case RTM_NEWQDISC: + if (qdisc) { + qdisc_enter_configured(qdisc); + log_qdisc_debug(qdisc, link, "Received remembered"); + } else { + qdisc_enter_configured(tmp); + log_qdisc_debug(tmp, link, "Received new"); + + r = qdisc_add(link, tmp); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to remember QDisc, ignoring: %m"); + return 0; + } + + qdisc = TAKE_PTR(tmp); + } + + break; + + case RTM_DELQDISC: + if (qdisc) { + qdisc_enter_removed(qdisc); + if (qdisc->state == 0) { + log_qdisc_debug(qdisc, link, "Forgetting"); + qdisc_free(qdisc); + } else + log_qdisc_debug(qdisc, link, "Removed"); + } else + log_qdisc_debug(tmp, link, "Kernel removed unknown"); + + break; + + default: + assert_not_reached(); + } + + return 1; +} + int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) { int r; diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 8329d18b4f5..29ceb50183c 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -37,8 +37,11 @@ typedef enum QDiscKind { typedef struct QDisc { TrafficControl meta; - ConfigSection *section; + Link *link; Network *network; + ConfigSection *section; + NetworkConfigSource source; + NetworkConfigState state; uint32_t handle; uint32_t parent; @@ -73,12 +76,19 @@ extern const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX]; /* For casting the various qdisc kinds into a qdisc */ #define QDISC(q) (&(q)->meta) +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(QDisc, qdisc); + QDisc* qdisc_free(QDisc *qdisc); int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret); +void qdisc_hash_func(const QDisc *qdic, struct siphash *state); +int qdisc_compare_func(const QDisc *a, const QDisc *b); + int qdisc_configure(Link *link, QDisc *qdisc); int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact); +int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); + DEFINE_SECTION_CLEANUP_FUNCTIONS(QDisc, qdisc_free); DEFINE_TC_CAST(QDISC, QDisc); diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c index c0f6989b1b6..55d1b0c9371 100644 --- a/src/network/tc/tc.c +++ b/src/network/tc/tc.c @@ -21,6 +21,83 @@ void traffic_control_free(TrafficControl *tc) { } } +void traffic_control_hash_func(const TrafficControl *tc, struct siphash *state) { + assert(tc); + assert(state); + + siphash24_compress(&tc->kind, sizeof(tc->kind), state); + + switch (tc->kind) { + case TC_KIND_QDISC: + qdisc_hash_func(TC_TO_QDISC_CONST(tc), state); + break; + case TC_KIND_TCLASS: + tclass_hash_func(TC_TO_TCLASS_CONST(tc), state); + break; + default: + assert_not_reached(); + } +} + +int traffic_control_compare_func(const TrafficControl *a, const TrafficControl *b) { + int r; + + assert(a); + assert(b); + + r = CMP(a->kind, b->kind); + if (r != 0) + return r; + + switch (a->kind) { + case TC_KIND_QDISC: + return qdisc_compare_func(TC_TO_QDISC_CONST(a), TC_TO_QDISC_CONST(b)); + case TC_KIND_TCLASS: + return tclass_compare_func(TC_TO_TCLASS_CONST(a), TC_TO_TCLASS_CONST(b)); + default: + assert_not_reached(); + } +} + +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + traffic_control_hash_ops, + TrafficControl, + traffic_control_hash_func, + traffic_control_compare_func, + traffic_control_free); + +int traffic_control_get(Link *link, const TrafficControl *in, TrafficControl **ret) { + TrafficControl *existing; + + assert(link); + assert(in); + + existing = set_get(link->traffic_control, in); + if (!existing) + return -ENOENT; + + if (ret) + *ret = existing; + return 0; +} + +int traffic_control_add(Link *link, TrafficControl *tc) { + int r; + + assert(link); + assert(tc); + + /* This must be called only from qdisc_add() or tclass_add(). */ + + r = set_ensure_put(&link->traffic_control, &traffic_control_hash_ops, tc); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + + return 0; +} + static int traffic_control_configure(Link *link, TrafficControl *tc) { assert(link); assert(tc); diff --git a/src/network/tc/tc.h b/src/network/tc/tc.h index badd5227c6f..954dcd39f9c 100644 --- a/src/network/tc/tc.h +++ b/src/network/tc/tc.h @@ -16,12 +16,18 @@ typedef struct TrafficControl { } TrafficControl; /* For casting a tc into the various tc kinds */ -#define DEFINE_TC_CAST(UPPERCASE, MixedCase) \ - static inline MixedCase* TC_TO_##UPPERCASE(TrafficControl *tc) { \ - if (_unlikely_(!tc || tc->kind != TC_KIND_##UPPERCASE)) \ - return NULL; \ - \ - return (MixedCase*) tc; \ +#define DEFINE_TC_CAST(UPPERCASE, MixedCase) \ + static inline MixedCase* TC_TO_##UPPERCASE(TrafficControl *tc) { \ + if (_unlikely_(!tc || tc->kind != TC_KIND_##UPPERCASE)) \ + return NULL; \ + \ + return (MixedCase*) tc; \ + } \ + static inline const MixedCase* TC_TO_##UPPERCASE##_CONST(const TrafficControl *tc) { \ + if (_unlikely_(!tc || tc->kind != TC_KIND_##UPPERCASE)) \ + return NULL; \ + \ + return (const MixedCase*) tc; \ } /* For casting the various tc kinds into a tc */ @@ -30,3 +36,9 @@ typedef struct TrafficControl { void traffic_control_free(TrafficControl *tc); int link_configure_traffic_control(Link *link); void network_drop_invalid_traffic_control(Network *network); + +void traffic_control_hash_func(const TrafficControl *tc, struct siphash *state); +int traffic_control_compare_func(const TrafficControl *a, const TrafficControl *b); + +int traffic_control_get(Link *link, const TrafficControl *in, TrafficControl **ret); +int traffic_control_add(Link *link, TrafficControl *tc); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index 860d8c997a9..cd61b42c4e2 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -25,18 +25,31 @@ static int tclass_new(TClassKind kind, TClass **ret) { _cleanup_(tclass_freep) TClass *tclass = NULL; int r; - tclass = malloc0(tclass_vtable[kind]->object_size); - if (!tclass) - return -ENOMEM; + if (kind == _TCLASS_KIND_INVALID) { + tclass = new(TClass, 1); + if (!tclass) + return -ENOMEM; - tclass->meta.kind = TC_KIND_TCLASS, - tclass->parent = TC_H_ROOT; - tclass->kind = kind; + *tclass = (TClass) { + .meta.kind = TC_KIND_TCLASS, + .parent = TC_H_ROOT, + .kind = kind, + }; + } else { + assert(kind >= 0 && kind < _TCLASS_KIND_MAX); + tclass = malloc0(tclass_vtable[kind]->object_size); + if (!tclass) + return -ENOMEM; - if (TCLASS_VTABLE(tclass)->init) { - r = TCLASS_VTABLE(tclass)->init(tclass); - if (r < 0) - return r; + tclass->meta.kind = TC_KIND_TCLASS; + tclass->parent = TC_H_ROOT; + tclass->kind = kind; + + if (TCLASS_VTABLE(tclass)->init) { + r = TCLASS_VTABLE(tclass)->init(tclass); + if (r < 0) + return r; + } } *ret = TAKE_PTR(tclass); @@ -99,9 +112,94 @@ TClass* tclass_free(TClass *tclass) { config_section_free(tclass->section); + if (tclass->link) + set_remove(tclass->link->traffic_control, TC(tclass)); + + free(tclass->tca_kind); return mfree(tclass); } +static const char *tclass_get_tca_kind(const TClass *tclass) { + assert(tclass); + + return (TCLASS_VTABLE(tclass) && TCLASS_VTABLE(tclass)->tca_kind) ? + TCLASS_VTABLE(tclass)->tca_kind : tclass->tca_kind; +} + +void tclass_hash_func(const TClass *tclass, struct siphash *state) { + assert(tclass); + assert(state); + + siphash24_compress(&tclass->classid, sizeof(tclass->classid), state); + siphash24_compress(&tclass->parent, sizeof(tclass->parent), state); + siphash24_compress_string(tclass_get_tca_kind(tclass), state); +} + +int tclass_compare_func(const TClass *a, const TClass *b) { + int r; + + assert(a); + assert(b); + + r = CMP(a->classid, b->classid); + if (r != 0) + return r; + + r = CMP(a->parent, b->parent); + if (r != 0) + return r; + + return strcmp_ptr(tclass_get_tca_kind(a), tclass_get_tca_kind(b)); +} + +static int tclass_get(Link *link, const TClass *in, TClass **ret) { + TrafficControl *existing; + int r; + + assert(link); + assert(in); + + r = traffic_control_get(link, TC(in), &existing); + if (r < 0) + return r; + + if (ret) + *ret = TC_TO_TCLASS(existing); + return 0; +} + +static int tclass_add(Link *link, TClass *tclass) { + int r; + + assert(link); + assert(tclass); + + r = traffic_control_add(link, TC(tclass)); + if (r < 0) + return r; + + tclass->link = link; + return 0; +} + +static void log_tclass_debug(TClass *tclass, Link *link, const char *str) { + _cleanup_free_ char *state = NULL; + + assert(tclass); + assert(str); + + if (!DEBUG_LOGGING) + return; + + (void) network_config_state_to_string_alloc(tclass->state, &state); + + log_link_debug(link, "%s %s TClass (%s): classid=%"PRIx32":%"PRIx32", parent=%"PRIx32":%"PRIx32", kind=%s", + str, strna(network_config_source_to_string(tclass->source)), strna(state), + TC_H_MAJ(tclass->classid) >> 16, TC_H_MIN(tclass->classid), + TC_H_MAJ(tclass->parent) >> 16, TC_H_MIN(tclass->parent), + strna(tclass_get_tca_kind(tclass))); +} + static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -162,6 +260,113 @@ int tclass_configure(Link *link, TClass *tclass) { return 0; } +int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + _cleanup_(tclass_freep) TClass *tmp = NULL; + TClass *tclass = NULL; + Link *link; + uint16_t type; + int ifindex, r; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive TClass message, ignoring"); + + return 0; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); + return 0; + } else if (!IN_SET(type, RTM_NEWTCLASS, RTM_DELTCLASS)) { + log_warning("rtnl: received unexpected message type %u when processing TClass, ignoring.", type); + return 0; + } + + r = sd_rtnl_message_traffic_control_get_ifindex(message, &ifindex); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received TClass message with invalid ifindex %d, ignoring.", ifindex); + return 0; + } + + if (link_get_by_index(m, ifindex, &link) < 0) { + if (!m->enumerating) + log_warning("rtnl: received TClass for link '%d' we don't know about, ignoring.", ifindex); + return 0; + } + + r = tclass_new(_TCLASS_KIND_INVALID, &tmp); + if (r < 0) + return log_oom(); + + r = sd_rtnl_message_traffic_control_get_handle(message, &tmp->classid); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received TClass message without handle, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_traffic_control_get_parent(message, &tmp->parent); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received TClass message without parent, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_string_strdup(message, TCA_KIND, &tmp->tca_kind); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received TClass message without kind, ignoring: %m"); + return 0; + } + + (void) tclass_get(link, tmp, &tclass); + + switch (type) { + case RTM_NEWTCLASS: + if (tclass) { + tclass_enter_configured(tclass); + log_tclass_debug(tclass, link, "Received remembered"); + } else { + tclass_enter_configured(tmp); + log_tclass_debug(tmp, link, "Received new"); + + r = tclass_add(link, tmp); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to remember TClass, ignoring: %m"); + return 0; + } + + tclass = TAKE_PTR(tmp); + } + + break; + + case RTM_DELTCLASS: + if (tclass) { + tclass_enter_removed(tclass); + if (tclass->state == 0) { + log_tclass_debug(tclass, link, "Forgetting"); + tclass_free(tclass); + } else + log_tclass_debug(tclass, link, "Removed"); + } else + log_tclass_debug(tmp, link, "Kernel removed unknown"); + + break; + + default: + assert_not_reached(); + } + + return 1; +} + int tclass_section_verify(TClass *tclass) { int r; diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h index 5f8016c1872..9e57e9566f8 100644 --- a/src/network/tc/tclass.h +++ b/src/network/tc/tclass.h @@ -19,13 +19,17 @@ typedef enum TClassKind { typedef struct TClass { TrafficControl meta; - ConfigSection *section; + Link *link; Network *network; + ConfigSection *section; + NetworkConfigSource source; + NetworkConfigState state; uint32_t classid; uint32_t parent; TClassKind kind; + char *tca_kind; } TClass; typedef struct TClassVTable { @@ -53,12 +57,19 @@ extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX]; /* For casting the various tclass kinds into a tclass */ #define TCLASS(t) (&(t)->meta) +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(TClass, tclass); + TClass* tclass_free(TClass *tclass); int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret); +void tclass_hash_func(const TClass *tclass, struct siphash *state); +int tclass_compare_func(const TClass *a, const TClass *b); + int tclass_configure(Link *link, TClass *tclass); int tclass_section_verify(TClass *tclass); +int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); + DEFINE_SECTION_CLEANUP_FUNCTIONS(TClass, tclass_free); DEFINE_TC_CAST(TCLASS, TClass); From b3208e0fad3e8c4c0f7d02098991e9694ff617e1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 12:18:47 +0900 Subject: [PATCH 09/12] network: tc/teql: set tca_kind in verify() Then, qdisc_configure() can be simplified. --- src/network/tc/qdisc.c | 22 +++++----------------- src/network/tc/qdisc.h | 1 - src/network/tc/teql.c | 23 +++++++---------------- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 11c092d4bf7..d1b689793ca 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -269,24 +269,12 @@ int qdisc_configure(Link *link, QDisc *qdisc) { if (r < 0) return log_link_debug_errno(link, r, "Could not create RTM_NEWQDISC message: %m"); - if (QDISC_VTABLE(qdisc)) { - if (QDISC_VTABLE(qdisc)->fill_tca_kind) { - r = QDISC_VTABLE(qdisc)->fill_tca_kind(link, qdisc, req); - if (r < 0) - return r; - } else { - r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind); - if (r < 0) - return r; - } + r = sd_netlink_message_append_string(req, TCA_KIND, qdisc_get_tca_kind(qdisc)); + if (r < 0) + return r; - if (QDISC_VTABLE(qdisc)->fill_message) { - r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req); - if (r < 0) - return r; - } - } else { - r = sd_netlink_message_append_string(req, TCA_KIND, qdisc->tca_kind); + if (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->fill_message) { + r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req); if (r < 0) return r; } diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 29ceb50183c..2fc1dc2b039 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -55,7 +55,6 @@ typedef struct QDiscVTable { const char *tca_kind; /* called in qdisc_new() */ int (*init)(QDisc *qdisc); - int (*fill_tca_kind)(Link *link, QDisc *qdisc, sd_netlink_message *m); int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m); int (*verify)(QDisc *qdisc); } QDiscVTable; diff --git a/src/network/tc/teql.c b/src/network/tc/teql.c index 633a7827b4b..1422860f55d 100644 --- a/src/network/tc/teql.c +++ b/src/network/tc/teql.c @@ -1,34 +1,25 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "macro.h" -#include "netlink-util.h" #include "parse-util.h" -#include "stdio-util.h" #include "string-util.h" #include "teql.h" -static int trivial_link_equalizer_fill_tca_kind(Link *link, QDisc *qdisc, sd_netlink_message *req) { - char kind[STRLEN("teql") + DECIMAL_STR_MAX(unsigned)]; +static int trivial_link_equalizer_verify(QDisc *qdisc) { + _cleanup_free_ char *tca_kind = NULL; TrivialLinkEqualizer *teql; - int r; - assert(link); - assert(qdisc); - assert(req); + teql = TEQL(ASSERT_PTR(qdisc)); - teql = TEQL(qdisc); + if (asprintf(&tca_kind, "teql%u", teql->id) < 0) + return log_oom(); - xsprintf(kind, "teql%u", teql->id); - r = sd_netlink_message_append_string(req, TCA_KIND, kind); - if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); - - return 0; + return free_and_replace(qdisc->tca_kind, tca_kind); } const QDiscVTable teql_vtable = { .object_size = sizeof(TrivialLinkEqualizer), - .fill_tca_kind = trivial_link_equalizer_fill_tca_kind, + .verify = trivial_link_equalizer_verify, }; int config_parse_trivial_link_equalizer_id( From 1dec9d816b752af82bcc8a567a3979314b44c93f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 03:49:27 +0900 Subject: [PATCH 10/12] network: tc: use request queue to configure traffic control But no dependency resolution is implemented. --- src/network/networkd-link.c | 2 +- src/network/networkd-queue.c | 16 +++++++- src/network/networkd-queue.h | 3 ++ src/network/tc/qdisc.c | 76 +++++++++++++++++++++++++++++++++++- src/network/tc/qdisc.h | 2 + src/network/tc/tc.c | 76 ++++++++++++++++++++++++++++++------ src/network/tc/tc.h | 6 ++- src/network/tc/tclass.c | 76 +++++++++++++++++++++++++++++++++++- src/network/tc/tclass.h | 2 + 9 files changed, 243 insertions(+), 16 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index db55b467f80..43372caa355 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1097,7 +1097,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = link_configure_traffic_control(link); + r = link_request_traffic_control(link); if (r < 0) return r; diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 1aa4bbae554..d673c2f1550 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -16,6 +16,7 @@ #include "networkd-routing-policy-rule.h" #include "networkd-queue.h" #include "networkd-setlink.h" +#include "tc.h" static void request_free_object(RequestType type, void *object) { switch (type) { @@ -58,6 +59,10 @@ static void request_free_object(RequestType type, void *object) { break; case REQUEST_TYPE_SET_LINK: case REQUEST_TYPE_STACKED_NETDEV: + break; + case REQUEST_TYPE_TRAFFIC_CONTROL: + traffic_control_free(object); + break; case REQUEST_TYPE_UP_DOWN: break; default: @@ -143,6 +148,9 @@ static void request_hash_func(const Request *req, struct siphash *state) { case REQUEST_TYPE_SET_LINK: trivial_hash_func(req->set_link_operation_ptr, state); break; + case REQUEST_TYPE_TRAFFIC_CONTROL: + traffic_control_hash_func(req->traffic_control, state); + break; case REQUEST_TYPE_UP_DOWN: break; default: @@ -196,6 +204,8 @@ static int request_compare_func(const struct Request *a, const struct Request *b return routing_policy_rule_compare_func(a->rule, b->rule); case REQUEST_TYPE_SET_LINK: return trivial_compare_func(a->set_link_operation_ptr, b->set_link_operation_ptr); + case REQUEST_TYPE_TRAFFIC_CONTROL: + return traffic_control_compare_func(a->traffic_control, b->traffic_control); case REQUEST_TYPE_UP_DOWN: return 0; default: @@ -241,7 +251,8 @@ int link_queue_request( REQUEST_TYPE_DHCP4_CLIENT, REQUEST_TYPE_DHCP6_CLIENT, REQUEST_TYPE_NDISC, - REQUEST_TYPE_RADV) || + REQUEST_TYPE_RADV, + REQUEST_TYPE_TRAFFIC_CONTROL) || netlink_handler); req = new(Request, 1); @@ -347,6 +358,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) { case REQUEST_TYPE_STACKED_NETDEV: r = request_process_stacked_netdev(req); break; + case REQUEST_TYPE_TRAFFIC_CONTROL: + r = request_process_traffic_control(req); + break; case REQUEST_TYPE_UP_DOWN: r = request_process_link_up_or_down(req); break; diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index ac33e885f2b..ac02b9d8159 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -14,6 +14,7 @@ typedef struct NetDev NetDev; typedef struct NextHop NextHop; typedef struct Route Route; typedef struct RoutingPolicyRule RoutingPolicyRule; +typedef struct TrafficControl TrafficControl; typedef enum RequestType { REQUEST_TYPE_ACTIVATE_LINK, @@ -33,6 +34,7 @@ typedef enum RequestType { REQUEST_TYPE_ROUTING_POLICY_RULE, REQUEST_TYPE_SET_LINK, REQUEST_TYPE_STACKED_NETDEV, + REQUEST_TYPE_TRAFFIC_CONTROL, REQUEST_TYPE_UP_DOWN, _REQUEST_TYPE_MAX, _REQUEST_TYPE_INVALID = -EINVAL, @@ -54,6 +56,7 @@ typedef struct Request { RoutingPolicyRule *rule; void *set_link_operation_ptr; NetDev *netdev; + TrafficControl *traffic_control; void *object; }; void *userdata; diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index d1b689793ca..4fa1a6f5eac 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -8,6 +8,7 @@ #include "in-addr-util.h" #include "netlink-util.h" #include "networkd-manager.h" +#include "networkd-queue.h" #include "parse-util.h" #include "qdisc.h" #include "set.h" @@ -123,6 +124,7 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns qdisc->network = network; qdisc->section = TAKE_PTR(n); + qdisc->source = NETWORK_CONFIG_SOURCE_STATIC; r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, qdisc->section, TC(qdisc)); if (r < 0) @@ -211,6 +213,35 @@ static int qdisc_add(Link *link, QDisc *qdisc) { return 0; } +static int qdisc_dup(const QDisc *src, QDisc **ret) { + _cleanup_(qdisc_freep) QDisc *dst = NULL; + + assert(src); + assert(ret); + + if (QDISC_VTABLE(src)) + dst = memdup(src, QDISC_VTABLE(src)->object_size); + else + dst = newdup(QDisc, src, 1); + if (!dst) + return -ENOMEM; + + /* clear all pointers */ + dst->network = NULL; + dst->section = NULL; + dst->link = NULL; + dst->tca_kind = NULL; + + if (src->tca_kind) { + dst->tca_kind = strdup(src->tca_kind); + if (!dst->tca_kind) + return -ENOMEM; + } + + *ret = TAKE_PTR(dst); + return 0; +} + static void log_qdisc_debug(QDisc *qdisc, Link *link, const char *str) { _cleanup_free_ char *state = NULL; @@ -264,6 +295,8 @@ int qdisc_configure(Link *link, QDisc *qdisc) { assert(link->manager->rtnl); assert(link->ifindex > 0); + log_qdisc_debug(qdisc, link, "Configuring"); + r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &req, RTM_NEWQDISC, link->ifindex, qdisc->handle, qdisc->parent); if (r < 0) @@ -284,11 +317,52 @@ int qdisc_configure(Link *link, QDisc *qdisc) { return log_link_debug_errno(link, r, "Could not send netlink message: %m"); link_ref(link); - link->tc_messages++; + qdisc_enter_configuring(qdisc); return 0; } +int qdisc_is_ready_to_configure(Link *link, QDisc *qdisc) { + assert(link); + assert(qdisc); + + return true; +} + +int link_request_qdisc(Link *link, QDisc *qdisc) { + QDisc *existing; + int r; + + assert(link); + assert(qdisc); + + if (qdisc_get(link, qdisc, &existing) < 0) { + _cleanup_(qdisc_freep) QDisc *tmp = NULL; + + r = qdisc_dup(qdisc, &tmp); + if (r < 0) + return log_oom(); + + r = qdisc_add(link, tmp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store QDisc: %m"); + + existing = TAKE_PTR(tmp); + } else + existing->source = qdisc->source; + + log_qdisc_debug(existing, link, "Requesting"); + r = link_queue_request(link, REQUEST_TYPE_TRAFFIC_CONTROL, TC(existing), false, + &link->tc_messages, NULL, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request QDisc: %m"); + if (r == 0) + return 0; + + qdisc_enter_requesting(existing); + return 1; +} + int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(qdisc_freep) QDisc *tmp = NULL; QDisc *qdisc = NULL; diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 2fc1dc2b039..dc4156ad0af 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -83,6 +83,8 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns void qdisc_hash_func(const QDisc *qdic, struct siphash *state); int qdisc_compare_func(const QDisc *a, const QDisc *b); +int link_request_qdisc(Link *link, QDisc *qdisc); +int qdisc_is_ready_to_configure(Link *link, QDisc *qdisc); int qdisc_configure(Link *link, QDisc *qdisc); int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact); diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c index 55d1b0c9371..cca5a92e620 100644 --- a/src/network/tc/tc.c +++ b/src/network/tc/tc.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "macro.h" +#include "networkd-queue.h" #include "qdisc.h" #include "tc.h" #include "tclass.h" @@ -112,34 +113,87 @@ static int traffic_control_configure(Link *link, TrafficControl *tc) { } } -int link_configure_traffic_control(Link *link) { +static int link_request_traffic_control_one(Link *link, TrafficControl *tc) { + assert(link); + assert(tc); + + switch (tc->kind) { + case TC_KIND_QDISC: + return link_request_qdisc(link, TC_TO_QDISC(tc)); + case TC_KIND_TCLASS: + return link_request_tclass(link, TC_TO_TCLASS(tc)); + default: + assert_not_reached(); + } +} + +int link_request_traffic_control(Link *link) { TrafficControl *tc; int r; assert(link); assert(link->network); - if (link->tc_messages != 0) { - log_link_debug(link, "Traffic control is configuring."); - return 0; - } - link->tc_configured = false; ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section) { - r = traffic_control_configure(link, tc); + r = link_request_traffic_control_one(link, tc); if (r < 0) - return log_link_error_errno(link, r, "Could not create send configuration message: %m"); + return r; } - if (link->tc_messages == 0) + if (link->tc_messages == 0) { link->tc_configured = true; - else - log_link_debug(link, "Configuring traffic control"); + link_check_ready(link); + } else { + log_link_debug(link, "Setting traffic control"); + link_set_state(link, LINK_STATE_CONFIGURING); + } return 0; } +static int traffic_control_is_ready_to_configure(Link *link, TrafficControl *tc) { + assert(link); + assert(tc); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return false; + + switch(tc->kind) { + case TC_KIND_QDISC: + return qdisc_is_ready_to_configure(link, TC_TO_QDISC(tc)); + case TC_KIND_TCLASS: + return tclass_is_ready_to_configure(link, TC_TO_TCLASS(tc)); + default: + assert_not_reached(); + } +} + +int request_process_traffic_control(Request *req) { + TrafficControl *tc; + Link *link; + int r; + + assert(req); + assert(req->traffic_control); + assert(req->type == REQUEST_TYPE_TRAFFIC_CONTROL); + + link = ASSERT_PTR(req->link); + tc = ASSERT_PTR(req->traffic_control); + + r = traffic_control_is_ready_to_configure(link, tc); + if (r <= 0) { + return r; + } + + r = traffic_control_configure(link, tc); + if (r < 0) + return r; + + return 1; +} + static int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) { assert(tc); diff --git a/src/network/tc/tc.h b/src/network/tc/tc.h index 954dcd39f9c..57458ee65e9 100644 --- a/src/network/tc/tc.h +++ b/src/network/tc/tc.h @@ -3,6 +3,8 @@ #include "networkd-link.h" +typedef struct Request Request; + typedef enum TrafficControlKind { TC_KIND_QDISC, TC_KIND_TCLASS, @@ -34,7 +36,6 @@ typedef struct TrafficControl { #define TC(tc) (&(tc)->meta) void traffic_control_free(TrafficControl *tc); -int link_configure_traffic_control(Link *link); void network_drop_invalid_traffic_control(Network *network); void traffic_control_hash_func(const TrafficControl *tc, struct siphash *state); @@ -42,3 +43,6 @@ int traffic_control_compare_func(const TrafficControl *a, const TrafficControl * int traffic_control_get(Link *link, const TrafficControl *in, TrafficControl **ret); int traffic_control_add(Link *link, TrafficControl *tc); + +int link_request_traffic_control(Link *link); +int request_process_traffic_control(Request *req); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index cd61b42c4e2..2553caa76d9 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -8,6 +8,7 @@ #include "in-addr-util.h" #include "netlink-util.h" #include "networkd-manager.h" +#include "networkd-queue.h" #include "parse-util.h" #include "set.h" #include "string-util.h" @@ -94,6 +95,7 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u tclass->network = network; tclass->section = TAKE_PTR(n); + tclass->source = NETWORK_CONFIG_SOURCE_STATIC; r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, tclass->section, tclass); if (r < 0) @@ -182,6 +184,35 @@ static int tclass_add(Link *link, TClass *tclass) { return 0; } +static int tclass_dup(const TClass *src, TClass **ret) { + _cleanup_(tclass_freep) TClass *dst = NULL; + + assert(src); + assert(ret); + + if (TCLASS_VTABLE(src)) + dst = memdup(src, TCLASS_VTABLE(src)->object_size); + else + dst = newdup(TClass, src, 1); + if (!dst) + return -ENOMEM; + + /* clear all pointers */ + dst->network = NULL; + dst->section = NULL; + dst->link = NULL; + dst->tca_kind = NULL; + + if (src->tca_kind) { + dst->tca_kind = strdup(src->tca_kind); + if (!dst->tca_kind) + return -ENOMEM; + } + + *ret = TAKE_PTR(dst); + return 0; +} + static void log_tclass_debug(TClass *tclass, Link *link, const char *str) { _cleanup_free_ char *state = NULL; @@ -235,6 +266,8 @@ int tclass_configure(Link *link, TClass *tclass) { assert(link->manager->rtnl); assert(link->ifindex > 0); + log_tclass_debug(tclass, link, "Configuring"); + r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &req, RTM_NEWTCLASS, link->ifindex, tclass->classid, tclass->parent); if (r < 0) @@ -255,11 +288,52 @@ int tclass_configure(Link *link, TClass *tclass) { return log_link_debug_errno(link, r, "Could not send netlink message: %m"); link_ref(link); - link->tc_messages++; + tclass_enter_configuring(tclass); return 0; } +int tclass_is_ready_to_configure(Link *link, TClass *tclass) { + assert(link); + assert(tclass); + + return true; +} + +int link_request_tclass(Link *link, TClass *tclass) { + TClass *existing; + int r; + + assert(link); + assert(tclass); + + if (tclass_get(link, tclass, &existing) < 0) { + _cleanup_(tclass_freep) TClass *tmp = NULL; + + r = tclass_dup(tclass, &tmp); + if (r < 0) + return log_oom(); + + r = tclass_add(link, tmp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store TClass: %m"); + + existing = TAKE_PTR(tmp); + } else + existing->source = tclass->source; + + log_tclass_debug(existing, link, "Requesting"); + r = link_queue_request(link, REQUEST_TYPE_TRAFFIC_CONTROL, TC(existing), false, + &link->tc_messages, NULL, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request TClass: %m"); + if (r == 0) + return 0; + + tclass_enter_requesting(existing); + return 1; +} + int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(tclass_freep) TClass *tmp = NULL; TClass *tclass = NULL; diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h index 9e57e9566f8..525630a87cb 100644 --- a/src/network/tc/tclass.h +++ b/src/network/tc/tclass.h @@ -65,6 +65,8 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u void tclass_hash_func(const TClass *tclass, struct siphash *state); int tclass_compare_func(const TClass *a, const TClass *b); +int link_request_tclass(Link *link, TClass *tclass); +int tclass_is_ready_to_configure(Link *link, TClass *tclass); int tclass_configure(Link *link, TClass *tclass); int tclass_section_verify(TClass *tclass); From 7ec1846242055cc68dc419ee4415b0d8b234ffd7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 13:01:19 +0900 Subject: [PATCH 11/12] network: tc: introduce order dependency of traffic control --- src/network/tc/qdisc.c | 43 ++++++++++++++++++++++++++++++++++++++++- src/network/tc/qdisc.h | 2 ++ src/network/tc/tclass.c | 32 +++++++++++++++++++++++++++++- src/network/tc/tclass.h | 2 ++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 4fa1a6f5eac..7df6449de63 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -260,6 +260,44 @@ static void log_qdisc_debug(QDisc *qdisc, Link *link, const char *str) { strna(qdisc_get_tca_kind(qdisc))); } +int link_find_qdisc(Link *link, uint32_t handle, uint32_t parent, const char *kind, QDisc **ret) { + TrafficControl *tc; + + assert(link); + + handle = TC_H_MAJ(handle); + + SET_FOREACH(tc, link->traffic_control) { + QDisc *qdisc; + + if (tc->kind != TC_KIND_QDISC) + continue; + + qdisc = TC_TO_QDISC(tc); + + if (qdisc->handle != handle) + continue; + + if (qdisc->parent != parent) + continue; + + if (qdisc->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + + if (!qdisc_exists(qdisc)) + continue; + + if (kind && !streq_ptr(kind, qdisc_get_tca_kind(qdisc))) + continue; + + if (ret) + *ret = qdisc; + return 0; + } + + return -ENOENT; +} + static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -326,7 +364,10 @@ int qdisc_is_ready_to_configure(Link *link, QDisc *qdisc) { assert(link); assert(qdisc); - return true; + if (IN_SET(qdisc->parent, TC_H_ROOT, TC_H_CLSACT)) /* TC_H_CLSACT == TC_H_INGRESS */ + return true; + + return link_find_tclass(link, qdisc->parent, NULL) >= 0; } int link_request_qdisc(Link *link, QDisc *qdisc) { diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index dc4156ad0af..1e4b72e271f 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -83,6 +83,8 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns void qdisc_hash_func(const QDisc *qdic, struct siphash *state); int qdisc_compare_func(const QDisc *a, const QDisc *b); +int link_find_qdisc(Link *link, uint32_t handle, uint32_t parent, const char *kind, QDisc **qdisc); + int link_request_qdisc(Link *link, QDisc *qdisc); int qdisc_is_ready_to_configure(Link *link, QDisc *qdisc); int qdisc_configure(Link *link, QDisc *qdisc); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index 2553caa76d9..903ff283362 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -213,6 +213,36 @@ static int tclass_dup(const TClass *src, TClass **ret) { return 0; } +int link_find_tclass(Link *link, uint32_t classid, TClass **ret) { + TrafficControl *tc; + + assert(link); + + SET_FOREACH(tc, link->traffic_control) { + TClass *tclass; + + if (tc->kind != TC_KIND_TCLASS) + continue; + + tclass = TC_TO_TCLASS(tc); + + if (tclass->classid != classid) + continue; + + if (tclass->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + + if (!tclass_exists(tclass)) + continue; + + if (ret) + *ret = tclass; + return 0; + } + + return -ENOENT; +} + static void log_tclass_debug(TClass *tclass, Link *link, const char *str) { _cleanup_free_ char *state = NULL; @@ -297,7 +327,7 @@ int tclass_is_ready_to_configure(Link *link, TClass *tclass) { assert(link); assert(tclass); - return true; + return link_find_qdisc(link, tclass->classid, tclass->parent, tclass_get_tca_kind(tclass), NULL) >= 0; } int link_request_tclass(Link *link, TClass *tclass) { diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h index 525630a87cb..6b12fba1e8b 100644 --- a/src/network/tc/tclass.h +++ b/src/network/tc/tclass.h @@ -65,6 +65,8 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u void tclass_hash_func(const TClass *tclass, struct siphash *state); int tclass_compare_func(const TClass *a, const TClass *b); +int link_find_tclass(Link *link, uint32_t classid, TClass **ret); + int link_request_tclass(Link *link, TClass *tclass); int tclass_is_ready_to_configure(Link *link, TClass *tclass); int tclass_configure(Link *link, TClass *tclass); From e4bbd9c61e981476fb832d7bf9a61f65927a137f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Jan 2022 13:06:37 +0900 Subject: [PATCH 12/12] network: tc: use hashmap to store traffic control settings in .network files As now each qdisc or tclass is configured when it is ready. --- src/network/networkd-network.c | 2 +- src/network/networkd-network.h | 2 +- src/network/tc/qdisc.c | 6 +++--- src/network/tc/tc.c | 4 ++-- src/network/tc/tclass.c | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index edcd68d6167..26b007b72fb 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -754,7 +754,7 @@ static Network *network_free(Network *network) { hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free); hashmap_free_with_destructor(network->dhcp_static_leases_by_section, dhcp_static_lease_free); ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free); - ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free); + hashmap_free_with_destructor(network->tc_by_section, traffic_control_free); free(network->name); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index f7eb37aceda..54a855aa4d9 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -340,7 +340,7 @@ struct Network { Hashmap *route_prefixes_by_section; Hashmap *rules_by_section; Hashmap *dhcp_static_leases_by_section; - OrderedHashmap *tc_by_section; + Hashmap *tc_by_section; OrderedHashmap *sr_iov_by_section; /* All kinds of DNS configuration */ diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 7df6449de63..665b2a270c8 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -92,7 +92,7 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns if (r < 0) return r; - existing = ordered_hashmap_get(network->tc_by_section, n); + existing = hashmap_get(network->tc_by_section, n); if (existing) { if (existing->kind != TC_KIND_QDISC) return -EINVAL; @@ -126,7 +126,7 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns qdisc->section = TAKE_PTR(n); qdisc->source = NETWORK_CONFIG_SOURCE_STATIC; - r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, qdisc->section, TC(qdisc)); + r = hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, qdisc->section, TC(qdisc)); if (r < 0) return r; @@ -139,7 +139,7 @@ QDisc* qdisc_free(QDisc *qdisc) { return NULL; if (qdisc->network && qdisc->section) - ordered_hashmap_remove(qdisc->network->tc_by_section, qdisc->section); + hashmap_remove(qdisc->network->tc_by_section, qdisc->section); config_section_free(qdisc->section); diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c index cca5a92e620..7be2de63547 100644 --- a/src/network/tc/tc.c +++ b/src/network/tc/tc.c @@ -136,7 +136,7 @@ int link_request_traffic_control(Link *link) { link->tc_configured = false; - ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section) { + HASHMAP_FOREACH(tc, link->network->tc_by_section) { r = link_request_traffic_control_one(link, tc); if (r < 0) return r; @@ -213,7 +213,7 @@ void network_drop_invalid_traffic_control(Network *network) { assert(network); - ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section) + HASHMAP_FOREACH(tc, network->tc_by_section) if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0) traffic_control_free(tc); } diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index 903ff283362..676733c33f1 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -73,7 +73,7 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u if (r < 0) return r; - existing = ordered_hashmap_get(network->tc_by_section, n); + existing = hashmap_get(network->tc_by_section, n); if (existing) { TClass *t; @@ -97,7 +97,7 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u tclass->section = TAKE_PTR(n); tclass->source = NETWORK_CONFIG_SOURCE_STATIC; - r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, tclass->section, tclass); + r = hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, tclass->section, tclass); if (r < 0) return r; @@ -110,7 +110,7 @@ TClass* tclass_free(TClass *tclass) { return NULL; if (tclass->network && tclass->section) - ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section); + hashmap_remove(tclass->network->tc_by_section, tclass->section); config_section_free(tclass->section);