networkd: add support for Uplink Failure Detection

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]
This commit is contained in:
Alin Rauta 2015-02-17 04:06:57 -08:00 committed by Tom Gundersen
parent 2bdbf32183
commit 0d4ad91dd4
9 changed files with 438 additions and 31 deletions

View File

@ -279,6 +279,17 @@
</para>
</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>

View 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;

View 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;
}

View 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);

View 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);

View 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

View 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);

View 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;

View 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);