[systemd-devel] [PATCH v4] Added support for Uplink Failure Detection using BindCarrier
Rauta, Alin
alin.rauta at intel.com
Fri Feb 20 02:36:51 PST 2015
Hi Tom,
Thank you. Please let me know if you have any questions regarding the implementation.
We can have another patch for the DOWN logic. It's a little bit complicated since we don't have UP either and since during UP operation we don't know the previous port state so that we get back to it.
Best Regards,
Alin
-----Original Message-----
From: Tom Gundersen [mailto:teg at jklm.no]
Sent: Friday, February 20, 2015 10:27 AM
To: Rauta, Alin
Cc: lennart at poettering.net; zbyszek at in.waw.pl; systemd-devel at lists.freedesktop.org; Kinsella, Ray
Subject: Re: [PATCH v4] Added support for Uplink Failure Detection using BindCarrier
On Fri, Feb 20, 2015 at 10:50 AM, Rauta, Alin <alin.rauta at intel.com> wrote:
> Hi Tom, Lennart, Zbyszek,
> Did you have any chance to look at this patch version ?
I hope to review it this weekend. I might go ahead and implement the DOWN logic independently if that is still an issue (saw your question, but didn't yet look at how you dealt with it in the patch). We need that anyway for manually UP/DOWN of networks.
> -----Original Message-----
> From: Rauta, Alin
> Sent: Tuesday, February 17, 2015 12:07 PM
> To: teg at jklm.no; lennart at poettering.net; zbyszek at in.waw.pl
> Cc: systemd-devel at lists.freedesktop.org; Kinsella, Ray; Rauta, Alin
> Subject: [PATCH v4] Added support for Uplink Failure Detection using
> BindCarrier
>
> ---
> man/systemd.network.xml | 11 +
> src/libsystemd/sd-network/sd-network.c | 8 +
> src/network/networkctl.c | 43 ++--
> src/network/networkd-link.c | 394 +++++++++++++++++++++++++++++--
> src/network/networkd-link.h | 3 +
> src/network/networkd-network-gperf.gperf | 1 +
> src/network/networkd-network.c | 1 +
> src/network/networkd.h | 2 +-
> src/systemd/sd-network.h | 6 +
> 9 files changed, 438 insertions(+), 31 deletions(-)
>
> diff --git a/man/systemd.network.xml b/man/systemd.network.xml index
> 485876b..60252e5 100644
> --- a/man/systemd.network.xml
> +++ b/man/systemd.network.xml
> @@ -280,6 +280,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 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, diff --git a/src/libsystemd/sd-network/sd-network.c
> b/src/libsystemd/sd-network/sd-network.c
> index c4713fe..fb5152c 100644
> --- a/src/libsystemd/sd-network/sd-network.c
> +++ b/src/libsystemd/sd-network/sd-network.c
> @@ -264,6 +264,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; diff --git
> a/src/network/networkctl.c b/src/network/networkctl.c index
> aa83f32..0637513 100644
> --- a/src/network/networkctl.c
> +++ b/src/network/networkctl.c
> @@ -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;
> }
> diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
> index f716e82..d718037 100644
> --- a/src/network/networkd-link.c
> +++ b/src/network/networkd-link.c
> @@ -275,6 +275,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;
> @@ -312,6 +314,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);
> }
>
> @@ -358,19 +368,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);
>
> @@ -1151,13 +1148,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); @@ -1432,6 +1735,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) {
> @@ -1455,6 +1766,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;
> @@ -1732,6 +2047,10 @@ static int link_carrier_gained(Link *link) {
> }
> }
>
> + r = link_handle_bound_by_list(link);
> + if (r < 0)
> + return r;
> +
> return 0;
> }
>
> @@ -1746,6 +2065,10 @@ static int link_carrier_lost(Link *link) {
> return r;
> }
>
> + r = link_handle_bound_by_list(link);
> + if (r < 0)
> + return r;
> +
> return 0;
> }
>
> @@ -1785,16 +2108,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); @@ -2063,6 +2396,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);
>
> diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
> index cec158e..479098c 100644
> --- a/src/network/networkd-link.h
> +++ b/src/network/networkd-link.h
> @@ -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);
> diff --git a/src/network/networkd-network-gperf.gperf
> b/src/network/networkd-network-gperf.gperf
> index fc277df..b0c23a7 100644
> --- a/src/network/networkd-network-gperf.gperf
> +++ b/src/network/networkd-network-gperf.gperf
> @@ -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
> diff --git a/src/network/networkd-network.c
> b/src/network/networkd-network.c index 35ac064..3be993c 100644
> --- a/src/network/networkd-network.c
> +++ b/src/network/networkd-network.c
> @@ -209,6 +209,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);
>
> diff --git a/src/network/networkd.h b/src/network/networkd.h index
> bdb2f20..e75746f 100644
> --- a/src/network/networkd.h
> +++ b/src/network/networkd.h
> @@ -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;
>
> diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index
> 027730d..4d96c86 100644
> --- a/src/systemd/sd-network.h
> +++ b/src/systemd/sd-network.h
> @@ -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);
> --
> 1.9.3
>
More information about the systemd-devel
mailing list