]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: tc: monitor qdisc and tclass
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 25 Jan 2022 23:42:22 +0000 (08:42 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 9 Feb 2022 05:43:19 +0000 (14:43 +0900)
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/tc/qdisc.c
src/network/tc/qdisc.h
src/network/tc/tc.c
src/network/tc/tc.h
src/network/tc/tclass.c
src/network/tc/tclass.h

index addf0d4afeee0667dcf3b108c55019bbcee383d1..db55b467f80c15fe8c98b60cf4f883260807ffb6 100644 (file)
@@ -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);
 
index 7ccb31df79199250c7d153b5432bb29ae16c6032..23a893449f26e1f15005cd9dbdb669e25178c179 100644 (file)
@@ -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;
index 3261f289e91486ae74aba012218b7afae36d4f45..fab0b4094c337e50e9b5b3d79ecfaa10f1937140 100644 (file)
 #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");
index f0e643b9aedba01a6b0bd7655d1f633d0eabcdc2..11c092d4bf79547c79eee5bb138ba9e6527d21ba 100644 (file)
@@ -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;
 
index 8329d18b4f5de8655bf140e3d9ead1ec1f3dc385..29ceb50183c59fa81e09cbb4cd928109cbfbeea0 100644 (file)
@@ -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);
index c0f6989b1b6f875e07a2ca3b403f8f19cbbbcc2c..55d1b0c93713bfd719ec29de51ac16c79f0c79aa 100644 (file)
@@ -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);
index badd5227c6fe9cbb397aba3daabdc83576967443..954dcd39f9c28a17721a8ea7cd16a179a3a39448 100644 (file)
@@ -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);
index 860d8c997a9fac95d22323f43d9c712245d5a076..cd61b42c4e241107b69b7b275ec23355913a552f 100644 (file)
@@ -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;
-
-        tclass->meta.kind = TC_KIND_TCLASS,
-        tclass->parent = TC_H_ROOT;
-        tclass->kind = kind;
+        if (kind == _TCLASS_KIND_INVALID) {
+                tclass = new(TClass, 1);
+                if (!tclass)
+                        return -ENOMEM;
+
+                *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;
+
+                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;
+                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;
 
index 5f8016c187258c868f2db2c9310c8e770378b126..9e57e9566f8dd28323b2c057c665a404153cfa5e 100644 (file)
@@ -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);