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 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel