[systemd-devel] [PATCH v2] Added support for Uplink Failure Detection using BindCarrier

Alin Rauta alin.rauta at intel.com
Mon Feb 9 03:06:46 PST 2015


---
 man/systemd.network.xml                  |  11 ++
 src/libsystemd/sd-network/sd-network.c   |   4 +
 src/network/networkctl.c                 | 212 ++++++++++++++++++++++++++++---
 src/network/networkd-link.c              | 178 ++++++++++++++++++++++++++
 src/network/networkd-link.h              |  10 ++
 src/network/networkd-manager.c           |   7 +
 src/network/networkd-network-gperf.gperf |   1 +
 src/network/networkd.h                   |   2 +
 src/systemd/sd-network.h                 |   3 +
 9 files changed, 412 insertions(+), 16 deletions(-)

diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index b8facdc..5f2c328 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -268,6 +268,17 @@
           </listitem>
         </varlistentry>
         <varlistentry>
+          <term><varname>BindCarrier=</varname></term>
+          <listitem>
+            <para>A port or a list of ports. When set, controls the
+            behaviour of the current interface. When all ports in the list
+            are operational down, the failure is propagated to the current
+            interface. When at least one port has carrier, the current
+            interface is brought up.
+            </para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
           <term><varname>Address=</varname></term>
           <listitem>
             <para>A static IPv4 or IPv6 address and its prefix length,
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index c735cac..b574d19 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -264,6 +264,10 @@ _public_ int sd_network_link_get_domains(int ifindex, char ***ret) {
         return network_get_link_strv("DOMAINS", ifindex, ret);
 }
 
+_public_ int sd_network_link_get_carriers(int ifindex, char ***ret) {
+        return network_get_link_strv("CARRIERS", ifindex, ret);
+}
+
 _public_ int sd_network_link_get_wildcard_domain(int ifindex) {
         int r;
         _cleanup_free_ char *p = NULL, *s = NULL;
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index aa83f32..fe9248f 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -22,6 +22,7 @@
 #include <stdbool.h>
 #include <getopt.h>
 #include <net/if.h>
+#include <fnmatch.h>
 
 #include "sd-network.h"
 #include "sd-rtnl.h"
@@ -393,6 +394,162 @@ static int get_gateway_description(
         return -ENODATA;
 }
 
+static bool is_carrier(const char *ifname,
+                       char **carriers) {
+       char **i;
+
+       STRV_FOREACH(i, carriers)
+               if (0 == fnmatch(*i, ifname, 0))
+                       return true;
+
+       return false;
+}
+
+/* get the links that are bound to this port. */
+static int get_downlinks(const char *name,
+                         sd_rtnl_message *m,
+                         LinkInfo **downlinks,
+                         int *down_count) {
+        _cleanup_free_ LinkInfo  *links = NULL;
+        sd_rtnl_message *i;
+        size_t size = 0;
+        size_t c = 0;
+        int r;
+
+        assert(m);
+        assert(name);
+
+        *down_count = 0;
+        *downlinks = NULL;
+
+        for (i = m; i; i = sd_rtnl_message_next(i)) {
+                _cleanup_strv_free_ char **carriers = NULL;
+                const char *link_name;
+                int ifindex;
+
+                r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
+                if (r < 0)
+                        return r;
+
+                r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &link_name);
+                if (r < 0)
+                        return r;
+
+                r = sd_network_link_get_carriers(ifindex, &carriers);
+                if ((-ENODATA == r) || (NULL == carriers))
+                        continue;
+
+                if (r < 0) {
+                        log_warning("Failed to get carrier list for port: %s", name);
+                        continue;
+                }
+
+                if (is_carrier(name, carriers)) {
+                         if (!GREEDY_REALLOC(links, size, c+1))
+                                 return -ENOMEM;
+
+                         links[c].name = link_name;
+                         c++;
+                }
+        }
+
+        *downlinks = links;
+        *down_count = (int) c;
+
+        links = NULL;
+
+        return 0;
+}
+
+/* get the links to which current port is bound to. */
+static int get_uplinks(int ifindex,
+                       sd_rtnl_message *m,
+                       LinkInfo **uplinks,
+                       int *up_count) {
+        _cleanup_free_ LinkInfo  *links = NULL;
+        _cleanup_strv_free_ char **carriers = NULL;
+        sd_rtnl_message *i;
+        size_t size = 0, c = 0;
+        int r;
+
+        assert(m);
+
+        *up_count = 0;
+        *uplinks = NULL;
+
+        /* check if this port has carriers. */
+        r = sd_network_link_get_carriers(ifindex, &carriers);
+        if ((-ENODATA == r)  || (NULL == carriers))
+                return 0;
+
+        if (r < 0)
+                return r;
+
+        for (i = m; i; i = sd_rtnl_message_next(i)) {
+                const char *name;
+
+                r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
+                if (r < 0)
+                        return r;
+
+                if (is_carrier(name, carriers)) {
+                         if (!GREEDY_REALLOC(links, size, c+1))
+                                 return -ENOMEM;
+
+                         links[c].name = name;
+                         c++;
+                }
+        }
+
+        *uplinks = links;
+        *up_count = (int) c;
+
+        links = NULL;
+
+        return 0;
+}
+
+static int get_links(sd_rtnl *rtnl,
+                     sd_rtnl_message **ret) {
+        _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
+        int r;
+        assert(rtnl);
+
+        r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
+        if (r < 0)
+                return rtnl_log_create_error(r);
+
+        r = sd_rtnl_message_request_dump(req, true);
+        if (r < 0)
+                return rtnl_log_create_error(r);
+
+        r = sd_rtnl_call(rtnl, req, 0, &reply);
+        if (r < 0) {
+                log_error("Failed to enumerate links: %s", strerror(-r));
+                return r;
+        }
+
+        *ret = reply;
+        reply = NULL;
+
+        return 0;
+}
+
+static void dump_carrier_list(const char *prefix,
+                              LinkInfo *links,
+                              int len) {
+        int i;
+
+        assert(links);
+
+        for (i = 0; i < len; i++) {
+                printf("%*s%s\n",
+                       (int) strlen(prefix),
+                       i == 0 ? prefix : "",
+                       links[i].name);
+        }
+}
+
 static int dump_gateways(
                 sd_rtnl *rtnl,
                 sd_hwdb *hwdb,
@@ -508,11 +665,16 @@ static int link_status_one(
         const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
         const char *on_color_operational, *off_color_operational,
                    *on_color_setup, *off_color_setup;
+        _cleanup_rtnl_message_unref_ sd_rtnl_message *link_list = NULL;
+        _cleanup_free_ LinkInfo *uplinks = NULL;    /* links to which current port is bound to. */
+        _cleanup_free_ LinkInfo *downlinks = NULL;  /* links that are bound to current port. */
         struct ether_addr e;
         unsigned iftype;
         int r, ifindex;
         bool have_mac;
         uint32_t mtu;
+        int up_count;
+        int down_count;
 
         assert(rtnl);
         assert(udev);
@@ -606,12 +768,24 @@ static int link_status_one(
 
         sd_network_link_get_network_file(ifindex, &network);
 
+        r = get_links(rtnl, &link_list);
+        if (r < 0)
+                return r;
+
+        r = get_uplinks(ifindex, link_list, &uplinks, &up_count);
+        if (r < 0)
+                log_warning("Failed to get uplinks for port: %d", ifindex);
+
+        r = get_downlinks(name, link_list, &downlinks, &down_count);
+        if (r < 0)
+                log_warning("Failed to get downlinks for port: %d", ifindex);
+
         printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
 
-        printf("   Link File: %s\n"
-               "Network File: %s\n"
-               "        Type: %s\n"
-               "       State: %s%s%s (%s%s%s)\n",
+        printf("       Link File: %s\n"
+               "    Network File: %s\n"
+               "            Type: %s\n"
+               "           State: %s%s%s (%s%s%s)\n",
                strna(link),
                strna(network),
                strna(t),
@@ -619,13 +793,13 @@ static int link_status_one(
                on_color_setup, strna(setup_state), off_color_setup);
 
         if (path)
-                printf("        Path: %s\n", path);
+                printf("            Path: %s\n", path);
         if (driver)
-                printf("      Driver: %s\n", driver);
+                printf("          Driver: %s\n", driver);
         if (vendor)
-                printf("      Vendor: %s\n", vendor);
+                printf("          Vendor: %s\n", vendor);
         if (model)
-                printf("       Model: %s\n", model);
+                printf("           Model: %s\n", model);
 
         if (have_mac) {
                 _cleanup_free_ char *description = NULL;
@@ -634,23 +808,29 @@ static int link_status_one(
                 ieee_oui(hwdb, &e, &description);
 
                 if (description)
-                        printf("  HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
+                        printf("      HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
                 else
-                        printf("  HW Address: %s\n", ether_addr_to_string(&e, ea));
+                        printf("      HW Address: %s\n", ether_addr_to_string(&e, ea));
         }
 
         if (mtu > 0)
-                printf("         MTU: %u\n", mtu);
+                printf("             MTU: %u\n", mtu);
 
-        dump_addresses(rtnl, "     Address: ", ifindex);
-        dump_gateways(rtnl, hwdb, "     Gateway: ", ifindex);
+        dump_addresses(rtnl, "         Address: ", ifindex);
+        dump_gateways(rtnl, hwdb, "         Gateway: ", ifindex);
 
         if (!strv_isempty(dns))
-                dump_list("         DNS: ", dns);
+                dump_list("             DNS: ", dns);
         if (!strv_isempty(domains))
-                dump_list("      Domain: ", domains);
+                dump_list("          Domain: ", domains);
         if (!strv_isempty(ntp))
-                dump_list("         NTP: ", ntp);
+                dump_list("             NTP: ", ntp);
+
+        if (uplinks)
+                dump_carrier_list("Carrier Bound To: ", uplinks, up_count);
+
+        if (downlinks)
+                dump_carrier_list("Carrier Bound By: ", downlinks, down_count);
 
         return 0;
 }
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index eff1ce9..d8ebca3 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -22,6 +22,7 @@
 #include <netinet/ether.h>
 #include <linux/if.h>
 #include <unistd.h>
+#include <fnmatch.h>
 
 #include "util.h"
 #include "virt.h"
@@ -265,6 +266,7 @@ static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) {
 
 static void link_free(Link *link) {
         Address *address;
+        LinkCarrier *carrier;
 
         if (!link)
                 return;
@@ -279,6 +281,12 @@ static void link_free(Link *link) {
                 address_free(address);
         }
 
+        while ((carrier = link->carriers)) {
+                LIST_REMOVE(carriers, link->carriers, carrier);
+                free(carrier->name);
+                free(carrier);
+        }
+
         sd_dhcp_server_unref(link->dhcp_server);
         sd_dhcp_client_unref(link->dhcp_client);
         sd_dhcp_lease_unref(link->dhcp_lease);
@@ -1101,6 +1109,58 @@ static int link_up(Link *link) {
         return 0;
 }
 
+static int link_down_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
+        _cleanup_link_unref_ Link *link = userdata;
+        int r;
+
+        assert(link);
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_rtnl_message_get_errno(m);
+        if (r < 0)
+                log_link_warning_errno(link, -r, "%-*s: could not bring down interface: %m", IFNAMSIZ, link->ifname);
+
+        return 1;
+}
+
+static int link_down(Link *link) {
+        _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+
+        r = sd_rtnl_message_new_link(link->manager->rtnl, &req,
+                                     RTM_SETLINK, link->ifindex);
+        if (r < 0) {
+                log_link_error(link, "Could not allocate RTM_SETLINK message");
+                return r;
+        }
+
+        r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP);
+        if (r < 0) {
+                log_link_error(link, "Could not set link flags: %s",
+                               strerror(-r));
+                return r;
+        }
+
+        r = sd_rtnl_call_async(link->manager->rtnl, req, link_down_handler, link,
+                               0, NULL);
+        if (r < 0) {
+                log_link_error(link,
+                               "Could not send rtnetlink message: %s",
+                               strerror(-r));
+                return r;
+        }
+
+        link_ref(link);
+
+        return 0;
+}
+
 static int link_joined(Link *link) {
         int r;
 
@@ -1367,6 +1427,51 @@ static int link_configure(Link *link) {
         return link_enter_join_netdev(link);
 }
 
+/* split carrier string into pieces. */
+static int link_parse_carriers(const char *carriers, Link *link) {
+         _cleanup_free_ char *list = NULL;
+        char *token = NULL;
+
+        /* use a new list for carriers to not broke the original string. */
+        list = (char *) malloc(strlen(carriers) + 1);
+        if (!list)
+                return -ENOMEM;
+
+        strncpy(list, carriers, strlen(carriers) + 1);
+
+        token = strtok(list, " ");
+
+        while (token) {
+                _cleanup_free_ char *name = NULL;
+                LinkCarrier *carrier;
+
+                /* avoid having the link itself as uplink. */
+                if (0 == fnmatch(token, link->ifname, 0)) {
+                        log_error("Link bound to itself: %s", link->ifname);
+                        return -EINVAL;
+                }
+
+                name = malloc(strlen(token) + 1);
+                if (!name)
+                        return -ENOMEM;
+
+                strncpy(name, token, strlen(token) + 1);
+
+                carrier = new0(LinkCarrier, 1);
+                if (!carrier)
+                        return -ENOMEM;
+
+                carrier->name = name;
+                name = NULL;
+
+                LIST_PREPEND(carriers, link->carriers, carrier);
+
+                token = strtok(NULL, " ");
+        }
+
+        return 0;
+}
+
 static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
                                        void *userdata) {
         _cleanup_link_unref_ Link *link = userdata;
@@ -1405,6 +1510,14 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
         if (r < 0)
                 return r;
 
+        if(network->bind_carrier) {
+                r = link_parse_carriers(network->bind_carrier, link);
+                if (r < 0)
+                        return r;
+
+                link_save(link);
+        }
+
         r = link_configure(link);
         if (r < 0)
                 return r;
@@ -2011,6 +2124,12 @@ int link_save(Link *link) {
 
                 fprintf(f, "LLMNR=%s\n",
                         llmnr_support_to_string(link->network->llmnr));
+
+                if (link->network->bind_carrier) {
+                        fputs("CARRIERS=", f);
+                        fputs(link->network->bind_carrier, f);
+                        fputs("\n", f);
+                }
         }
 
         if (link->dhcp_lease) {
@@ -2056,6 +2175,65 @@ fail:
         return r;
 }
 
+int link_handle_carriers(Link *link) {
+        Manager *m;
+        LinkCarrier *carrier;
+        Link *downlink;
+        Link *uplink;
+        Iterator i;
+        Iterator j;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        m = link->manager;
+
+        /* go through the list of available links. */
+        HASHMAP_FOREACH (downlink, m->links, i) {
+                if (downlink->carriers) {
+                        bool has_carrier;
+                        bool required_up = false;
+                        bool found_match = false;
+
+                        has_carrier = link_has_carrier(downlink);
+
+                        LIST_FOREACH (carriers, carrier, downlink->carriers) {
+                                HASHMAP_FOREACH (uplink, m->links, j) {
+                                        if (0 == fnmatch(carrier->name, uplink->ifname, 0)) {
+                                                found_match = true;
+
+                                                if (link_has_carrier(uplink)) {
+                                                        required_up = true;
+                                                        break;
+                                                }
+                                        }
+                                }
+
+                                if (required_up)
+                                        break;
+                        }
+
+                        if (found_match) {
+                                if (has_carrier && !required_up) {
+                                        r = link_down(downlink);
+                                        if (r < 0)
+                                                return r;
+                                }
+                                else if (!has_carrier && required_up) {
+                                        if (!(downlink->flags & IFF_UP)) {
+                                                r = link_up(downlink);
+                                                if (r < 0)
+                                                        return r;
+                                        }
+                                }
+                        }
+                }
+        }
+
+        return 0;
+}
+
 static const char* const link_state_table[_LINK_STATE_MAX] = {
         [LINK_STATE_PENDING] = "pending",
         [LINK_STATE_ENSLAVING] = "configuring",
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index ce83f24..1e2507b 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -36,6 +36,13 @@ typedef enum LinkState {
         _LINK_STATE_INVALID = -1
 } LinkState;
 
+typedef struct LinkCarrier LinkCarrier;
+
+struct LinkCarrier {
+        char *name;
+        LIST_FIELDS(LinkCarrier, carriers);
+};
+
 struct Link {
         Manager *manager;
 
@@ -56,6 +63,8 @@ struct Link {
         LinkState state;
         LinkOperationalState operstate;
 
+        LIST_HEAD(LinkCarrier, carriers);
+
         unsigned link_messages;
         unsigned enslaving;
 
@@ -106,6 +115,7 @@ int link_save(Link *link);
 
 int link_carrier_reset(Link *link);
 bool link_has_carrier(Link *link);
+int link_handle_carriers(Link *link);
 
 int link_set_mtu(Link *link, uint32_t mtu);
 int link_set_hostname(Link *link, const char *hostname);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 8dfe437..0924332 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -348,6 +348,13 @@ static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, vo
                 assert_not_reached("Received invalid RTNL message type.");
         }
 
+        link = hashmap_first(m->links);
+        if (link) {
+                r = link_handle_carriers(link);
+                if (r < 0)
+                        return 0;
+        }
+
         return 1;
 }
 
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 26fce97..301fdae 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -47,6 +47,7 @@ Network.LLMNR,               config_parse_llmnr,                 0,
 Network.NTP,                 config_parse_strv,                  0,                             offsetof(Network, ntp)
 Network.IPForward,           config_parse_address_family_boolean,0,                             offsetof(Network, ip_forward)
 Network.IPMasquerade,        config_parse_bool,                  0,                             offsetof(Network, ip_masquerade)
+Network.BindCarrier,         config_parse_string,                0,                             offsetof(Network, bind_carrier)
 Address.Address,             config_parse_address,               0,                             0
 Address.Peer,                config_parse_address,               0,                             0
 Address.Broadcast,           config_parse_broadcast,             0,                             0
diff --git a/src/network/networkd.h b/src/network/networkd.h
index cd54e9b..f4e5efd 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -140,6 +140,8 @@ struct Network {
 
         bool lldp;
 
+        char *bind_carrier;
+
         LIST_HEAD(Address, static_addresses);
         LIST_HEAD(Route, static_routes);
         LIST_HEAD(FdbEntry, static_fdb_entries);
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 027730d..0a7adf2 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -116,6 +116,9 @@ int sd_network_link_get_lldp(int ifindex, char **lldp);
 /* Get the DNS domain names for a given link. */
 int sd_network_link_get_domains(int ifindex, char ***domains);
 
+/* Get the CARRIERS to which current link is bound to. */
+int sd_network_link_get_carriers(int ifindex, char ***carriers);
+
 /* Returns whether or not domains that don't match any link should be resolved
  * on this link. 1 for yes, 0 for no and negative value for error */
 int sd_network_link_get_wildcard_domain(int ifindex);
-- 
1.9.3



More information about the systemd-devel mailing list