]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/netdev/netdev.c
networkd: introduce netdev "Netdevsim" Driver
[thirdparty/systemd.git] / src / network / netdev / netdev.c
index 9b9e83d9db4242751532c1a9e0ba1cb7c8c054ae..da6a50c5a2711597e33792aa45fec73297351c21 100644 (file)
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
   Copyright 2013 Tom Gundersen <teg@jklm.no>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
 #include <net/if.h>
@@ -28,6 +16,7 @@
 #include "network-internal.h"
 #include "netdev/netdev.h"
 #include "networkd-manager.h"
+#include "networkd-link.h"
 #include "siphash24.h"
 #include "stat-util.h"
 #include "string-table.h"
@@ -35,6 +24,7 @@
 
 #include "netdev/bridge.h"
 #include "netdev/bond.h"
+#include "netdev/geneve.h"
 #include "netdev/vlan.h"
 #include "netdev/macvlan.h"
 #include "netdev/ipvlan.h"
@@ -45,6 +35,9 @@
 #include "netdev/dummy.h"
 #include "netdev/vrf.h"
 #include "netdev/vcan.h"
+#include "netdev/vxcan.h"
+#include "netdev/wireguard.h"
+#include "netdev/netdevsim.h"
 
 const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
         [NETDEV_KIND_BRIDGE] = &bridge_vtable,
@@ -69,6 +62,10 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
         [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable,
         [NETDEV_KIND_VRF] = &vrf_vtable,
         [NETDEV_KIND_VCAN] = &vcan_vtable,
+        [NETDEV_KIND_GENEVE] = &geneve_vtable,
+        [NETDEV_KIND_VXCAN] = &vxcan_vtable,
+        [NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
+        [NETDEV_KIND_NETDEVSIM] = &netdevsim_vtable,
 };
 
 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@@ -94,6 +91,10 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
         [NETDEV_KIND_IP6TNL] = "ip6tnl",
         [NETDEV_KIND_VRF] = "vrf",
         [NETDEV_KIND_VCAN] = "vcan",
+        [NETDEV_KIND_GENEVE] = "geneve",
+        [NETDEV_KIND_VXCAN] = "vxcan",
+        [NETDEV_KIND_WIREGUARD] = "wireguard",
+        [NETDEV_KIND_NETDEVSIM] = "netdevsim",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
@@ -103,10 +104,10 @@ static void netdev_cancel_callbacks(NetDev *netdev) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         netdev_join_callback *callback;
 
-        if (!netdev)
+        if (!netdev || !netdev->manager)
                 return;
 
-        rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
+        rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
 
         while ((callback = netdev->callbacks)) {
                 if (m) {
@@ -130,7 +131,7 @@ static void netdev_free(NetDev *netdev) {
 
         netdev_cancel_callbacks(netdev);
 
-        if (netdev->ifname)
+        if (netdev->ifname && netdev->manager)
                 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
 
         free(netdev->filename);
@@ -141,10 +142,19 @@ static void netdev_free(NetDev *netdev) {
 
         condition_free_list(netdev->match_host);
         condition_free_list(netdev->match_virt);
-        condition_free_list(netdev->match_kernel);
+        condition_free_list(netdev->match_kernel_cmdline);
+        condition_free_list(netdev->match_kernel_version);
         condition_free_list(netdev->match_arch);
 
-        if (NETDEV_VTABLE(netdev) &&
+        /* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that
+         * because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure
+         * allocation, with no room for per-kind fields), and once to read the kind's properties (with a full,
+         * comprehensive NetDev structure allocation with enough space for whatever the specific kind needs). Now, in
+         * the first case we shouldn't try to destruct the per-kind NetDev fields on destruction, in the second case we
+         * should. We use the state field to discern the two cases: it's _NETDEV_STATE_INVALID on the first "raw"
+         * call. */
+        if (netdev->state != _NETDEV_STATE_INVALID &&
+            NETDEV_VTABLE(netdev) &&
             NETDEV_VTABLE(netdev)->done)
                 NETDEV_VTABLE(netdev)->done(netdev);
 
@@ -218,6 +228,13 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_h
         assert(link);
         assert(callback);
 
+        if (link->flags & IFF_UP && netdev->kind == NETDEV_KIND_BOND) {
+                log_netdev_debug(netdev, "Link '%s' was up when attempting to enslave it. Bringing link down.", link->ifname);
+                r = link_down(link);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not bring link down: %m");
+        }
+
         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m");
@@ -271,7 +288,7 @@ 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_unref_ NetDev *netdev = userdata;
+        _cleanup_(netdev_unrefp) NetDev *netdev = userdata;
         int r;
 
         assert(netdev->state != _NETDEV_STATE_INVALID);
@@ -306,7 +323,7 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t call
         } else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) {
                 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
 
-                r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
+                r = rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
                 if (r >= 0)
                         callback(netdev->manager->rtnl, m, link);
         } else {
@@ -461,8 +478,7 @@ int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
         mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
         mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
 
-        *ret = mac;
-        mac = NULL;
+        *ret = TAKE_PTR(mac);
 
         return 0;
 }
@@ -586,10 +602,10 @@ int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callbac
 }
 
 static int netdev_load_one(Manager *manager, const char *filename) {
-        _cleanup_netdev_unref_ NetDev *netdev = NULL;
-        _cleanup_free_ NetDev *netdev_raw = NULL;
+        _cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
         _cleanup_fclose_ FILE *file = NULL;
         const char *dropin_dirname;
+        bool independent = false;
         int r;
 
         assert(manager);
@@ -599,8 +615,8 @@ static int netdev_load_one(Manager *manager, const char *filename) {
         if (!file) {
                 if (errno == ENOENT)
                         return 0;
-                else
-                        return -errno;
+
+                return -errno;
         }
 
         if (null_or_empty_fd(fileno(file))) {
@@ -612,24 +628,23 @@ static int netdev_load_one(Manager *manager, const char *filename) {
         if (!netdev_raw)
                 return log_oom();
 
+        netdev_raw->n_ref = 1;
         netdev_raw->kind = _NETDEV_KIND_INVALID;
-        dropin_dirname = strjoina(basename(filename), ".d");
+        netdev_raw->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,
                               "Match\0NetDev\0",
                               config_item_perf_lookup, network_netdev_gperf_lookup,
-                              true, netdev_raw);
+                              CONFIG_PARSE_WARN|CONFIG_PARSE_RELAXED, netdev_raw);
         if (r < 0)
                 return r;
 
-        r = fseek(file, 0, SEEK_SET);
-        if (r < 0)
-                return -errno;
-
         /* skip out early if configuration does not match the environment */
         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
                              netdev_raw->match_host, netdev_raw->match_virt,
-                             netdev_raw->match_kernel, netdev_raw->match_arch,
+                             netdev_raw->match_kernel_cmdline, netdev_raw->match_kernel_version,
+                             netdev_raw->match_arch,
                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
                 return 0;
 
@@ -643,15 +658,18 @@ static int netdev_load_one(Manager *manager, const char *filename) {
                 return 0;
         }
 
+        r = fseek(file, 0, SEEK_SET);
+        if (r < 0)
+                return -errno;
+
         netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
         if (!netdev)
                 return log_oom();
 
         netdev->n_ref = 1;
         netdev->manager = manager;
-        netdev->state = _NETDEV_STATE_INVALID;
         netdev->kind = netdev_raw->kind;
-        netdev->ifname = netdev_raw->ifname;
+        netdev->state = NETDEV_STATE_LOADING; /* we initialize the state here for the first time, so that done() will be called on destruction */
 
         if (NETDEV_VTABLE(netdev)->init)
                 NETDEV_VTABLE(netdev)->init(netdev);
@@ -659,7 +677,7 @@ static int netdev_load_one(Manager *manager, const char *filename) {
         r = config_parse(NULL, filename, file,
                          NETDEV_VTABLE(netdev)->sections,
                          config_item_perf_lookup, network_netdev_gperf_lookup,
-                         false, false, false, netdev);
+                         CONFIG_PARSE_WARN, netdev);
         if (r < 0)
                 return r;
 
@@ -693,13 +711,51 @@ static int netdev_load_one(Manager *manager, const char *filename) {
         case NETDEV_CREATE_INDEPENDENT:
                 r = netdev_create(netdev, NULL, NULL);
                 if (r < 0)
-                        return 0;
+                        return r;
 
                 break;
         default:
                 break;
         }
 
+        switch (netdev->kind) {
+        case NETDEV_KIND_IPIP:
+                independent = IPIP(netdev)->independent;
+                break;
+        case NETDEV_KIND_GRE:
+                independent = GRE(netdev)->independent;
+                break;
+        case NETDEV_KIND_GRETAP:
+                independent = GRETAP(netdev)->independent;
+                break;
+        case NETDEV_KIND_IP6GRE:
+                independent = IP6GRE(netdev)->independent;
+                break;
+        case NETDEV_KIND_IP6GRETAP:
+                independent = IP6GRETAP(netdev)->independent;
+                break;
+        case NETDEV_KIND_SIT:
+                independent = SIT(netdev)->independent;
+                break;
+        case NETDEV_KIND_VTI:
+                independent = VTI(netdev)->independent;
+                break;
+        case NETDEV_KIND_VTI6:
+                independent = VTI6(netdev)->independent;
+                break;
+        case NETDEV_KIND_IP6TNL:
+                independent = IP6TNL(netdev)->independent;
+                break;
+        default:
+                break;
+        }
+
+        if (independent) {
+                r = netdev_create(netdev, NULL, NULL);
+                if (r < 0)
+                        return r;
+        }
+
         netdev = NULL;
 
         return 0;
@@ -716,7 +772,7 @@ int netdev_load(Manager *manager) {
         while ((netdev = hashmap_first(manager->netdevs)))
                 netdev_unref(netdev);
 
-        r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
+        r = conf_files_list_strv(&files, ".netdev", NULL, 0, network_dirs);
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate netdev files: %m");