[systemd-devel] [PATCH v3] Added support for Uplink Failure Detection using BindCarrier
Alin Rauta
alin.rauta at intel.com
Tue Feb 10 03:30:42 PST 2015
---
man/systemd.network.xml | 11 ++
src/libsystemd/sd-network/sd-network.c | 4 +
src/network/networkctl.c | 211 ++++++++++++++++++++++++++++---
src/network/networkd-link.c | 123 +++++++++++++++++-
src/network/networkd-link.h | 1 +
src/network/networkd-manager.c | 7 +
src/network/networkd-network-gperf.gperf | 1 +
src/network/networkd-network.c | 10 ++
src/network/networkd.h | 2 +-
src/systemd/sd-network.h | 3 +
10 files changed, 355 insertions(+), 18 deletions(-)
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 9b3a92d..8b2ca4e 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -270,6 +270,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..ffb38e8 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,161 @@ static int get_gateway_description(
return -ENODATA;
}
+static bool is_carrier(const char *ifname,
+ char **carriers) {
+ char **i;
+
+ STRV_FOREACH(i, carriers)
+ if (fnmatch(*i, ifname, 0) == 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 (r == -ENODATA || strv_isempty(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 (r == -ENODATA || strv_isempty(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 +664,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 +767,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 +792,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 +807,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 0b1cac1..ccd255a 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"
@@ -1151,6 +1152,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;
@@ -1982,7 +2035,7 @@ int link_save(Link *link) {
admin_state, oper_state);
if (link->network) {
- char **address, **domain;
+ char **address, **domain, **carrier;
bool space;
fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
@@ -2061,6 +2114,15 @@ int link_save(Link *link) {
fprintf(f, "LLMNR=%s\n",
llmnr_support_to_string(link->network->llmnr));
+
+ fputs("CARRIERS=", f);
+ space = false;
+ STRV_FOREACH(carrier, link->network->carriers) {
+ if (space)
+ fputc(' ', f);
+ fputs(*carrier, f);
+ space = true;
+ }
}
if (link->dhcp_lease) {
@@ -2106,6 +2168,65 @@ fail:
return r;
}
+int link_handle_carriers(Link *link) {
+ Manager *m;
+ char **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->network && !strv_isempty(downlink->network->carriers)) {
+ bool has_carrier;
+ bool required_up = false;
+ bool found_match = false;
+
+ has_carrier = link_has_carrier(downlink);
+
+ STRV_FOREACH (carrier, downlink->network->carriers) {
+ HASHMAP_FOREACH (uplink, m->links, j) {
+ if (fnmatch(*carrier, uplink->ifname, 0) == 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 449dbc8..d94e9cd 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -106,6 +106,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 4617f11..06807df 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -356,6 +356,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 1731e04..e191380 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, carriers)
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 3ebd4d7..9c4425e 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -21,6 +21,7 @@
#include <ctype.h>
#include <net/if.h>
+#include <fnmatch.h>
#include "path-util.h"
#include "conf-files.h"
@@ -37,6 +38,7 @@ static int network_load_one(Manager *manager, const char *filename) {
char *d;
Route *route;
Address *address;
+ char **carrier;
int r;
assert(manager);
@@ -154,6 +156,13 @@ static int network_load_one(Manager *manager, const char *filename) {
}
}
+ if (!strv_isempty(network->carriers))
+ STRV_FOREACH(carrier, network->carriers)
+ if (fnmatch(*carrier, network->match_name, 0) == 0) {
+ log_error("Link bound to itself: %s", network->match_name);
+ return -EINVAL;
+ }
+
network = NULL;
return 0;
@@ -209,6 +218,7 @@ void network_free(Network *network) {
strv_free(network->ntp);
strv_free(network->dns);
strv_free(network->domains);
+ strv_free(network->carriers);
netdev_unref(network->bridge);
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 22cc51d..04596a8 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, **carriers;
LLMNRSupport llmnr;
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