chiark / gitweb /
networkd: add support for Uplink Failure Detection
authorAlin Rauta <alin.rauta@intel.com>
Tue, 17 Feb 2015 12:06:57 +0000 (04:06 -0800)
committerTom Gundersen <teg@jklm.no>
Fri, 27 Feb 2015 18:58:30 +0000 (13:58 -0500)
Introduce BindCarrier= to indicate the set of links that determine if
the current link should be brought UP or DOWN.

[tomegun: add a bit to commit message]

man/systemd.network.xml
src/libsystemd/sd-network/sd-network.c
src/network/networkctl.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd.h
src/systemd/sd-network.h

index 485876b..60252e5 100644 (file)
           </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 in an operational down state, the current interface is brought
+            down. 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,
index 297fdac..db1f699 100644 (file)
@@ -261,6 +261,14 @@ _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_carrier_bound_to(int ifindex, char ***ret) {
+        return network_get_link_strv("CARRIER_BOUND_TO", ifindex, ret);
+}
+
+_public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) {
+        return network_get_link_strv("CARRIER_BOUND_BY", ifindex, ret);
+}
+
 _public_ int sd_network_link_get_wildcard_domain(int ifindex) {
         int r;
         _cleanup_free_ char *p = NULL, *s = NULL;
index aa83f32..0637513 100644 (file)
@@ -508,6 +508,8 @@ 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_strv_free_ char **carrier_bound_to = NULL;
+        _cleanup_strv_free_ char **carrier_bound_by = NULL;
         struct ether_addr e;
         unsigned iftype;
         int r, ifindex;
@@ -606,12 +608,15 @@ static int link_status_one(
 
         sd_network_link_get_network_file(ifindex, &network);
 
+        sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
+        sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
+
         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 +624,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 +639,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 (!strv_isempty(carrier_bound_to))
+                dump_list("Carrier Bound To: ", carrier_bound_to);
+
+        if (!strv_isempty(carrier_bound_by))
+                dump_list("Carrier Bound By: ", carrier_bound_by);
 
         return 0;
 }
index 0665e1f..842ca1c 100644 (file)
@@ -272,6 +272,8 @@ static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) {
 
 static void link_free(Link *link) {
         Address *address;
+        Iterator i;
+        Link *carrier;
 
         if (!link)
                 return;
@@ -309,6 +311,14 @@ static void link_free(Link *link) {
 
         udev_device_unref(link->udev_device);
 
+        HASHMAP_FOREACH (carrier, link->bound_to_links, i)
+                hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex));
+        hashmap_free(link->bound_to_links);
+
+        HASHMAP_FOREACH (carrier, link->bound_by_links, i)
+                hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex));
+        hashmap_free(link->bound_by_links);
+
         free(link);
 }
 
@@ -355,19 +365,6 @@ static void link_set_state(Link *link, LinkState state) {
         return;
 }
 
-void link_drop(Link *link) {
-        if (!link || link->state == LINK_STATE_LINGER)
-                return;
-
-        link_set_state(link, LINK_STATE_LINGER);
-
-        log_link_debug(link, "link removed");
-
-        link_unref(link);
-
-        return;
-}
-
 static void link_enter_unmanaged(Link *link) {
         assert(link);
 
@@ -1148,13 +1145,319 @@ 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);
+
+        log_link_debug(link, "bringing link down");
+
+        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_handle_bound_to_list(Link *link) {
+        Link *l;
+        Iterator i;
+        int r;
+        bool required_up = false;
+        bool link_is_up = false;
+
+        assert(link);
+
+        if (hashmap_isempty(link->bound_to_links))
+                return 0;
+
+        if (link->flags & IFF_UP)
+                link_is_up = true;
+
+        HASHMAP_FOREACH (l, link->bound_to_links, i)
+                if (link_has_carrier(l)) {
+                        required_up = true;
+                        break;
+                }
+
+        if (!required_up && link_is_up) {
+                r = link_down(link);
+                if (r < 0)
+                        return r;
+        } else if (required_up && !link_is_up) {
+                r = link_up(link);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int link_handle_bound_by_list(Link *link) {
+        Iterator i;
+        Link *l;
+        int r;
+
+        assert(link);
+
+        if (hashmap_isempty(link->bound_by_links))
+                return 0;
+
+        HASHMAP_FOREACH (l, link->bound_by_links, i) {
+                r = link_handle_bound_to_list(l);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int link_put_carrier(Link *link, Link *carrier, Hashmap **h) {
+        int r;
+
+        assert(link);
+        assert(carrier);
+
+        if (link == carrier)
+                return 0;
+
+        if (hashmap_get(*h, INT_TO_PTR(carrier->ifindex)))
+                return 0;
+
+        r = hashmap_ensure_allocated(h, NULL);
+        if (r < 0)
+                return r;
+
+        r = hashmap_put(*h, INT_TO_PTR(carrier->ifindex), carrier);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int link_new_bound_by_list(Link *link) {
+        Manager *m;
+        Link *carrier;
+        Iterator i;
+        int r;
+        bool list_updated = false;
+
+        assert(link);
+        assert(link->manager);
+
+        m = link->manager;
+
+        HASHMAP_FOREACH (carrier, m->links, i) {
+                if (!carrier->network)
+                        continue;
+
+                if (strv_isempty(carrier->network->bind_carrier))
+                        continue;
+
+                if (strv_fnmatch(carrier->network->bind_carrier, link->ifname, 0)) {
+                        r = link_put_carrier(link, carrier, &link->bound_by_links);
+                        if (r < 0)
+                                return r;
+
+                        list_updated = true;
+                }
+        }
+
+        if (list_updated)
+                link_save(link);
+
+        HASHMAP_FOREACH (carrier, link->bound_by_links, i) {
+                r = link_put_carrier(carrier, link, &carrier->bound_to_links);
+                if (r < 0)
+                        return r;
+
+                link_save(carrier);
+        }
+
+        return 0;
+}
+
+static int link_new_bound_to_list(Link *link) {
+        Manager *m;
+        Link *carrier;
+        Iterator i;
+        int r;
+        bool list_updated = false;
+
+        assert(link);
+        assert(link->manager);
+
+        if (!link->network)
+                return 0;
+
+        if (strv_isempty(link->network->bind_carrier))
+                return 0;
+
+        m = link->manager;
+
+        HASHMAP_FOREACH (carrier, m->links, i) {
+                if (strv_fnmatch(link->network->bind_carrier, carrier->ifname, 0)) {
+                        r = link_put_carrier(link, carrier, &link->bound_to_links);
+                        if (r < 0)
+                                return r;
+
+                        list_updated = true;
+                }
+        }
+
+        if (list_updated)
+                link_save(link);
+
+        HASHMAP_FOREACH (carrier, link->bound_to_links, i) {
+                r = link_put_carrier(carrier, link, &carrier->bound_by_links);
+                if (r < 0)
+                        return r;
+
+                link_save(carrier);
+        }
+
+        return 0;
+}
+
+static int link_new_carrier_maps(Link *link) {
+        int r;
+
+        r = link_new_bound_by_list(link);
+        if (r < 0)
+                return r;
+
+        r = link_handle_bound_by_list(link);
+        if (r < 0)
+                return r;
+
+        r = link_new_bound_to_list(link);
+        if (r < 0)
+                return r;
+
+        r = link_handle_bound_to_list(link);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static void link_free_bound_to_list(Link *link) {
+        Link *bound_to;
+        Iterator i;
+
+        HASHMAP_FOREACH (bound_to, link->bound_to_links, i) {
+                hashmap_remove(link->bound_to_links, INT_TO_PTR(bound_to->ifindex));
+
+                if (hashmap_remove(bound_to->bound_by_links, INT_TO_PTR(link->ifindex)))
+                        link_save(bound_to);
+        }
+
+        return;
+}
+
+static void link_free_bound_by_list(Link *link) {
+        Link *bound_by;
+        Iterator i;
+
+        HASHMAP_FOREACH (bound_by, link->bound_by_links, i) {
+                hashmap_remove(link->bound_by_links, INT_TO_PTR(bound_by->ifindex));
+
+                if (hashmap_remove(bound_by->bound_to_links, INT_TO_PTR(link->ifindex))) {
+                        link_save(bound_by);
+                        link_handle_bound_to_list(bound_by);
+                }
+        }
+
+        return;
+}
+
+static void link_free_carrier_maps(Link *link) {
+        bool list_updated = false;
+
+        assert(link);
+
+        if (!hashmap_isempty(link->bound_to_links)) {
+                link_free_bound_to_list(link);
+                list_updated = true;
+        }
+
+        if (!hashmap_isempty(link->bound_by_links)) {
+                link_free_bound_by_list(link);
+                list_updated = true;
+        }
+
+        if (list_updated)
+                link_save(link);
+
+        return;
+}
+
+void link_drop(Link *link) {
+        if (!link || link->state == LINK_STATE_LINGER)
+                return;
+
+        link_set_state(link, LINK_STATE_LINGER);
+
+        link_free_carrier_maps(link);
+
+        log_link_debug(link, "link removed");
+
+        link_unref(link);
+
+        return;
+}
+
 static int link_joined(Link *link) {
         int r;
 
         assert(link);
         assert(link->network);
 
-        if (!(link->flags & IFF_UP)) {
+        if (!hashmap_isempty(link->bound_to_links)) {
+                r = link_handle_bound_to_list(link);
+                if (r < 0)
+                        return r;
+        } else if (!(link->flags & IFF_UP)) {
                 r = link_up(link);
                 if (r < 0) {
                         link_enter_failed(link);
@@ -1429,6 +1732,14 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
 
         log_link_debug(link, "link state is up-to-date");
 
+        r = link_new_bound_by_list(link);
+        if (r < 0)
+                return r;
+
+        r = link_handle_bound_by_list(link);
+        if (r < 0)
+                return r;
+
         r = network_get(link->manager, link->udev_device, link->ifname,
                         &link->mac, &network);
         if (r == -ENOENT) {
@@ -1452,6 +1763,10 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
         if (r < 0)
                 return r;
 
+        r = link_new_bound_to_list(link);
+        if (r < 0)
+                return r;
+
         r = link_configure(link);
         if (r < 0)
                 return r;
@@ -1729,6 +2044,10 @@ static int link_carrier_gained(Link *link) {
                 }
         }
 
+        r = link_handle_bound_by_list(link);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
@@ -1743,6 +2062,10 @@ static int link_carrier_lost(Link *link) {
                 return r;
         }
 
+        r = link_handle_bound_by_list(link);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
@@ -1782,16 +2105,26 @@ int link_update(Link *link, sd_rtnl_message *m) {
                 link_ref(link);
                 log_link_info(link, "link readded");
                 link_set_state(link, LINK_STATE_ENSLAVING);
+
+                r = link_new_carrier_maps(link);
+                if (r < 0)
+                        return r;
         }
 
         r = sd_rtnl_message_read_string(m, IFLA_IFNAME, &ifname);
         if (r >= 0 && !streq(ifname, link->ifname)) {
                 log_link_info(link, "renamed to %s", ifname);
 
+                link_free_carrier_maps(link);
+
                 free(link->ifname);
                 link->ifname = strdup(ifname);
                 if (!link->ifname)
                         return -ENOMEM;
+
+                r = link_new_carrier_maps(link);
+                if (r < 0)
+                        return r;
         }
 
         r = sd_rtnl_message_read_u32(m, IFLA_MTU, &mtu);
@@ -2060,6 +2393,39 @@ int link_save(Link *link) {
                         llmnr_support_to_string(link->network->llmnr));
         }
 
+        if (!hashmap_isempty(link->bound_to_links)) {
+                Link *carrier;
+                Iterator i;
+                bool space = false;
+
+                fputs("CARRIER_BOUND_TO=", f);
+                HASHMAP_FOREACH(carrier, link->bound_to_links, i) {
+                        if (space)
+                                fputc(' ', f);
+                        fputs(carrier->ifname, f);
+                        space = true;
+                }
+
+                fputs("\n", f);
+        }
+
+        if (!hashmap_isempty(link->bound_by_links)) {
+                Link *carrier;
+                Iterator i;
+                bool space = false;
+
+                fputs("CARRIER_BOUND_BY=", f);
+                space = false;
+                HASHMAP_FOREACH(carrier, link->bound_by_links, i) {
+                        if (space)
+                                fputc(' ', f);
+                        fputs(carrier->ifname, f);
+                        space = true;
+                }
+
+                fputs("\n", f);
+        }
+
         if (link->dhcp_lease) {
                 assert(link->network);
 
index cec158e..479098c 100644 (file)
@@ -85,6 +85,9 @@ struct Link {
 
         sd_lldp *lldp;
         char *lldp_file;
+
+        Hashmap *bound_by_links;
+        Hashmap *bound_to_links;
 };
 
 Link *link_unref(Link *link);
index fc277df..b0c23a7 100644 (file)
@@ -48,6 +48,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_strv,                  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
index c6cf00a..0ba0c75 100644 (file)
@@ -208,6 +208,7 @@ void network_free(Network *network) {
         strv_free(network->ntp);
         strv_free(network->dns);
         strv_free(network->domains);
+        strv_free(network->bind_carrier);
 
         netdev_unref(network->bridge);
 
index bdb2f20..e75746f 100644 (file)
@@ -151,7 +151,7 @@ struct Network {
         Hashmap *fdb_entries_by_section;
 
         bool wildcard_domain;
-        char **domains, **dns, **ntp;
+        char **domains, **dns, **ntp, **bind_carrier;
 
         LLMNRSupport llmnr;
 
index 027730d..4d96c86 100644 (file)
@@ -116,6 +116,12 @@ 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_carrier_bound_to(int ifindex, char ***carriers);
+
+/* Get the CARRIERS that are bound to current link. */
+int sd_network_link_get_carrier_bound_by(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);