]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/tc: drop child tree of traffic control nodes on remove
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 7 Oct 2023 05:20:38 +0000 (14:20 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 7 Oct 2023 12:35:16 +0000 (21:35 +0900)
When a node of traffic control tree is removed, all child nodes are also
removed but their removal are not notified by the kernel.
So, previously, removed TC classes or qdiscs under the removed node were
kept in the memory of networkd, and may cause failure on reconfigure.

src/network/tc/qdisc.c
src/network/tc/qdisc.h
src/network/tc/tclass.c
src/network/tc/tclass.h

index 7bb95ede7544ca0fdae4e18f921e921f230883f4..828fe9ab8292c489db794063bcd26248dd2de5d4 100644 (file)
@@ -293,6 +293,33 @@ int link_find_qdisc(Link *link, uint32_t handle, uint32_t parent, const char *ki
         return -ENOENT;
 }
 
+QDisc* qdisc_drop(QDisc *qdisc) {
+        TClass *tclass;
+        Link *link;
+
+        assert(qdisc);
+
+        link = ASSERT_PTR(qdisc->link);
+
+        /* also drop all child classes assigned to the qdisc. */
+        SET_FOREACH(tclass, link->tclasses) {
+                if (TC_H_MAJ(tclass->classid) != qdisc->handle)
+                        continue;
+
+                tclass_drop(tclass);
+        }
+
+        qdisc_enter_removed(qdisc);
+
+        if (qdisc->state == 0) {
+                log_qdisc_debug(qdisc, link, "Forgetting");
+                qdisc = qdisc_free(qdisc);
+        } else
+                log_qdisc_debug(qdisc, link, "Removed");
+
+        return qdisc;
+}
+
 static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, QDisc *qdisc) {
         int r;
 
@@ -512,14 +539,9 @@ int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 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
+                if (qdisc)
+                        qdisc_drop(qdisc);
+                else
                         log_qdisc_debug(tmp, link, "Kernel removed unknown");
 
                 break;
index 155e2adf24871380d532af02a3f057c7fb913f22..86db44763bf29d7640184bb601ebb3479a6144fc 100644 (file)
@@ -77,6 +77,8 @@ 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);
 
+QDisc* qdisc_drop(QDisc *qdisc);
+
 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);
index 0452cc99a87ec5ea59d6e0372f4f6d4f9f22b4c5..9e6032b35a6d4d370e56cd84a9c99a60c1fb2888 100644 (file)
@@ -255,6 +255,33 @@ static void log_tclass_debug(TClass *tclass, Link *link, const char *str) {
                        strna(tclass_get_tca_kind(tclass)));
 }
 
+TClass* tclass_drop(TClass *tclass) {
+        QDisc *qdisc;
+        Link *link;
+
+        assert(tclass);
+
+        link = ASSERT_PTR(tclass->link);
+
+        /* Also drop all child qdiscs assigned to the class. */
+        SET_FOREACH(qdisc, link->qdiscs) {
+                if (qdisc->parent != tclass->classid)
+                        continue;
+
+                qdisc_drop(qdisc);
+        }
+
+        tclass_enter_removed(tclass);
+
+        if (tclass->state == 0) {
+                log_tclass_debug(tclass, link, "Forgetting");
+                tclass = tclass_free(tclass);
+        } else
+                log_tclass_debug(tclass, link, "Removed");
+
+        return tclass;
+}
+
 static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, TClass *tclass) {
         int r;
 
@@ -464,14 +491,9 @@ int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, M
                 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
+                if (tclass)
+                        (void) tclass_drop(tclass);
+                else
                         log_tclass_debug(tmp, link, "Kernel removed unknown");
 
                 break;
index 606bb3fef3345a0456a8717b200120d470c635df..f0ee318c76f2230829cd4ab77008e9d3b281678c 100644 (file)
@@ -58,6 +58,8 @@ 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);
 
+TClass* tclass_drop(TClass *tclass);
+
 int link_find_tclass(Link *link, uint32_t classid, TClass **ret);
 
 int link_request_tclass(Link *link, TClass *tclass);