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

Zbigniew Jędrzejewski-Szmek zbyszek at in.waw.pl
Mon Feb 9 08:51:07 PST 2015


On Mon, Feb 09, 2015 at 03:06:46AM -0800, Alin Rauta wrote:
> ---
>  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))
Please don't invert the order.

> +                       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))
Please don't add extra parentheses, and don't invert the order.

I think an empty list should be treated as unset. So use strv_isempty
instead of comparing to NULL directly.

> +                        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;
Here too.

> +
> +        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);
> +        }
No need for braces.

> +}
> +
>  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)) {
Here too.

> +                        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);
strdup

> +
> +                carrier = new0(LinkCarrier, 1);
> +                if (!carrier)
> +                        return -ENOMEM;
> +
> +                carrier->name = name;
> +                name = NULL;
It was just zeroed out above. No need to set stuff again.

> +
> +                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);
fprintf

> +                }
>          }
>  
>          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);
> +};
Maybe I'm missing something here, but couldn't this be just a plain strv?

Zbyszek


More information about the systemd-devel mailing list