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;
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),
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;
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;
}
static void link_free(Link *link) {
Address *address;
+ Iterator i;
+ Link *carrier;
if (!link)
return;
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);
}
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);
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);
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) {
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;
}
}
+ r = link_handle_bound_by_list(link);
+ if (r < 0)
+ return r;
+
return 0;
}
return r;
}
+ r = link_handle_bound_by_list(link);
+ if (r < 0)
+ return r;
+
return 0;
}
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);
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);