]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/netdev/netdev.c
network: make Link and NetDev always have the valid poiter to Manager
[thirdparty/systemd.git] / src / network / netdev / netdev.c
index 93df63b5c1d65da2f7cf3a25a994a2f957079bbd..65959f4a03f308a22699b0cb77f573db28154349 100644 (file)
@@ -64,6 +64,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
         [NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
         [NETDEV_KIND_NETDEVSIM] = &netdevsim_vtable,
         [NETDEV_KIND_FOU] = &foutnl_vtable,
+        [NETDEV_KIND_ERSPAN] = &erspan_vtable,
 };
 
 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@@ -94,44 +95,77 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
         [NETDEV_KIND_WIREGUARD] = "wireguard",
         [NETDEV_KIND_NETDEVSIM] = "netdevsim",
         [NETDEV_KIND_FOU] = "fou",
+        [NETDEV_KIND_ERSPAN] = "erspan",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
 
-static void netdev_cancel_callbacks(NetDev *netdev) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        netdev_join_callback *callback;
+int config_parse_netdev_kind(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        NetDevKind k, *kind = data;
+
+        assert(rvalue);
+        assert(data);
+
+        k = netdev_kind_from_string(rvalue);
+        if (k < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse netdev kind, ignoring assignment: %s", rvalue);
+                return 0;
+        }
 
-        if (!netdev || !netdev->manager)
-                return;
+        if (*kind != _NETDEV_KIND_INVALID && *kind != k) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Specified netdev kind is different from the previous value '%s', ignoring assignment: %s",
+                           netdev_kind_to_string(*kind), rvalue);
+                return 0;
+        }
 
-        rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
+        *kind = k;
 
-        while ((callback = netdev->callbacks)) {
-                if (m) {
-                        assert(callback->link);
-                        assert(callback->callback);
-                        assert(netdev->manager);
-                        assert(netdev->manager->rtnl);
+        return 0;
+}
 
-                        callback->callback(netdev->manager->rtnl, m, callback->link);
-                }
+static void netdev_callbacks_clear(NetDev *netdev) {
+        netdev_join_callback *callback;
+
+        if (!netdev)
+                return;
 
+        while ((callback = netdev->callbacks)) {
                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
                 link_unref(callback->link);
                 free(callback);
         }
 }
 
-static void netdev_free(NetDev *netdev) {
-        if (!netdev)
-                return;
+bool netdev_is_managed(NetDev *netdev) {
+        if (!netdev || !netdev->manager || !netdev->ifname)
+                return false;
 
-        netdev_cancel_callbacks(netdev);
+        return hashmap_get(netdev->manager->netdevs, netdev->ifname) == netdev;
+}
 
+static void netdev_detach_from_manager(NetDev *netdev) {
         if (netdev->ifname && netdev->manager)
                 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
+}
+
+static NetDev *netdev_free(NetDev *netdev) {
+        assert(netdev);
+
+        netdev_callbacks_clear(netdev);
+
+        netdev_detach_from_manager(netdev);
 
         free(netdev->filename);
 
@@ -157,22 +191,10 @@ static void netdev_free(NetDev *netdev) {
             NETDEV_VTABLE(netdev)->done)
                 NETDEV_VTABLE(netdev)->done(netdev);
 
-        free(netdev);
+        return mfree(netdev);
 }
 
-NetDev *netdev_unref(NetDev *netdev) {
-        if (netdev && (-- netdev->n_ref <= 0))
-                netdev_free(netdev);
-
-        return NULL;
-}
-
-NetDev *netdev_ref(NetDev *netdev) {
-        if (netdev)
-                assert_se(++ netdev->n_ref >= 2);
-
-        return netdev;
-}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free);
 
 void netdev_drop(NetDev *netdev) {
         if (!netdev || netdev->state == NETDEV_STATE_LINGER)
@@ -182,7 +204,9 @@ void netdev_drop(NetDev *netdev) {
 
         log_netdev_debug(netdev, "netdev removed");
 
-        netdev_cancel_callbacks(netdev);
+        netdev_callbacks_clear(netdev);
+
+        netdev_detach_from_manager(netdev);
 
         netdev_unref(netdev);
 
@@ -210,12 +234,12 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) {
 static int netdev_enter_failed(NetDev *netdev) {
         netdev->state = NETDEV_STATE_FAILED;
 
-        netdev_cancel_callbacks(netdev);
+        netdev_callbacks_clear(netdev);
 
         return 0;
 }
 
-static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_handler_t callback) {
+static int netdev_enslave_ready(NetDev *netdev, Link* link, link_netlink_message_handler_t callback) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
@@ -242,7 +266,8 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_h
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_MASTER attribute: %m");
 
-        r = sd_netlink_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
+        r = netlink_call_async(netdev->manager->rtnl, NULL, req, callback,
+                               link_netlink_destroy_callback, link);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
 
@@ -286,10 +311,10 @@ static int netdev_enter_ready(NetDev *netdev) {
 }
 
 /* callback for netdev's created without a backing Link */
-static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
-        _cleanup_(netdev_unrefp) NetDev *netdev = userdata;
+static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
         int r;
 
+        assert(netdev);
         assert(netdev->state != _NETDEV_STATE_INVALID);
 
         r = sd_netlink_message_get_errno(m);
@@ -307,7 +332,7 @@ static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *
         return 1;
 }
 
-int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) {
+static int netdev_enslave(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
         int r;
 
         assert(netdev);
@@ -329,13 +354,14 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t call
                 /* the netdev is not yet read, save this request for when it is */
                 netdev_join_callback *cb;
 
-                cb = new0(netdev_join_callback, 1);
+                cb = new(netdev_join_callback, 1);
                 if (!cb)
                         return log_oom();
 
-                cb->callback = callback;
-                cb->link = link;
-                link_ref(link);
+                *cb = (netdev_join_callback) {
+                        .callback = callback,
+                        .link = link_ref(link),
+                };
 
                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
 
@@ -482,8 +508,7 @@ int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
         return 0;
 }
 
-static int netdev_create(NetDev *netdev, Link *link,
-                         sd_netlink_message_handler_t callback) {
+static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
         int r;
 
         assert(netdev);
@@ -550,13 +575,15 @@ static int netdev_create(NetDev *netdev, Link *link,
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
 
                 if (link) {
-                        r = sd_netlink_call_async(netdev->manager->rtnl, m, callback, link, 0, NULL);
+                        r = netlink_call_async(netdev->manager->rtnl, NULL, m, callback,
+                                               link_netlink_destroy_callback, link);
                         if (r < 0)
                                 return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
 
                         link_ref(link);
                 } else {
-                        r = sd_netlink_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL);
+                        r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler,
+                                               netdev_destroy_callback, netdev);
                         if (r < 0)
                                 return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
 
@@ -572,7 +599,7 @@ static int netdev_create(NetDev *netdev, Link *link,
 }
 
 /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
-int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) {
+int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
         int r;
 
         assert(netdev);
@@ -600,7 +627,7 @@ int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callbac
         return 0;
 }
 
-static int netdev_load_one(Manager *manager, const char *filename) {
+int netdev_load_one(Manager *manager, const char *filename) {
         _cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
         _cleanup_fclose_ FILE *file = NULL;
         const char *dropin_dirname;
@@ -623,13 +650,15 @@ static int netdev_load_one(Manager *manager, const char *filename) {
                 return 0;
         }
 
-        netdev_raw = new0(NetDev, 1);
+        netdev_raw = new(NetDev, 1);
         if (!netdev_raw)
                 return log_oom();
 
-        netdev_raw->n_ref = 1;
-        netdev_raw->kind = _NETDEV_KIND_INVALID;
-        netdev_raw->state = _NETDEV_STATE_INVALID; /* an invalid state means done() of the implementation won't be called on destruction */
+        *netdev_raw = (NetDev) {
+                .n_ref = 1,
+                .kind = _NETDEV_KIND_INVALID,
+                .state = _NETDEV_STATE_INVALID, /* an invalid state means done() of the implementation won't be called on destruction */
+        };
 
         dropin_dirname = strjoina(basename(filename), ".d");
         r = config_parse_many(filename, network_dirs, dropin_dirname,
@@ -673,10 +702,10 @@ static int netdev_load_one(Manager *manager, const char *filename) {
         if (NETDEV_VTABLE(netdev)->init)
                 NETDEV_VTABLE(netdev)->init(netdev);
 
-        r = config_parse(NULL, filename, file,
-                         NETDEV_VTABLE(netdev)->sections,
-                         config_item_perf_lookup, network_netdev_gperf_lookup,
-                         CONFIG_PARSE_WARN, netdev);
+        r = config_parse_many(filename, network_dirs, dropin_dirname,
+                              NETDEV_VTABLE(netdev)->sections,
+                              config_item_perf_lookup, network_netdev_gperf_lookup,
+                              CONFIG_PARSE_WARN, netdev);
         if (r < 0)
                 return r;
 
@@ -697,6 +726,10 @@ static int netdev_load_one(Manager *manager, const char *filename) {
                         return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname);
         }
 
+        r = hashmap_ensure_allocated(&netdev->manager->netdevs, &string_hash_ops);
+        if (r < 0)
+                return r;
+
         r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
         if (r < 0)
                 return r;
@@ -762,14 +795,12 @@ static int netdev_load_one(Manager *manager, const char *filename) {
 
 int netdev_load(Manager *manager) {
         _cleanup_strv_free_ char **files = NULL;
-        NetDev *netdev;
         char **f;
         int r;
 
         assert(manager);
 
-        while ((netdev = hashmap_first(manager->netdevs)))
-                netdev_unref(netdev);
+        hashmap_clear_with_destructor(manager->netdevs, netdev_unref);
 
         r = conf_files_list_strv(&files, ".netdev", NULL, 0, network_dirs);
         if (r < 0)