]> git.ipfire.org Git - people/ms/network.git/blobdiff - src/networkd/port.c
ports: Do not expect to come back after creating links
[people/ms/network.git] / src / networkd / port.c
index 41b8b2426bb79f310fb8fa56aa7258b06872b8a3..c87ed599db74c3a74af34c1fb8934f3adb6b0fb7 100644 (file)
 #include "config.h"
 #include "link.h"
 #include "logging.h"
-#include "string.h"
 #include "port.h"
-
-struct nw_port {
-       nw_daemon* daemon;
-       int nrefs;
-
-       char name[IF_NAMESIZE];
-       nw_port_type_t type;
-
-       // Configuration
-       nw_config *config;
-
-       // Common attributes
-       nw_address_t address;
-};
+#include "port-dummy.h"
+#include "port-vlan.h"
+#include "stats-collector.h"
+#include "string.h"
 
 static const struct nw_port_type_map {
        nw_port_type_t type;
        const char* name;
 } nw_port_type_map[] = {
-       { NW_PORT_DUMMY, "dummy" },
+       { NW_PORT_DUMMY,   "dummy" },
+       { NW_PORT_VLAN,    "vlan" },
        { NW_PORT_UNKNOWN, NULL },
 };
 
@@ -66,6 +56,8 @@ static nw_port_type_t nw_port_type_from_string(const char* s) {
 }
 
 static void nw_port_free(nw_port* port) {
+       if (port->link)
+               nw_link_unref(port->link);
        if (port->config)
                nw_config_unref(port->config);
        if (port->daemon)
@@ -80,7 +72,7 @@ static int nw_port_setup_address(nw_port* port) {
        // Read ADDRESS from configuration
        const char* s = nw_config_get(port->config, "ADDRESS");
        if (!s) {
-               ERROR("Port %s: Address isn't set\n", port->name);
+               ERROR("Port %s: Address is not set\n", port->name);
                goto ERROR;
        }
 
@@ -122,52 +114,74 @@ static int nw_port_setup_common(nw_port* port) {
        return 0;
 }
 
-static nw_port_type_t nw_port_setup_type(nw_port* port) {
-       const char* type = nw_config_get(port->config, "TYPE");
-       if (!type)
-               return NW_PORT_UNKNOWN;
+static int nw_port_set_link(nw_port* port, nw_link* link) {
+       // Do nothing if the same link is being re-assigned
+       if (port->link == link)
+               return 0;
+
+       // Dereference the former link if set
+       if (port->link)
+               nw_link_unref(port->link);
+
+       // Store the new link
+       if (link) {
+               port->link = nw_link_ref(link);
+
+               DEBUG("Port %s: Assigned link %d\n", port->name, nw_link_ifindex(port->link));
+
+       // Or clear the pointer if no link has been provided
+       } else {
+               port->link = NULL;
 
-       return nw_port_type_from_string(type);
+               DEBUG("Port %s: Removed link\n", port->name);
+       }
+
+       return 0;
 }
 
 static int nw_port_setup(nw_port* port) {
+       nw_link* link = NULL;
        char path[PATH_MAX];
        int r;
 
+       // Find the link
+       link = nw_daemon_get_link_by_name(port->daemon, port->name);
+       if (link) {
+               r = nw_port_set_link(port, link);
+               if (r)
+                       goto ERROR;
+       }
+
        // Compose the path to the main configuration file
        r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
        if (r)
-               return r;
+               goto ERROR;
 
        // Initialize the configuration
        r = nw_config_create(&port->config, path);
        if (r)
-               return r;
-
-       // Determine type
-       port->type = nw_port_setup_type(port);
-       if (!port->type) {
-               ERROR("Could not determine type of port %s\n", port->name);
-               return 0;
-       }
+               goto ERROR;
 
        // Perform some common initialization
        r = nw_port_setup_common(port);
        if (r)
-               return r;
+               goto ERROR;
 
        // Call any custom initialization
-       switch (port->type) {
-               // These do not need any special initialization
-               case NW_PORT_DUMMY:
-               case NW_PORT_UNKNOWN:
-                       break;
+       if (NW_PORT_OPS(port)->config_read) {
+               r = NW_PORT_OPS(port)->config_read(port);
+               if (r)
+                       goto ERROR;
        }
 
-       return 0;
+ERROR:
+       if (link)
+               nw_link_unref(link);
+
+       return r;
 }
 
-int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name) {
+int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const char* name) {
        int r;
 
        // Allocate a new object
@@ -181,6 +195,20 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name) {
        // Initialize reference counter
        p->nrefs = 1;
 
+       // Store the type
+       p->type = type;
+
+       // Set operations
+       switch (p->type) {
+               case NW_PORT_DUMMY:
+                       p->info = &nw_port_info_dummy;
+                       break;
+
+               case NW_PORT_VLAN:
+                       p->info = &nw_port_info_vlan;
+                       break;
+       }
+
        // Store the name
        r = nw_string_set(p->name, name);
        if (r)
@@ -199,6 +227,36 @@ ERROR:
        return r;
 }
 
+int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+               const char* name, const char* path) {
+       nw_config* config = NULL;
+       int r;
+
+       // Initialize the configuration
+       r = nw_config_create(&config, path);
+       if (r)
+               goto ERROR;
+
+       // Fetch the type
+       const char* type = nw_config_get(config, "TYPE");
+       if (!type) {
+               ERROR("Port configuration %s has no TYPE\n", path);
+               r = 1;
+               goto ERROR;
+       }
+
+       // Create a new port
+       r = nw_port_create(port, daemon, nw_port_type_from_string(type), name);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (config)
+               nw_config_unref(config);
+
+       return r;
+}
+
 nw_port* nw_port_ref(nw_port* port) {
        port->nrefs++;
 
@@ -213,6 +271,93 @@ nw_port* nw_port_unref(nw_port* port) {
        return NULL;
 }
 
+/*
+       This is a helper function for when we pass a reference to the event loop
+       it will have to dereference the port instance later.
+*/
+static void __nw_port_unref(void* data) {
+       nw_port* port = (nw_port*)data;
+
+       nw_port_unref(port);
+}
+
+int nw_port_destroy(nw_port* port) {
+       int r;
+
+       DEBUG("Destroying port %s\n", port->name);
+
+       // Destroy the physical link (if exists)
+       if (port->link) {
+               r = nw_link_destroy(port->link);
+               if (r)
+                       return r;
+       }
+
+       // Dereference the port from other ports
+       r = nw_daemon_ports_walk(port->daemon, __nw_port_drop_port, port);
+       if (r)
+               return r;
+
+       // Dereference the port from other zones
+       r = nw_daemon_zones_walk(port->daemon, __nw_zone_drop_port, port);
+       if (r)
+               return r;
+
+       // Destroy the configuration
+       r = nw_config_destroy(port->config);
+       if (r)
+               return r;
+
+       // Reset type
+       port->type = NW_PORT_UNKNOWN;
+
+       return 0;
+}
+
+int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+       nw_port* dropped_port = (nw_port*)data;
+       int r;
+
+       switch (port->type) {
+               case NW_PORT_VLAN:
+                       if (port->vlan.parent == dropped_port) {
+                               r = nw_port_set_vlan_parent(port, NULL);
+                               if (r)
+                                       return r;
+                       }
+                       break;
+
+               case NW_PORT_DUMMY:
+               case NW_PORT_UNKNOWN:
+                       break;
+       }
+
+       return 0;
+}
+
+int nw_port_save(nw_port* port) {
+       int r;
+
+       // Call the custom handler
+       if (NW_PORT_OPS(port)->config_write) {
+               r = NW_PORT_OPS(port)->config_write(port);
+               if (r)
+                       goto ERROR;
+       }
+
+       // Write the configuration
+       r = nw_config_write(port->config);
+       if (r)
+               return r;
+
+       return 0;
+
+ERROR:
+       ERROR("Could not save configuration for port %s: %m\n", port->name);
+
+       return 1;
+}
+
 const char* nw_port_name(nw_port* port) {
        return port->name;
 }
@@ -229,10 +374,245 @@ char* nw_port_bus_path(nw_port* port) {
        return p;
 }
 
+int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) {
+       nw_link* link = (nw_link*)data;
+
+       // Fetch the link name
+       const char* ifname = nw_link_ifname(link);
+       if (!ifname) {
+               ERROR("Link does not have a name set\n");
+               return 1;
+       }
+
+       // Set link if the name matches
+       if (strcmp(port->name, ifname) == 0)
+               return nw_port_set_link(port, link);
+
+       // If we have the link set but the name did not match, we will remove it
+       else if (port->link == link)
+               return nw_port_set_link(port, NULL);
+
+       return 0;
+}
+
+int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
+       nw_link* link = (nw_link*)data;
+
+       // Drop the link if it matches
+       if (port->link == link)
+               return nw_port_set_link(port, NULL);
+
+       return 0;
+}
+
 static nw_link* nw_port_get_link(nw_port* port) {
-       return nw_daemon_get_link_by_name(port->daemon, port->name);
+       // Fetch the link if not set
+       if (!port->link)
+               port->link = nw_daemon_get_link_by_name(port->daemon, port->name);
+
+       if (!port->link)
+               return NULL;
+
+       return nw_link_ref(port->link);
 }
 
 const nw_address_t* nw_port_get_address(nw_port* port) {
        return &port->address;
 }
+
+static int nw_port_is_disabled(nw_port* port) {
+       return nw_config_get_bool(port->config, "DISABLED");
+}
+
+static nw_link* nw_port_get_parent_link(nw_port* port) {
+       nw_port* parent = NULL;
+       nw_link* link = NULL;
+
+       // Do nothing if not implemented
+       if (!NW_PORT_OPS(port)->get_parent_port)
+               goto ERROR;
+
+       // Fetch the parent
+       parent = NW_PORT_OPS(port)->get_parent_port(port);
+       if (!parent)
+               goto ERROR;
+
+       // Fetch the link
+       link = nw_port_get_link(parent);
+
+ERROR:
+       if (parent)
+               nw_port_unref(parent);
+
+       return link;
+}
+
+static int __nw_port_create_link(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
+       nw_port* port = (nw_port*)data;
+       int r;
+
+       // Check if the operation was successful
+       r = sd_netlink_message_get_errno(m);
+       if (r < 0) {
+               ERROR("Could not create port %s: %s\n", port->name, strerror(-r));
+               // XXX We should extract the error message
+
+               return 0;
+       }
+
+       DEBUG("Successfully created %s\n", port->name);
+
+       return 0;
+}
+
+static int nw_port_create_link(nw_port* port) {
+       sd_netlink_message* m = NULL;
+       nw_link* link = NULL;
+       int r;
+
+       sd_netlink* rtnl = nw_daemon_get_rtnl(port->daemon);
+
+       // Create a new link
+       r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+       if (r < 0) {
+               ERROR("Could not create netlink message: %m\n");
+               goto ERROR;
+       }
+
+       // Set the name
+       r = sd_netlink_message_append_string(m, IFLA_IFNAME, port->name);
+       if (r < 0) {
+               ERROR("Could not set port name: %s\n", strerror(-r));
+               goto ERROR;
+       }
+
+       // XXX Set common things like MAC address, etc.
+
+       // Fetch the parent link
+       link = nw_port_get_parent_link(port);
+       if (link) {
+               r = sd_netlink_message_append_u32(m, IFLA_LINK, nw_link_ifindex(link));
+               if (r < 0)
+                       goto ERROR;
+       }
+
+       // Open an IFLA_LINKINFO container
+       r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+       if (r < 0)
+               goto ERROR;
+
+       // Run the custom setup
+       if (NW_PORT_OPS(port)->create_link) {
+               r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, NW_PORT_INFO(port)->kind);
+               if (r < 0) {
+                       ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r));
+                       goto ERROR;
+               }
+
+               r = NW_PORT_OPS(port)->create_link(port, m);
+               if (r) {
+                       ERROR("Could not create port %s: %m\n", port->name);
+                       goto ERROR;
+               }
+
+               // Close the container
+               r = sd_netlink_message_close_container(m);
+               if (r < 0)
+                       goto ERROR;
+
+       // Just set IFLA_INFO_KIND if there is no custom function
+       } else {
+               r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, NW_PORT_INFO(port)->kind);
+               if (r < 0)
+                       goto ERROR;
+       }
+
+       // Close the container
+       r = sd_netlink_message_close_container(m);
+       if (r < 0)
+               goto ERROR;
+
+       // Send the message
+       r = sd_netlink_call_async(rtnl, NULL, m, __nw_port_create_link,
+               __nw_port_unref, nw_port_ref(port), -1, NULL);
+       if (r < 0) {
+               ERROR("Could not send netlink message: %s\n", strerror(-r));
+               goto ERROR;
+       }
+
+       r = 0;
+
+ERROR:
+       if (m)
+               sd_netlink_message_unref(m);
+       if (link)
+               nw_link_unref(link);
+
+       return r;
+}
+
+int nw_port_reconfigure(nw_port* port) {
+       int r;
+
+       // If the port is disabled, we will try to destroy it
+       if (nw_port_is_disabled(port)) {
+               if (port->link) {
+                       r = nw_link_destroy(port->link);
+                       if (r)
+                               return r;
+               }
+
+               return 0;
+       }
+
+       // If there is no link, we will try to create it
+       if (!port->link)
+               return nw_port_create_link(port);
+
+       // XXX TODO
+
+       return 0;
+}
+
+int nw_port_has_carrier(nw_port* port) {
+       if (!port->link)
+               return 0;
+
+       return nw_link_has_carrier(port->link);
+}
+
+/*
+       Stats
+*/
+
+const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) {
+       if (!port->link)
+               return NULL;
+
+       return nw_link_get_stats64(port->link);
+}
+
+int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) {
+       nw_link* link = (nw_link*)data;
+
+       // Emit stats if link matches
+       if (port->link == link)
+               return nw_stats_collector_emit_port_stats(daemon, port);
+
+       return 0;
+}
+
+int nw_port_update_stats(nw_port* port) {
+       if (port->link)
+               return nw_link_update_stats(port->link);
+
+       return 0;
+}
+
+int nw_port_check_type(nw_port* port, const nw_port_type_t type) {
+       if (port->type == type)
+               return 0;
+
+       errno = ENOTSUP;
+       return -errno;
+}