diff options
Diffstat (limited to 'src/network')
49 files changed, 2208 insertions, 835 deletions
diff --git a/src/network/.gitignore b/src/network/.gitignore index 8858596489..aca55206b7 100644 --- a/src/network/.gitignore +++ b/src/network/.gitignore @@ -1,2 +1,3 @@ /networkd-network-gperf.c /networkd-netdev-gperf.c +/networkd-gperf.c diff --git a/src/network/Makefile b/src/network/Makefile index 3453a9c56d..d43dfdfca8 100644 --- a/src/network/Makefile +++ b/src/network/Makefile @@ -47,6 +47,8 @@ libnetworkd_core_la_CFLAGS = \ libnetworkd_core_la_SOURCES = \ src/libsystemd-network/network-internal.h \ src/network/networkd.h \ + src/network/networkd-conf.h \ + src/network/networkd-conf.c \ src/network/networkd-link.h \ src/network/networkd-link.c \ src/network/networkd-netdev.h \ @@ -90,9 +92,12 @@ libnetworkd_core_la_SOURCES = \ src/network/networkd-address-pool.h \ src/network/networkd-address-pool.c \ src/network/networkd-util.h \ - src/network/networkd-util.c + src/network/networkd-util.c \ + src/network/networkd-lldp-tx.h \ + src/network/networkd-lldp-tx.c nodist_libnetworkd_core_la_SOURCES = \ + src/network/networkd-gperf.c \ src/network/networkd-network-gperf.c \ src/network/networkd-netdev-gperf.c @@ -131,6 +136,12 @@ networkctl_LDADD = \ dist_bashcompletion_data += \ shell-completion/bash/networkctl +test_networkd_conf_SOURCES = \ + src/network/test-networkd-conf.c + +test_networkd_conf_LDADD = \ + libnetworkd-core.la + test_network_SOURCES = \ src/network/test-network.c @@ -156,6 +167,7 @@ test_network_tables_LDADD += \ endif # HAVE_LIBIPTC tests += \ + test-networkd-conf \ test-network \ test-network-tables @@ -189,6 +201,7 @@ BUSNAMES_TARGET_WANTS += \ endif # ENABLE_NETWORKD gperf_gperf_sources += \ + src/network/networkd-gperf.gperf \ src/network/networkd-network-gperf.gperf \ src/network/networkd-netdev-gperf.gperf diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 6e436d2043..85635b59bc 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -23,6 +23,7 @@ #include <systemd/sd-device.h> #include <systemd/sd-hwdb.h> +#include <systemd/sd-lldp.h> #include <systemd/sd-netlink.h> #include <systemd/sd-network.h> @@ -30,18 +31,20 @@ #include "arphrd-list.h" #include "device-util.h" #include "ether-addr-util.h" +#include "fd-util.h" #include "hwdb-util.h" -#include "lldp.h" #include "local-addresses.h" #include "locale-util.h" #include "netlink-util.h" #include "pager.h" #include "parse-util.h" #include "socket-util.h" +#include "sparse-endian.h" #include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "strxcpyx.h" #include "terminal-util.h" #include "util.h" #include "verbs.h" @@ -50,15 +53,7 @@ static bool arg_no_pager = false; static bool arg_legend = true; static bool arg_all = false; -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - -static int link_get_type_string(int iftype, sd_device *d, char **ret) { +static int link_get_type_string(unsigned short iftype, sd_device *d, char **ret) { const char *t; char *p; @@ -70,7 +65,7 @@ static int link_get_type_string(int iftype, sd_device *d, char **ret) { * to show a more useful type string for * them */ - (void)sd_device_get_devtype(d, &devtype); + (void) sd_device_get_devtype(d, &devtype); if (streq_ptr(devtype, "wlan")) id = "wlan"; @@ -103,10 +98,46 @@ static int link_get_type_string(int iftype, sd_device *d, char **ret) { return 0; } +static void operational_state_to_color(const char *state, const char **on, const char **off) { + assert(on); + assert(off); + + if (streq_ptr(state, "routable")) { + *on = ansi_highlight_green(); + *off = ansi_normal(); + } else if (streq_ptr(state, "degraded")) { + *on = ansi_highlight_yellow(); + *off = ansi_normal(); + } else + *on = *off = ""; +} + +static void setup_state_to_color(const char *state, const char **on, const char **off) { + assert(on); + assert(off); + + if (streq_ptr(state, "configured")) { + *on = ansi_highlight_green(); + *off = ansi_normal(); + } else if (streq_ptr(state, "configuring")) { + *on = ansi_highlight_yellow(); + *off = ansi_normal(); + } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) { + *on = ansi_highlight_red(); + *off = ansi_normal(); + } else + *on = *off = ""; +} + typedef struct LinkInfo { - const char *name; + char name[IFNAMSIZ+1]; int ifindex; - unsigned iftype; + unsigned short iftype; + struct ether_addr mac_address; + uint32_t mtu; + + bool has_mac_address:1; + bool has_mtu:1; } LinkInfo; static int link_info_compare(const void *a, const void *b) { @@ -115,44 +146,84 @@ static int link_info_compare(const void *a, const void *b) { return x->ifindex - y->ifindex; } -static int decode_and_sort_links(sd_netlink_message *m, LinkInfo **ret) { +static int decode_link(sd_netlink_message *m, LinkInfo *info) { + const char *name; + uint16_t type; + int r; + + assert(m); + assert(info); + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return r; + + if (type != RTM_NEWLINK) + return 0; + + r = sd_rtnl_message_link_get_ifindex(m, &info->ifindex); + if (r < 0) + return r; + + r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name); + if (r < 0) + return r; + + r = sd_rtnl_message_link_get_type(m, &info->iftype); + if (r < 0) + return r; + + strscpy(info->name, sizeof info->name, name); + + info->has_mac_address = + sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 && + memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0; + + info->has_mtu = + sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) && + info->mtu > 0; + + return 1; +} + +static int acquire_link_info_strv(sd_netlink *rtnl, char **l, LinkInfo **ret) { _cleanup_free_ LinkInfo *links = NULL; - size_t size = 0, c = 0; - sd_netlink_message *i; + char **i; + size_t c = 0; int r; - for (i = m; i; i = sd_netlink_message_next(i)) { - const char *name; - unsigned iftype; - uint16_t type; - int ifindex; + assert(rtnl); + assert(ret); - r = sd_netlink_message_get_type(i, &type); - if (r < 0) - return r; + links = new(LinkInfo, strv_length(l)); + if (!links) + return log_oom(); - if (type != RTM_NEWLINK) - continue; + STRV_FOREACH(i, l) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + int ifindex; + + if (parse_ifindex(*i, &ifindex) >= 0) + r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex); + else { + r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); + if (r < 0) + return rtnl_log_create_error(r); - r = sd_rtnl_message_link_get_ifindex(i, &ifindex); + r = sd_netlink_message_append_string(req, IFLA_IFNAME, *i); + } if (r < 0) - return r; + return rtnl_log_create_error(r); - r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name); + r = sd_netlink_call(rtnl, req, 0, &reply); if (r < 0) - return r; + return log_error_errno(r, "Failed to request link: %m"); - r = sd_rtnl_message_link_get_type(i, &iftype); + r = decode_link(reply, links + c); if (r < 0) return r; - - if (!GREEDY_REALLOC(links, size, c+1)) - return -ENOMEM; - - links[c].name = name; - links[c].ifindex = ifindex; - links[c].iftype = iftype; - c++; + if (r > 0) + c++; } qsort_safe(links, c, sizeof(LinkInfo), link_info_compare); @@ -163,48 +234,15 @@ static int decode_and_sort_links(sd_netlink_message *m, LinkInfo **ret) { return (int) c; } -static void operational_state_to_color(const char *state, const char **on, const char **off) { - assert(on); - assert(off); - - if (streq_ptr(state, "routable")) { - *on = ansi_highlight_green(); - *off = ansi_normal(); - } else if (streq_ptr(state, "degraded")) { - *on = ansi_highlight_yellow(); - *off = ansi_normal(); - } else - *on = *off = ""; -} - -static void setup_state_to_color(const char *state, const char **on, const char **off) { - assert(on); - assert(off); - - if (streq_ptr(state, "configured")) { - *on = ansi_highlight_green(); - *off = ansi_normal(); - } else if (streq_ptr(state, "configuring")) { - *on = ansi_highlight_yellow(); - *off = ansi_normal(); - } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) { - *on = ansi_highlight_red(); - *off = ansi_normal(); - } else - *on = *off = ""; -} - -static int list_links(int argc, char *argv[], void *userdata) { +static int acquire_link_info_all(sd_netlink *rtnl, LinkInfo **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_free_ LinkInfo *links = NULL; - int r, c, i; - - pager_open_if_enabled(); + size_t allocated = 0, c = 0; + sd_netlink_message *i; + int r; - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); + assert(rtnl); + assert(ret); r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); if (r < 0) @@ -218,12 +256,50 @@ static int list_links(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to enumerate links: %m"); - if (arg_legend) - printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP"); + for (i = reply; i; i = sd_netlink_message_next(i)) { + if (!GREEDY_REALLOC(links, allocated, c+1)) + return -ENOMEM; + + r = decode_link(i, links + c); + if (r < 0) + return r; + if (r > 0) + c++; + } - c = decode_and_sort_links(reply, &links); + qsort_safe(links, c, sizeof(LinkInfo), link_info_compare); + + *ret = links; + links = NULL; + + return (int) c; +} + +static int list_links(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_free_ LinkInfo *links = NULL; + int c, i, r; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + if (argc > 1) + c = acquire_link_info_strv(rtnl, argv + 1, &links); + else + c = acquire_link_info_all(rtnl, &links); if (c < 0) - return rtnl_log_parse_error(c); + return c; + + pager_open(arg_no_pager, false); + + if (arg_legend) + printf("%3s %-16s %-18s %-11s %-10s\n", + "IDX", + "LINK", + "TYPE", + "OPERATIONAL", + "SETUP"); for (i = 0; i < c; i++) { _cleanup_free_ char *setup_state = NULL, *operational_state = NULL; @@ -233,16 +309,18 @@ static int list_links(int argc, char *argv[], void *userdata) { char devid[2 + DECIMAL_STR_MAX(int)]; _cleanup_free_ char *t = NULL; - sd_network_link_get_operational_state(links[i].ifindex, &operational_state); + (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state); operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - sd_network_link_get_setup_state(links[i].ifindex, &setup_state); + r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state); + if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */ + setup_state = strdup("unmanaged"); setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); - sprintf(devid, "n%i", links[i].ifindex); - (void)sd_device_new_from_device_id(&d, devid); + xsprintf(devid, "n%i", links[i].ifindex); + (void) sd_device_new_from_device_id(&d, devid); - link_get_type_string(links[i].iftype, d, &t); + (void) link_get_type_string(links[i].iftype, d, &t); printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n", links[i].ifindex, links[i].name, strna(t), @@ -257,7 +335,7 @@ static int list_links(int argc, char *argv[], void *userdata) { } /* IEEE Organizationally Unique Identifier vendor string */ -static int ieee_oui(sd_hwdb *hwdb, struct ether_addr *mac, char **ret) { +static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) { const char *description; char modalias[strlen("OUI:XXYYXXYYXXYY") + 1], *desc; int r; @@ -404,6 +482,9 @@ static int dump_gateways( _cleanup_free_ struct local_address *local = NULL; int r, n, i; + assert(rtnl); + assert(prefix); + n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local); if (n < 0) return n; @@ -453,6 +534,9 @@ static int dump_addresses( _cleanup_free_ struct local_address *local = NULL; int r, n, i; + assert(rtnl); + assert(prefix); + n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local); if (n < 0) return n; @@ -485,6 +569,116 @@ static int dump_addresses( return 0; } +static int open_lldp_neighbors(int ifindex, FILE **ret) { + _cleanup_free_ char *p = NULL; + FILE *f; + + if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0) + return -ENOMEM; + + f = fopen(p, "re"); + if (!f) + return -errno; + + *ret = f; + return 0; +} + +static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) { + _cleanup_free_ void *raw = NULL; + size_t l; + le64_t u; + int r; + + assert(f); + assert(ret); + + l = fread(&u, 1, sizeof(u), f); + if (l == 0 && feof(f)) + return 0; + if (l != sizeof(u)) + return -EBADMSG; + + raw = new(uint8_t, le64toh(u)); + if (!raw) + return -ENOMEM; + + if (fread(raw, 1, le64toh(u), f) != le64toh(u)) + return -EBADMSG; + + r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u)); + if (r < 0) + return r; + + return 1; +} + +static int dump_lldp_neighbors(const char *prefix, int ifindex) { + _cleanup_fclose_ FILE *f = NULL; + int r, c = 0; + + assert(prefix); + assert(ifindex > 0); + + r = open_lldp_neighbors(ifindex, &f); + if (r < 0) + return r; + + for (;;) { + const char *system_name = NULL, *port_id = NULL, *port_description = NULL; + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + + r = next_lldp_neighbor(f, &n); + if (r < 0) + return r; + if (r == 0) + break; + + printf("%*s", + (int) strlen(prefix), + c == 0 ? prefix : ""); + + (void) sd_lldp_neighbor_get_system_name(n, &system_name); + (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); + (void) sd_lldp_neighbor_get_port_description(n, &port_description); + + printf("%s on port %s", strna(system_name), strna(port_id)); + + if (!isempty(port_description)) + printf(" (%s)", port_description); + + putchar('\n'); + + c++; + } + + return c; +} + +static void dump_ifindexes(const char *prefix, const int *ifindexes) { + unsigned c; + + assert(prefix); + + if (!ifindexes || ifindexes[0] <= 0) + return; + + for (c = 0; ifindexes[c] > 0; c++) { + char name[IF_NAMESIZE+1]; + + printf("%*s", + (int) strlen(prefix), + c == 0 ? prefix : ""); + + if (if_indextoname(ifindexes[c], name)) + fputs(name, stdout); + else + printf("%i", ifindexes[c]); + + fputc('\n', stdout); + } +} + static void dump_list(const char *prefix, char **l) { char **i; @@ -502,85 +696,36 @@ static void dump_list(const char *prefix, char **l) { static int link_status_one( sd_netlink *rtnl, sd_hwdb *hwdb, - const char *name) { + const LinkInfo *info) { + _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL; - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_(sd_device_unrefp) sd_device *d = NULL; char devid[2 + DECIMAL_STR_MAX(int)]; _cleanup_free_ char *t = NULL, *network = NULL; 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; - bool have_mac; - uint32_t mtu; + _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL; + int r; assert(rtnl); - assert(name); - - if (parse_ifindex(name, &ifindex) >= 0) - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex); - else { - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_append_string(req, IFLA_IFNAME, name); - } + assert(info); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to query link: %m"); - - r = sd_rtnl_message_link_get_ifindex(reply, &ifindex); - if (r < 0) - return rtnl_log_parse_error(r); - - r = sd_netlink_message_read_string(reply, IFLA_IFNAME, &name); - if (r < 0) - return rtnl_log_parse_error(r); - - r = sd_rtnl_message_link_get_type(reply, &iftype); - if (r < 0) - return rtnl_log_parse_error(r); - - have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0; - if (have_mac) { - const uint8_t *p; - bool all_zeroes = true; - - for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++) - if (*p != 0) { - all_zeroes = false; - break; - } - - if (all_zeroes) - have_mac = false; - } - - (void) sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu); - - (void) sd_network_link_get_operational_state(ifindex, &operational_state); + (void) sd_network_link_get_operational_state(info->ifindex, &operational_state); operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - (void) sd_network_link_get_setup_state(ifindex, &setup_state); + r = sd_network_link_get_setup_state(info->ifindex, &setup_state); + if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */ + setup_state = strdup("unmanaged"); setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); - (void) sd_network_link_get_dns(ifindex, &dns); - (void) sd_network_link_get_search_domains(ifindex, &search_domains); - (void) sd_network_link_get_route_domains(ifindex, &route_domains); - (void) sd_network_link_get_ntp(ifindex, &ntp); + (void) sd_network_link_get_dns(info->ifindex, &dns); + (void) sd_network_link_get_search_domains(info->ifindex, &search_domains); + (void) sd_network_link_get_route_domains(info->ifindex, &route_domains); + (void) sd_network_link_get_ntp(info->ifindex, &ntp); - sprintf(devid, "n%i", ifindex); + xsprintf(devid, "n%i", info->ifindex); (void) sd_device_new_from_device_id(&d, devid); @@ -598,14 +743,14 @@ static int link_status_one( (void) sd_device_get_property_value(d, "ID_MODEL", &model); } - link_get_type_string(iftype, d, &t); + (void) link_get_type_string(info->iftype, d, &t); - sd_network_link_get_network_file(ifindex, &network); + (void) sd_network_link_get_network_file(info->ifindex, &network); - sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to); - sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by); + (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to); + (void) sd_network_link_get_carrier_bound_by(info->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("%s%s%s %i: %s\n", on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, info->ifindex, info->name); printf(" Link File: %s\n" " Network File: %s\n" @@ -626,23 +771,23 @@ static int link_status_one( if (model) printf(" Model: %s\n", model); - if (have_mac) { + if (info->has_mac_address) { _cleanup_free_ char *description = NULL; char ea[ETHER_ADDR_TO_STRING_MAX]; - ieee_oui(hwdb, &e, &description); + (void) ieee_oui(hwdb, &info->mac_address, &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(&info->mac_address, ea), description); else - printf(" HW Address: %s\n", ether_addr_to_string(&e, ea)); + printf(" HW Address: %s\n", ether_addr_to_string(&info->mac_address, ea)); } - if (mtu > 0) - printf(" MTU: %u\n", mtu); + if (info->has_mtu) + printf(" MTU: %u\n", info->mtu); - dump_addresses(rtnl, " Address: ", ifindex); - dump_gateways(rtnl, hwdb, " Gateway: ", ifindex); + (void) dump_addresses(rtnl, " Address: ", info->ifindex); + (void) dump_gateways(rtnl, hwdb, " Gateway: ", info->ifindex); dump_list(" DNS: ", dns); dump_list(" Search Domains: ", search_domains); @@ -650,362 +795,237 @@ static int link_status_one( dump_list(" NTP: ", ntp); - dump_list("Carrier Bound To: ", carrier_bound_to); - dump_list("Carrier Bound By: ", carrier_bound_by); + dump_ifindexes("Carrier Bound To: ", carrier_bound_to); + dump_ifindexes("Carrier Bound By: ", carrier_bound_by); - (void) sd_network_link_get_timezone(ifindex, &tz); + (void) sd_network_link_get_timezone(info->ifindex, &tz); if (tz) - printf(" Time Zone: %s", tz); + printf(" Time Zone: %s\n", tz); + + (void) dump_lldp_neighbors(" Connected To: ", info->ifindex); return 0; } -static int link_status(int argc, char *argv[], void *userdata) { - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - char **name; - int r; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - r = sd_hwdb_new(&hwdb); - if (r < 0) - log_debug_errno(r, "Failed to open hardware database: %m"); - - if (argc <= 1 && !arg_all) { - _cleanup_free_ char *operational_state = NULL; - _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains; - const char *on_color_operational, *off_color_operational; - - sd_network_get_operational_state(&operational_state); - operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - - printf("%s%s%s State: %s%s%s\n", - on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, - on_color_operational, strna(operational_state), off_color_operational); - - dump_addresses(rtnl, " Address: ", 0); - dump_gateways(rtnl, hwdb, " Gateway: ", 0); - - sd_network_get_dns(&dns); - dump_list(" DNS: ", dns); - - sd_network_get_search_domains(&search_domains); - dump_list("Search Domains: ", search_domains); - - sd_network_get_route_domains(&route_domains); - dump_list(" Route Domains: ", route_domains); - - sd_network_get_ntp(&ntp); - dump_list(" NTP: ", ntp); - - return 0; - } +static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { + _cleanup_free_ char *operational_state = NULL; + _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; + const char *on_color_operational, *off_color_operational; - pager_open_if_enabled(); + assert(rtnl); - if (arg_all) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - _cleanup_free_ LinkInfo *links = NULL; - int c, i; + (void) sd_network_get_operational_state(&operational_state); + operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); + printf("%s%s%s State: %s%s%s\n", + on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, + on_color_operational, strna(operational_state), off_color_operational); - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return rtnl_log_create_error(r); + (void) dump_addresses(rtnl, " Address: ", 0); + (void) dump_gateways(rtnl, hwdb, " Gateway: ", 0); - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to enumerate links: %m"); + (void) sd_network_get_dns(&dns); + dump_list(" DNS: ", dns); - c = decode_and_sort_links(reply, &links); - if (c < 0) - return rtnl_log_parse_error(c); + (void) sd_network_get_search_domains(&search_domains); + dump_list("Search Domains: ", search_domains); - for (i = 0; i < c; i++) { - if (i > 0) - fputc('\n', stdout); + (void) sd_network_get_route_domains(&route_domains); + dump_list(" Route Domains: ", route_domains); - link_status_one(rtnl, hwdb, links[i].name); - } - } else { - STRV_FOREACH(name, argv + 1) { - if (name != argv + 1) - fputc('\n', stdout); - - link_status_one(rtnl, hwdb, *name); - } - } + (void) sd_network_get_ntp(&ntp); + dump_list(" NTP: ", ntp); return 0; } -const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_; -LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_; - -static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = { - [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O", - [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P", - [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B", - [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W", - [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R", - [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T", - [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D", - [LLDP_SYSTEM_CAPABILITIES_STATION] = "A", - [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C", - [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S", - [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M", - [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A", -}; - -DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities); - -static char *lldp_system_caps(uint16_t cap) { - _cleanup_free_ char *s = NULL, *t = NULL; - char *capability; - - t = strdup("[ "); - if (!t) - return NULL; - - if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } +static int link_status(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; + _cleanup_free_ LinkInfo *links = NULL; + int r, c, i; - if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL); - if (!s) - return NULL; + pager_open(arg_no_pager, false); - free(t); - t = s; - } + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); - if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL); - if (!s) - return NULL; + r = sd_hwdb_new(&hwdb); + if (r < 0) + log_debug_errno(r, "Failed to open hardware database: %m"); - free(t); - t = s; - } + if (arg_all) + c = acquire_link_info_all(rtnl, &links); + else if (argc <= 1) + return system_status(rtnl, hwdb); + else + c = acquire_link_info_strv(rtnl, argv + 1, &links); + if (c < 0) + return c; - if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL); - if (!s) - return NULL; + for (i = 0; i < c; i++) { + if (i > 0) + fputc('\n', stdout); - free(t); - t = s; + link_status_one(rtnl, hwdb, links + i); } - if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } + return 0; +} - if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) { - s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR)); - if (!s) - return NULL; +static char *lldp_capabilities_to_string(uint16_t x) { + static const char characters[] = { + 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm', + }; + char *ret; + unsigned i; - free(t); - } + ret = new(char, ELEMENTSOF(characters) + 1); + if (!ret) + return NULL; - if (!s) { - s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX)); - if (!s) - return NULL; + for (i = 0; i < ELEMENTSOF(characters); i++) + ret[i] = (x & (1U << i)) ? characters[i] : '.'; - free(t); - } + ret[i] = 0; + return ret; +} - t = strappend(s, "]"); - if (!t) - return NULL; +static void lldp_capabilities_legend(uint16_t x) { + unsigned w, i, cols = columns(); + static const char* const table[] = { + "o - Other", + "p - Repeater", + "b - Bridge", + "w - WLAN Access Point", + "r - Router", + "t - Telephone", + "d - DOCSIS cable device", + "a - Station", + "c - Customer VLAN", + "s - Service VLAN", + "m - Two-port MAC Relay (TPMR)", + }; - free(s); - capability = t; + if (x == 0) + return; - s = NULL; - t = NULL; + printf("\nCapability Flags:\n"); + for (w = 0, i = 0; i < ELEMENTSOF(table); i++) + if (x & (1U << i) || arg_all) { + bool newline; - return capability; + newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols; + if (newline) + w = 0; + w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]); + } + puts(""); } static int link_lldp_status(int argc, char *argv[], void *userdata) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_free_ LinkInfo *links = NULL; - double ttl = -1; - uint32_t capability; - int i, r, c, j; - const char *p; - char **s; - - pager_open_if_enabled(); + int i, r, c, m = 0; + uint16_t all = 0; r = sd_netlink_open(&rtnl); if (r < 0) return log_error_errno(r, "Failed to connect to netlink: %m"); - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to enumerate links: %m"); - - c = decode_and_sort_links(reply, &links); + if (argc > 1) + c = acquire_link_info_strv(rtnl, argv + 1, &links); + else + c = acquire_link_info_all(rtnl, &links); if (c < 0) - return rtnl_log_parse_error(c); + return c; + + pager_open(arg_no_pager, false); if (arg_legend) - printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability"); + printf("%-16s %-17s %-16s %-11s %-17s %-16s\n", + "LINK", + "CHASSIS ID", + "SYSTEM NAME", + "CAPS", + "PORT ID", + "PORT DESCRIPTION"); - for (i = j = 0; i < c; i++) { - _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL; - _cleanup_strv_free_ char **l = NULL; + for (i = 0; i < c; i++) { + _cleanup_fclose_ FILE *f = NULL; - r = sd_network_link_get_lldp(links[i].ifindex, &lldp); - if (r < 0) + r = open_lldp_neighbors(links[i].ifindex, &f); + if (r == -ENOENT) continue; + if (r < 0) { + log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex); + continue; + } - l = strv_split_newlines(lldp); - if (!l) - return -ENOMEM; - - STRV_FOREACH(s, l) { - - p = *s; - for (;;) { - _cleanup_free_ char *a = NULL, *b = NULL, *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse LLDP syntax \"%s\": %m", *s); - - if (r == 0) - break; - - r = split_pair(word, "=", &a, &b); - if (r < 0) - continue; - - if (streq(a, "_Chassis")) { - r = free_and_strdup(&chassis, b); - if (r < 0) - return r; - - } else if (streq(a, "_Port")) { - r = free_and_strdup(&port, b); - if (r < 0) - return r; - - } else if (streq(a, "_TTL")) { - long long unsigned x = 0; - usec_t time; + for (;;) { + _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL; + const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL, *capabilities = NULL; + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + uint16_t cc; - r = safe_atollu(b, &x); - if (r < 0 || (usec_t) x != x) - return log_warning_errno(r < 0 ? r : ERANGE, - "Failed to parse TTL \"%s\": %m", b); + r = next_lldp_neighbor(f, &n); + if (r < 0) { + log_warning_errno(r, "Failed to read neighbor data: %m"); + break; + } + if (r == 0) + break; - time = now(clock_boottime_or_monotonic()); - if (x < time) - continue; + (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id); + (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); + (void) sd_lldp_neighbor_get_system_name(n, &system_name); + (void) sd_lldp_neighbor_get_port_description(n, &port_description); - ttl = (double) (x - time) / USEC_PER_SEC; + if (chassis_id) { + cid = ellipsize(chassis_id, 17, 100); + if (cid) + chassis_id = cid; + } - } else if (streq(a, "_CAP")) { - sscanf(b, "%x", &capability); + if (port_id) { + pid = ellipsize(port_id, 17, 100); + if (pid) + port_id = pid; + } - cap = lldp_system_caps(capability); - } + if (system_name) { + sname = ellipsize(system_name, 16, 100); + if (sname) + system_name = sname; + } + if (port_description) { + pdesc = ellipsize(port_description, 16, 100); + if (pdesc) + port_description = pdesc; } - if (ttl >= 0) { - printf("%10s %24s %16s %16f %16s\n", - links[i].name, - strna(chassis), strna(port), - ttl, cap); - j++; + if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) { + capabilities = lldp_capabilities_to_string(cc); + all |= cc; } + + printf("%-16s %-17s %-16s %-11s %-17s %-16s\n", + links[i].name, + strna(chassis_id), + strna(system_name), + strna(capabilities), + strna(port_id), + strna(port_description)); + + m++; } } if (arg_legend) { - printf("\nCapability Codes:\n" - "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n" - "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n" - "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n"); - - printf("Total entries displayed: %d\n", j); + lldp_capabilities_legend(all); + printf("\n%i neighbors listed.\n", m); } return 0; @@ -1020,9 +1040,9 @@ static void help(void) { " --no-legend Do not show the headers and footers\n" " -a --all Show status for all links\n\n" "Commands:\n" - " list List links\n" + " list [LINK...] List links\n" " status [LINK...] Show link status\n" - " lldp Show lldp information\n" + " lldp [LINK...] Show LLDP neighbors\n" , program_invocation_short_name); } @@ -1084,15 +1104,23 @@ static int parse_argv(int argc, char *argv[]) { static int networkctl_main(int argc, char *argv[]) { const Verb verbs[] = { - { "list", VERB_ANY, 1, VERB_DEFAULT, list_links }, - { "status", 1, VERB_ANY, 0, link_status }, - { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status }, + { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links }, + { "status", VERB_ANY, VERB_ANY, 0, link_status }, + { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status }, {} }; return dispatch_verb(argc, argv, verbs, NULL); } +static void warn_networkd_missing(void) { + + if (access("/run/systemd/netif/state", F_OK) >= 0) + return; + + fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n"); +} + int main(int argc, char* argv[]) { int r; @@ -1103,6 +1131,8 @@ int main(int argc, char* argv[]) { if (r <= 0) goto finish; + warn_networkd_missing(); + r = networkctl_main(argc, argv); finish: diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index d9d487d805..ebc6c9eb9e 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -148,8 +148,12 @@ int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union for (;;) { if (!address_pool_prefix_is_taken(p, &u, prefixlen)) { _cleanup_free_ char *s = NULL; + int r; + + r = in_addr_to_string(p->family, &u, &s); + if (r < 0) + return r; - in_addr_to_string(p->family, &u, &s); log_debug("Found range %s/%u", strna(s), prefixlen); *found = u; diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h index 8e1378ff40..af30decfe0 100644 --- a/src/network/networkd-address-pool.h +++ b/src/network/networkd-address-pool.h @@ -22,7 +22,9 @@ typedef struct AddressPool AddressPool; #include "in-addr-util.h" -#include "networkd.h" +#include "list.h" + +typedef struct Manager Manager; struct AddressPool { Manager *manager; diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 7f9a7268cc..367c340e08 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -27,6 +27,7 @@ #include "networkd.h" #include "parse-util.h" #include "set.h" +#include "socket-util.h" #include "string-util.h" #include "utf8.h" #include "util.h" @@ -67,16 +68,15 @@ int address_new_static(Network *network, unsigned section, Address **ret) { if (r < 0) return r; - address->network = network; - - LIST_APPEND(addresses, network->static_addresses, address); - if (section) { address->section = section; hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address); } + address->network = network; + LIST_APPEND(addresses, network->static_addresses, address); + *ret = address; address = NULL; @@ -98,6 +98,9 @@ void address_free(Address *address) { if (address->link) { set_remove(address->link->addresses, address); set_remove(address->link->addresses_foreign, address); + + if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address)) + memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); } free(address); @@ -331,6 +334,10 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s assert(address); assert(cinfo); + assert_return(address->link, 1); + + if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; ready = address_is_ready(address); @@ -338,19 +345,18 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s address->scope = scope; address->cinfo = *cinfo; - if (address->link) { - link_update_operstate(address->link); - - if (!ready && address_is_ready(address)) { - link_check_ready(address->link); - - if (address->family == AF_INET6 && - in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && - in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) { - r = link_ipv6ll_gained(address->link, &address->in_addr.in6); - if (r < 0) - return r; - } + link_update_operstate(address->link); + + if (!ready && address_is_ready(address)) { + link_check_ready(address->link); + + if (address->family == AF_INET6 && + in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && + in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) { + + r = link_ipv6ll_gained(address->link, &address->in_addr.in6); + if (r < 0) + return r; } } @@ -404,8 +410,11 @@ int address_get(Link *link, int family, const union in_addr_union *in_addr, unsi return 0; } -int address_remove(Address *address, Link *link, - sd_netlink_message_handler_t callback) { +int address_remove( + Address *address, + Link *link, + sd_netlink_message_handler_t callback) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -727,7 +736,8 @@ int config_parse_address(const char *unit, return 0; } -int config_parse_label(const char *unit, +int config_parse_label( + const char *unit, const char *filename, unsigned line, const char *section, @@ -737,9 +747,9 @@ int config_parse_label(const char *unit, const char *rvalue, void *data, void *userdata) { - Network *network = userdata; + _cleanup_address_free_ Address *n = NULL; - char *label; + Network *network = userdata; int r; assert(filename); @@ -752,25 +762,64 @@ int config_parse_label(const char *unit, if (r < 0) return r; - label = strdup(rvalue); - if (!label) + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue); + return 0; + } + + r = free_and_strdup(&n->label, rvalue); + if (r < 0) return log_oom(); - if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - free(label); + n = NULL; + + return 0; +} + +int config_parse_lifetime(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + _cleanup_address_free_ Address *n = NULL; + unsigned k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new_static(network, section_line, &n); + if (r < 0) + return r; + + if (STR_IN_SET(rvalue, "forever", "infinity")) { + n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME; + n = NULL; + return 0; } - free(n->label); - if (*label) - n->label = label; - else { - free(label); - n->label = NULL; + r = safe_atou(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue); + return 0; } - n = NULL; + if (k != 0) + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k); + else { + n->cinfo.ifa_prefered = k; + n = NULL; + } return 0; } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 338f6eb9a2..784ab18b27 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -28,10 +28,12 @@ typedef struct Address Address; #include "networkd-link.h" #include "networkd-network.h" -#include "networkd.h" #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU +typedef struct Network Network; +typedef struct Link Link; + struct Address { Network *network; unsigned section; @@ -74,3 +76,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c new file mode 100644 index 0000000000..b67a1f6d09 --- /dev/null +++ b/src/network/networkd-conf.c @@ -0,0 +1,111 @@ +/*** + This file is part of systemd. + + Copyright 2014 Vinay Kulkarni <kulkarniv@vmware.com> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. + ***/ + +#include <ctype.h> + +#include "conf-parser.h" +#include "def.h" +#include "dhcp-identifier.h" +#include "hexdecoct.h" +#include "networkd-conf.h" +#include "string-table.h" + +int manager_parse_config_file(Manager *m) { + assert(m); + + return config_parse_many(PKGSYSCONFDIR "/networkd.conf", + CONF_PATHS_NULSTR("systemd/networkd.conf.d"), + "DHCP\0", + config_item_perf_lookup, networkd_gperf_lookup, + false, m); +} + +static const char* const duid_type_table[_DUID_TYPE_MAX] = { + [DUID_TYPE_LLT] = "link-layer-time", + [DUID_TYPE_EN] = "vendor", + [DUID_TYPE_LL] = "link-layer", + [DUID_TYPE_UUID] = "uuid", +}; +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType); +DEFINE_CONFIG_PARSE_ENUM(config_parse_duid_type, duid_type, DUIDType, "Failed to parse DUID type"); + +int config_parse_duid_rawdata( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + DUID *ret = data; + uint8_t raw_data[MAX_DUID_LEN]; + unsigned count = 0; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(ret); + + /* RawData contains DUID in format "NN:NN:NN..." */ + for (;;) { + int n1, n2, len, r; + uint32_t byte; + _cleanup_free_ char *cbyte = NULL; + + r = extract_first_word(&rvalue, &cbyte, ":", 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); + return 0; + } + if (r == 0) + break; + if (count >= MAX_DUID_LEN) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); + return 0; + } + + len = strlen(cbyte); + if (len != 1 && len != 2) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); + return 0; + } + n1 = unhexchar(cbyte[0]); + if (len == 2) + n2 = unhexchar(cbyte[1]); + else + n2 = 0; + + if (n1 < 0 || n2 < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); + return 0; + } + + byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2; + raw_data[count++] = byte; + } + + assert_cc(sizeof(raw_data) == sizeof(ret->raw_data)); + memcpy(ret->raw_data, raw_data, count); + ret->raw_data_len = count; + return 0; +} diff --git a/src/network/networkd-conf.h b/src/network/networkd-conf.h new file mode 100644 index 0000000000..c7bfb42a72 --- /dev/null +++ b/src/network/networkd-conf.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Vinay Kulkarni <kulkarniv@vmware.com> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "networkd.h" + +int manager_parse_config_file(Manager *m); + +const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, unsigned length); + +int config_parse_duid_type( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); +int config_parse_duid_rawdata( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 03c28bbcb6..2ddcee9db8 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -24,7 +24,7 @@ #include "dhcp-lease-internal.h" #include "hostname-util.h" #include "network-internal.h" -#include "networkd-link.h" +#include "networkd.h" static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { @@ -34,7 +34,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, assert(link); assert(link->dhcp4_messages > 0); - link->dhcp4_messages --; + link->dhcp4_messages--; r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -57,6 +57,10 @@ static int link_set_dhcp_routes(Link *link) { assert(link); assert(link->dhcp_lease); + assert(link->network); + + if (!link->network->dhcp_use_routes) + return 0; r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); if (r < 0 && r != -ENODATA) @@ -92,25 +96,25 @@ static int link_set_dhcp_routes(Link *link) { route_gw->protocol = RTPROT_DHCP; route_gw->priority = link->network->dhcp_route_metric; - r = route_configure(route_gw, link, &dhcp4_route_handler); + r = route_configure(route_gw, link, dhcp4_route_handler); if (r < 0) return log_link_warning_errno(link, r, "Could not set host route: %m"); - link->dhcp4_messages ++; + link->dhcp4_messages++; route->family = AF_INET; route->gw.in = gateway; route->prefsrc.in = address; route->priority = link->network->dhcp_route_metric; - r = route_configure(route, link, &dhcp4_route_handler); + r = route_configure(route, link, dhcp4_route_handler); if (r < 0) { log_link_warning_errno(link, r, "Could not set routes: %m"); link_enter_failed(link); return r; } - link->dhcp4_messages ++; + link->dhcp4_messages++; } n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes); @@ -133,11 +137,11 @@ static int link_set_dhcp_routes(Link *link) { assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0); route->priority = link->network->dhcp_route_metric; - r = route_configure(route, link, &dhcp4_route_handler); + r = route_configure(route, link, dhcp4_route_handler); if (r < 0) return log_link_warning_errno(link, r, "Could not set host route: %m"); - link->dhcp4_messages ++; + link->dhcp4_messages++; } return 0; @@ -173,7 +177,7 @@ static int dhcp_lease_lost(Link *link) { assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0); route_remove(route, link, - &link_route_remove_handler); + link_route_remove_handler); } } } @@ -194,7 +198,7 @@ static int dhcp_lease_lost(Link *link) { route_gw->scope = RT_SCOPE_LINK; route_remove(route_gw, link, - &link_route_remove_handler); + link_route_remove_handler); } r = route_new(&route); @@ -203,7 +207,7 @@ static int dhcp_lease_lost(Link *link) { route->gw.in = gateway; route_remove(route, link, - &link_route_remove_handler); + link_route_remove_handler); } } @@ -217,7 +221,7 @@ static int dhcp_lease_lost(Link *link) { address->in_addr.in = addr; address->prefixlen = prefixlen; - address_remove(address, link, &link_address_remove_handler); + address_remove(address, link, link_address_remove_handler); } } @@ -305,7 +309,7 @@ static int dhcp4_update_address(Link *link, /* allow reusing an existing address and simply update its lifetime * in case it already exists */ - r = address_configure(addr, link, &dhcp4_address_handler, true); + r = address_configure(addr, link, dhcp4_address_handler, true); if (r < 0) return r; @@ -624,14 +628,24 @@ int dhcp4_configure(Link *link) { } switch (link->network->dhcp_client_identifier) { - case DHCP_CLIENT_ID_DUID: - /* Library defaults to this. */ + case DHCP_CLIENT_ID_DUID: { + /* If configured, apply user specified DUID and/or IAID */ + const DUID *duid = link_duid(link); + + r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, + link->network->iaid, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return r; break; + } case DHCP_CLIENT_ID_MAC: r = sd_dhcp_client_set_client_id(link->dhcp_client, ARPHRD_ETHER, (const uint8_t *) &link->mac, - sizeof (link->mac)); + sizeof(link->mac)); if (r < 0) return r; break; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index bf8e826368..c5a3c52e94 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -23,7 +23,7 @@ #include <systemd/sd-dhcp6-client.h> #include "network-internal.h" -#include "networkd-link.h" +#include "networkd.h" static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); @@ -103,8 +103,8 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { sd_dhcp6_lease_reset_address_iter(lease); while (sd_dhcp6_lease_get_address(lease, &ip6_addr, - &lifetime_preferred, - &lifetime_valid) >= 0) { + &lifetime_preferred, + &lifetime_valid) >= 0) { r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); if (r < 0) @@ -194,18 +194,13 @@ int dhcp6_request_address(Link *link) { if (r < 0) return r; - if (running) { - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - return r; - } - return 0; } int dhcp6_configure(Link *link) { sd_dhcp6_client *client = NULL; int r; + const DUID *duid; assert(link); @@ -230,6 +225,18 @@ int dhcp6_configure(Link *link) { if (r < 0) goto error; + r = sd_dhcp6_client_set_iaid(client, link->network->iaid); + if (r < 0) + goto error; + + duid = link_duid(link); + r = sd_dhcp6_client_set_duid(client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + goto error; + r = sd_dhcp6_client_set_index(client, link->ifindex); if (r < 0) goto error; diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index 1538caa204..241f486211 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -37,7 +37,7 @@ int fdb_entry_new_static(Network *const network, assert(network); /* search entry in hashmap first. */ - if(section) { + if (section) { fdb_entry = hashmap_get(network->fdb_entries_by_section, UINT_TO_PTR(section)); if (fdb_entry) { *ret = fdb_entry; @@ -141,10 +141,10 @@ int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry) { /* remove and FDB entry. */ void fdb_entry_free(FdbEntry *fdb_entry) { - if(!fdb_entry) + if (!fdb_entry) return; - if(fdb_entry->network) { + if (fdb_entry->network) { LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry); diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h index 89b3e29405..84410714f5 100644 --- a/src/network/networkd-fdb.h +++ b/src/network/networkd-fdb.h @@ -19,10 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -typedef struct FdbEntry FdbEntry; +#include "list.h" +#include "macro.h" -#include "networkd-network.h" -#include "networkd.h" +typedef struct Network Network; +typedef struct FdbEntry FdbEntry; +typedef struct Link Link; struct FdbEntry { Network *network; diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf new file mode 100644 index 0000000000..3fdfe74955 --- /dev/null +++ b/src/network/networkd-gperf.gperf @@ -0,0 +1,18 @@ +%{ +#include <stddef.h> +#include "conf-parser.h" +#include "networkd-conf.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name networkd_gperf_hash +%define lookup-function-name networkd_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid.type) +DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 949c75337c..ae323d595b 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -21,7 +21,7 @@ #include <linux/if.h> #include "network-internal.h" -#include "networkd-link.h" +#include "networkd.h" static int ipv4ll_address_lost(Link *link) { _cleanup_address_free_ Address *address = NULL; @@ -51,7 +51,7 @@ static int ipv4ll_address_lost(Link *link) { address->prefixlen = 16; address->scope = RT_SCOPE_LINK; - address_remove(address, link, &link_address_remove_handler); + address_remove(address, link, link_address_remove_handler); r = route_new(&route); if (r < 0) { @@ -63,7 +63,7 @@ static int ipv4ll_address_lost(Link *link) { route->scope = RT_SCOPE_LINK; route->priority = IPV4LL_ROUTE_METRIC; - route_remove(route, link, &link_route_remove_handler); + route_remove(route, link, link_route_remove_handler); link_check_ready(link); @@ -165,7 +165,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { return 0; } -static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){ +static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { Link *link = userdata; int r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 692c0bf63d..a021fc886f 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -28,8 +28,8 @@ #include "fileio.h" #include "netlink-util.h" #include "network-internal.h" -#include "networkd-link.h" -#include "networkd-netdev.h" +#include "networkd.h" +#include "networkd-lldp-tx.h" #include "set.h" #include "socket-util.h" #include "stdio-util.h" @@ -38,7 +38,12 @@ #include "util.h" #include "virt.h" -bool link_dhcp6_enabled(Link *link) { +static bool link_dhcp6_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -48,7 +53,9 @@ bool link_dhcp6_enabled(Link *link) { return link->network->dhcp & ADDRESS_FAMILY_IPV6; } -bool link_dhcp4_enabled(Link *link) { +static bool link_dhcp4_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; @@ -58,7 +65,9 @@ bool link_dhcp4_enabled(Link *link) { return link->network->dhcp & ADDRESS_FAMILY_IPV4; } -bool link_dhcp4_server_enabled(Link *link) { +static bool link_dhcp4_server_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; @@ -68,7 +77,9 @@ bool link_dhcp4_server_enabled(Link *link) { return link->network->dhcp_server; } -bool link_ipv4ll_enabled(Link *link) { +static bool link_ipv4ll_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; @@ -78,7 +89,12 @@ bool link_ipv4ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV4; } -bool link_ipv6ll_enabled(Link *link) { +static bool link_ipv6ll_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -88,20 +104,51 @@ bool link_ipv6ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV6; } -bool link_lldp_enabled(Link *link) { +static bool link_ipv6_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + return link_dhcp6_enabled(link) || link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network); +} + +static bool link_lldp_rx_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; + if (link->iftype != ARPHRD_ETHER) + return false; + if (!link->network) return false; if (link->network->bridge) return false; - return link->network->lldp; + return link->network->lldp_mode != LLDP_MODE_NO; +} + +static bool link_lldp_emit_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (link->iftype != ARPHRD_ETHER) + return false; + + if (!link->network) + return false; + + return link->network->lldp_emit != LLDP_EMIT_NO; } static bool link_ipv4_forward_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; @@ -115,6 +162,7 @@ static bool link_ipv4_forward_enabled(Link *link) { } static bool link_ipv6_forward_enabled(Link *link) { + assert(link); if (!socket_ipv6_is_supported()) return false; @@ -131,7 +179,27 @@ static bool link_ipv6_forward_enabled(Link *link) { return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } -bool link_ipv6_accept_ra_enabled(Link *link) { +static bool link_proxy_arp_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->network->proxy_arp < 0) + return false; + + return true; +} + +static bool link_ipv6_accept_ra_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -154,6 +222,7 @@ bool link_ipv6_accept_ra_enabled(Link *link) { } static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { + assert(link); if (!socket_ipv6_is_supported()) return _IPV6_PRIVACY_EXTENSIONS_INVALID; @@ -167,6 +236,31 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { return link->network->ipv6_privacy_extensions; } +static int link_enable_ipv6(Link *link) { + const char *p = NULL; + bool disabled; + int r; + + if (link->flags & IFF_LOOPBACK) + return 0; + + disabled = !link_ipv6_enabled(link); + + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6"); + + r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname); + else { + if (disabled) + log_link_info(link, "IPv6 disabled for interface: %m"); + else + log_link_info(link, "IPv6 enabled for interface: %m"); + } + + return 0; +} + void link_update_operstate(Link *link) { LinkOperationalState operstate; assert(link); @@ -214,7 +308,6 @@ void link_update_operstate(Link *link) { link->operstate = operstate; link_send_changed(link, "OperationalState", NULL); link_dirty(link); - manager_dirty(link->manager); } } @@ -300,6 +393,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { uint16_t type; const char *ifname; int r, ifindex; + unsigned short iftype; assert(manager); assert(message); @@ -317,6 +411,10 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { else if (ifindex <= 0) return -EINVAL; + r = sd_rtnl_message_link_get_type(message, &iftype); + if (r < 0) + return r; + r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname); if (r < 0) return r; @@ -330,30 +428,24 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { link->state = LINK_STATE_PENDING; link->rtnl_extended_attrs = true; link->ifindex = ifindex; + link->iftype = iftype; link->ifname = strdup(ifname); if (!link->ifname) return -ENOMEM; r = sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac); if (r < 0) - log_link_debug(link, "MAC address not found for new device, continuing without"); + log_link_debug_errno(link, r, "MAC address not found for new device, continuing without"); - r = asprintf(&link->state_file, "/run/systemd/netif/links/%d", - link->ifindex); - if (r < 0) + if (asprintf(&link->state_file, "/run/systemd/netif/links/%d", link->ifindex) < 0) return -ENOMEM; - r = asprintf(&link->lease_file, "/run/systemd/netif/leases/%d", - link->ifindex); - if (r < 0) + if (asprintf(&link->lease_file, "/run/systemd/netif/leases/%d", link->ifindex) < 0) return -ENOMEM; - r = asprintf(&link->lldp_file, "/run/systemd/netif/lldp/%d", - link->ifindex); - if (r < 0) + if (asprintf(&link->lldp_file, "/run/systemd/netif/lldp/%d", link->ifindex) < 0) return -ENOMEM; - r = hashmap_ensure_allocated(&manager->links, NULL); if (r < 0) return r; @@ -399,10 +491,11 @@ static void link_free(Link *link) { sd_dhcp_client_unref(link->dhcp_client); sd_dhcp_lease_unref(link->dhcp_lease); + link_lldp_emit_stop(link); + free(link->lease_file); sd_lldp_unref(link->lldp); - free(link->lldp_file); sd_ipv4ll_unref(link->ipv4ll); @@ -436,7 +529,7 @@ Link *link_unref(Link *link) { assert(link->n_ref > 0); - link->n_ref --; + link->n_ref--; if (link->n_ref > 0) return NULL; @@ -452,7 +545,7 @@ Link *link_ref(Link *link) { assert(link->n_ref > 0); - link->n_ref ++; + link->n_ref++; return link; } @@ -482,8 +575,6 @@ static void link_set_state(Link *link, LinkState state) { link->state = state; link_send_changed(link, "AdministrativeState", NULL); - - return; } static void link_enter_unmanaged(Link *link) { @@ -506,33 +597,28 @@ static int link_stop_clients(Link *link) { if (link->dhcp_client) { k = sd_dhcp_client_stop(link->dhcp_client); if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop DHCPv4 client: %m"); + r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m"); } if (link->ipv4ll) { k = sd_ipv4ll_stop(link->ipv4ll); if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"); + r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m"); } if (link->dhcp6_client) { k = sd_dhcp6_client_stop(link->dhcp6_client); if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"); + r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); } if (link->ndisc_router_discovery) { k = sd_ndisc_stop(link->ndisc_router_discovery); if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"); - } - - if (link->lldp) { - k = sd_lldp_stop(link->lldp); - if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop LLDP: %m"); + r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); } + link_lldp_emit_stop(link); return r; } @@ -600,6 +686,9 @@ void link_check_ready(Link *link) { assert(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + if (!link->network) return; @@ -645,7 +734,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata LINK_STATE_SETTING_ROUTES, LINK_STATE_FAILED, LINK_STATE_LINGER)); - link->link_messages --; + link->link_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -674,14 +763,14 @@ static int link_enter_set_routes(Link *link) { link_set_state(link, LINK_STATE_SETTING_ROUTES); LIST_FOREACH(routes, rt, link->network->static_routes) { - r = route_configure(rt, link, &route_handler); + r = route_configure(rt, link, route_handler); if (r < 0) { log_link_warning_errno(link, r, "Could not set routes: %m"); link_enter_failed(link); return r; } - link->link_messages ++; + link->link_messages++; } if (link->link_messages == 0) { @@ -723,7 +812,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES, LINK_STATE_FAILED, LINK_STATE_LINGER)); - link->link_messages --; + link->link_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -843,14 +932,14 @@ static int link_enter_set_addresses(Link *link) { link_set_state(link, LINK_STATE_SETTING_ADDRESSES); LIST_FOREACH(addresses, ad, link->network->static_addresses) { - r = address_configure(ad, link, &address_handler, false); + r = address_configure(ad, link, address_handler, false); if (r < 0) { log_link_warning_errno(link, r, "Could not set addresses: %m"); link_enter_failed(link); return r; } - link->link_messages ++; + link->link_messages++; } /* now that we can figure out a default address for the dhcp server, @@ -934,6 +1023,12 @@ static int link_enter_set_addresses(Link *link) { log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m"); } + r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m"); + return r; + } + if (link->network->dhcp_server_emit_timezone) { _cleanup_free_ char *buffer = NULL; const char *tz = NULL; @@ -999,7 +1094,7 @@ static int link_set_bridge_fdb(Link *const link) { LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { r = fdb_entry_configure(link, fdb_entry); - if(r < 0) { + if (r < 0) { log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); break; } @@ -1008,6 +1103,22 @@ static int link_set_bridge_fdb(Link *const link) { return r; } +static int link_set_proxy_arp(Link *const link) { + const char *p = NULL; + int r; + + if (!link_proxy_arp_enabled(link)) + return 0; + + p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp"); + + r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m"); + + return 0; +} + static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; int r; @@ -1211,7 +1322,7 @@ static int link_set_bridge(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m"); - if(link->network->cost != 0) { + if (link->network->cost != 0) { r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); if (r < 0) return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m"); @@ -1230,23 +1341,93 @@ static int link_set_bridge(Link *link) { return r; } -static void lldp_handler(sd_lldp *lldp, int event, void *userdata) { +static int link_lldp_save(Link *link) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + sd_lldp_neighbor **l = NULL; + int n = 0, r, i; + + assert(link); + assert(link->lldp_file); + + if (!link->lldp) { + (void) unlink(link->lldp_file); + return 0; + } + + r = sd_lldp_get_neighbors(link->lldp, &l); + if (r < 0) + goto finish; + if (r == 0) { + (void) unlink(link->lldp_file); + goto finish; + } + + n = r; + + r = fopen_temporary(link->lldp_file, &f, &temp_path); + if (r < 0) + goto finish; + + fchmod(fileno(f), 0644); + + for (i = 0; i < n; i++) { + const void *p; + le64_t u; + size_t sz; + + r = sd_lldp_neighbor_get_raw(l[i], &p, &sz); + if (r < 0) + goto finish; + + u = htole64(sz); + (void) fwrite(&u, 1, sizeof(u), f); + (void) fwrite(p, 1, sz, f); + } + + r = fflush_and_check(f); + if (r < 0) + goto finish; + + if (rename(temp_path, link->lldp_file) < 0) { + r = -errno; + goto finish; + } + +finish: + if (r < 0) { + (void) unlink(link->lldp_file); + if (temp_path) + (void) unlink(temp_path); + + log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file); + } + + if (l) { + for (i = 0; i < n; i++) + sd_lldp_neighbor_unref(l[i]); + free(l); + } + + return r; +} + +static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { Link *link = userdata; int r; assert(link); - assert(link->network); - assert(link->manager); - switch (event) { - case SD_LLDP_EVENT_UPDATE_INFO: - r = sd_lldp_save(link->lldp, link->lldp_file); - if (r < 0) - log_link_warning_errno(link, r, "Could not save LLDP: %m"); + (void) link_lldp_save(link); + + if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) { + /* If we received information about a new neighbor, restart the LLDP "fast" logic */ - break; - default: - break; + log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); + + r = link_lldp_emit_start(link); + if (r < 0) + log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); } } @@ -1283,7 +1464,7 @@ static int link_acquire_ipv6_conf(Link *link) { return 0; } -static int link_acquire_conf(Link *link) { +static int link_acquire_ipv4_conf(Link *link) { int r; assert(link); @@ -1311,14 +1492,28 @@ static int link_acquire_conf(Link *link) { return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); } - if (link_lldp_enabled(link)) { - assert(link->lldp); + return 0; +} + +static int link_acquire_conf(Link *link) { + int r; - log_link_debug(link, "Starting LLDP"); + assert(link); - r = sd_lldp_start(link->lldp); + r = link_acquire_ipv4_conf(link); + if (r < 0) + return r; + + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { + r = link_acquire_ipv6_conf(link); + if (r < 0) + return r; + } + + if (link_lldp_emit_enabled(link)) { + r = link_lldp_emit_start(link); if (r < 0) - return log_link_warning_errno(link, r, "Could not start LLDP: %m"); + return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); } return 0; @@ -1382,7 +1577,21 @@ static int link_up(Link *link) { return log_link_error_errno(link, r, "Could not set MAC address: %m"); } + /* If IPv6 not configured (no static IPv6 address and neither DHCPv6 nor IPv6LL is enabled) + for this interface then disable IPv6 else enable it. */ + (void) link_enable_ipv6(link); + if (link->network->mtu) { + /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes + on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */ + if (link_ipv6_enabled(link) && link->network->mtu < IPV6_MIN_MTU) { + + log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as " + "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m"); + + link->network->mtu = IPV6_MIN_MTU; + } + r = sd_netlink_message_append_u32(req, IFLA_MTU, link->network->mtu); if (r < 0) return log_link_error_errno(link, r, "Could not set MTU: %m"); @@ -1392,7 +1601,7 @@ static int link_up(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); - if (socket_ipv6_is_supported()) { + if (link_ipv6_enabled(link)) { /* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */ r = sd_netlink_message_open_container(req, AF_INET6); if (r < 0) @@ -1559,7 +1768,7 @@ static int link_new_bound_by_list(Link *link) { m = link->manager; - HASHMAP_FOREACH (carrier, m->links, i) { + HASHMAP_FOREACH(carrier, m->links, i) { if (!carrier->network) continue; @@ -1578,7 +1787,7 @@ static int link_new_bound_by_list(Link *link) { if (list_updated) link_dirty(link); - HASHMAP_FOREACH (carrier, link->bound_by_links, i) { + HASHMAP_FOREACH(carrier, link->bound_by_links, i) { r = link_put_carrier(carrier, link, &carrier->bound_to_links); if (r < 0) return r; @@ -1738,7 +1947,7 @@ static int link_joined(Link *link) { } } - if(link->network->bridge) { + if (link->network->bridge) { r = link_set_bridge(link); if (r < 0) log_link_error_errno(link, r, "Could not set bridge message: %m"); @@ -1754,7 +1963,7 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *us assert(link); assert(link->network); - link->enslaving --; + link->enslaving--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -1810,7 +2019,7 @@ static int link_enter_join_netdev(Link *link) { return r; } - link->enslaving ++; + link->enslaving++; } if (link->network->bridge) { @@ -1831,7 +2040,7 @@ static int link_enter_join_netdev(Link *link) { return r; } - link->enslaving ++; + link->enslaving++; } HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) { @@ -1853,7 +2062,7 @@ static int link_enter_join_netdev(Link *link) { return r; } - link->enslaving ++; + link->enslaving++; } return 0; @@ -2030,6 +2239,27 @@ static int link_drop_foreign_config(Link *link) { return 0; } +static int link_update_lldp(Link *link) { + int r; + + assert(link); + + if (!link->lldp) + return 0; + + if (link->flags & IFF_UP) { + r = sd_lldp_start(link->lldp); + if (r > 0) + log_link_debug(link, "Started LLDP."); + } else { + r = sd_lldp_stop(link->lldp); + if (r > 0) + log_link_debug(link, "Stopped LLDP."); + } + + return r; +} + static int link_configure(Link *link) { int r; @@ -2049,6 +2279,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_set_proxy_arp(link); + if (r < 0) + return r; + r = link_set_ipv4_forward(link); if (r < 0) return r; @@ -2108,8 +2342,19 @@ static int link_configure(Link *link) { return r; } - if (link_lldp_enabled(link)) { - r = sd_lldp_new(link->ifindex, link->ifname, &link->mac, &link->lldp); + if (link_lldp_rx_enabled(link)) { + r = sd_lldp_new(&link->lldp, link->ifindex); + if (r < 0) + return r; + + r = sd_lldp_match_capabilities(link->lldp, + link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? + SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : + SD_LLDP_SYSTEM_CAPABILITIES_ALL); + if (r < 0) + return r; + + r = sd_lldp_set_filter_address(link->lldp, &link->mac); if (r < 0) return r; @@ -2117,8 +2362,11 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = sd_lldp_set_callback(link->lldp, - lldp_handler, link); + r = sd_lldp_set_callback(link->lldp, lldp_handler, link); + if (r < 0) + return r; + + r = link_update_lldp(link); if (r < 0) return r; } @@ -2127,12 +2375,6 @@ static int link_configure(Link *link) { r = link_acquire_conf(link); if (r < 0) return r; - - if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { - r = link_acquire_ipv6_conf(link); - if (r < 0) - return r; - } } return link_enter_join_netdev(link); @@ -2307,7 +2549,7 @@ network_file_fail: continue; } - *prefixlen_str ++ = '\0'; + *prefixlen_str++ = '\0'; r = sscanf(prefixlen_str, "%hhu", &prefixlen); if (r != 1) { @@ -2354,7 +2596,7 @@ network_file_fail: continue; } - *prefixlen_str ++ = '\0'; + *prefixlen_str++ = '\0'; r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%hhu/"USEC_FMT, &prefixlen, &tos, &priority, &table, &lifetime); if (r != 5) { @@ -2515,6 +2757,10 @@ static int link_carrier_gained(Link *link) { link_enter_failed(link); return r; } + + r = link_enter_set_addresses(link); + if (r < 0) + return r; } r = link_handle_bound_by_list(link); @@ -2562,7 +2808,6 @@ int link_carrier_reset(Link *link) { return 0; } - int link_update(Link *link, sd_netlink_message *m) { struct ether_addr mac; const char *ifname; @@ -2643,21 +2888,45 @@ int link_update(Link *link, sd_netlink_message *m) { } if (link->dhcp_client) { + const DUID *duid = link_duid(link); + r = sd_dhcp_client_set_mac(link->dhcp_client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); + + r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, + link->network->iaid, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DUID/IAID in DHCP client: %m"); } if (link->dhcp6_client) { + const DUID* duid = link_duid(link); + r = sd_dhcp6_client_set_mac(link->dhcp6_client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); + + r = sd_dhcp6_client_set_iaid(link->dhcp6_client, + link->network->iaid); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); + + r = sd_dhcp6_client_set_duid(link->dhcp6_client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m"); } } } @@ -2668,6 +2937,10 @@ int link_update(Link *link, sd_netlink_message *m) { if (r < 0) return r; + r = link_update_lldp(link); + if (r < 0) + return r; + carrier_gained = !had_carrier && link_has_carrier(link); carrier_lost = had_carrier && !link_has_carrier(link); @@ -2683,12 +2956,34 @@ int link_update(Link *link, sd_netlink_message *m) { r = link_carrier_lost(link); if (r < 0) return r; - } return 0; } +static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) { + bool space = false; + Iterator i; + Link *link; + + assert(f); + assert(prefix); + + if (hashmap_isempty(h)) + return; + + fputs(prefix, f); + HASHMAP_FOREACH(link, h, i) { + if (space) + fputc(' ', f); + + fprintf(f, "%i", link->ifindex); + space = true; + } + + fputc('\n', f); +} + int link_save(Link *link) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -2708,6 +3003,8 @@ int link_save(Link *link) { return 0; } + link_lldp_save(link); + admin_state = link_state_to_string(link->state); assert(admin_state); @@ -2887,27 +3184,8 @@ int link_save(Link *link) { fputc('\n', f); } - if (!hashmap_isempty(link->bound_to_links)) { - Link *carrier; - bool space = false; - - fputs("CARRIER_BOUND_TO=", f); - HASHMAP_FOREACH(carrier, link->bound_to_links, i) - fputs_with_space(f, carrier->ifname, NULL, &space); - - fputc('\n', f); - } - - if (!hashmap_isempty(link->bound_by_links)) { - Link *carrier; - bool space = false; - - fputs("CARRIER_BOUND_BY=", f); - HASHMAP_FOREACH(carrier, link->bound_by_links, i) - fputs_with_space(f, carrier->ifname, NULL, &space); - - fputc('\n', f); - } + print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links); + print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links); if (link->dhcp_lease) { struct in_addr address; @@ -2947,19 +3225,6 @@ int link_save(Link *link) { } } - if (link->lldp) { - assert(link->network); - - r = sd_lldp_save(link->lldp, link->lldp_file); - if (r < 0) - goto fail; - - fprintf(f, - "LLDP_FILE=%s\n", - link->lldp_file); - } else - unlink(link->lldp_file); - r = fflush_and_check(f); if (r < 0) goto fail; @@ -2985,14 +3250,17 @@ void link_dirty(Link *link) { assert(link); + /* mark manager dirty as link is dirty */ + manager_dirty(link->manager); + r = set_ensure_allocated(&link->manager->dirty_links, NULL); if (r < 0) /* allocation errors are ignored */ return; r = set_put(link->manager->dirty_links, link); - if (r < 0) - /* allocation errors are ignored */ + if (r <= 0) + /* don't take another ref if the link was already dirty */ return; link_ref(link); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 30f01faf2a..90cb9b93f6 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -21,14 +21,17 @@ #include <endian.h> +#include <systemd/sd-bus.h> #include <systemd/sd-dhcp-client.h> #include <systemd/sd-dhcp-server.h> #include <systemd/sd-dhcp6-client.h> #include <systemd/sd-ipv4ll.h> #include <systemd/sd-lldp.h> #include <systemd/sd-ndisc.h> +#include <systemd/sd-netlink.h> -typedef struct Link Link; +#include "list.h" +#include "set.h" typedef enum LinkState { LINK_STATE_PENDING, @@ -54,17 +57,18 @@ typedef enum LinkOperationalState { _LINK_OPERSTATE_INVALID = -1 } LinkOperationalState; -#include "networkd-address.h" -#include "networkd-network.h" -#include "networkd.h" +typedef struct Manager Manager; +typedef struct Network Network; +typedef struct Address Address; -struct Link { +typedef struct Link { Manager *manager; int n_ref; int ifindex; char *ifname; + unsigned short iftype; char *state_file; struct ether_addr mac; struct in6_addr ipv6ll_address; @@ -111,12 +115,17 @@ struct Link { sd_dhcp6_client *dhcp6_client; bool rtnl_extended_attrs; + /* This is about LLDP reception */ sd_lldp *lldp; char *lldp_file; + /* This is about LLDP transmission */ + unsigned lldp_tx_fast; /* The LLDP txFast counter (See 802.1ab-2009, section 9.2.5.18) */ + sd_event_source *lldp_emit_event_source; + Hashmap *bound_by_links; Hashmap *bound_to_links; -}; +} Link; Link *link_unref(Link *link); Link *link_ref(Link *link); @@ -154,14 +163,6 @@ int dhcp6_configure(Link *link); int dhcp6_request_address(Link *link); int ndisc_configure(Link *link); -bool link_lldp_enabled(Link *link); -bool link_ipv4ll_enabled(Link *link); -bool link_ipv6ll_enabled(Link *link); -bool link_dhcp4_server_enabled(Link *link); -bool link_dhcp4_enabled(Link *link); -bool link_dhcp6_enabled(Link *link); -bool link_ipv6_accept_ra_enabled(Link *link); - const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c new file mode 100644 index 0000000000..3aa768388b --- /dev/null +++ b/src/network/networkd-lldp-tx.c @@ -0,0 +1,416 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <endian.h> +#include <inttypes.h> +#include <string.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "hostname-util.h" +#include "networkd-lldp-tx.h" +#include "networkd.h" +#include "parse-util.h" +#include "random-util.h" +#include "socket-util.h" +#include "string-util.h" +#include "unaligned.h" + +/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ +#define LLDP_TX_FAST_INIT 4U + +/* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */ +#define LLDP_TX_HOLD 4U + +/* The jitter range to add, see 9.2.2. */ +#define LLDP_JITTER_USEC (400U * USEC_PER_MSEC) + +/* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */ +#define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) + +/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */ +#define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) + +static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = { + [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }}, + [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }}, + [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }}, +}; + +static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) { + assert(p); + + if (id > 127) + return -EBADMSG; + if (sz > 511) + return -ENOBUFS; + + (*p)[0] = (id << 1) | !!(sz & 256); + (*p)[1] = sz & 255; + + *p = *p + 2; + return 0; +} + +static int lldp_make_packet( + LLDPEmit mode, + const struct ether_addr *hwaddr, + const char *machine_id, + const char *ifname, + uint16_t ttl, + const char *port_description, + const char *hostname, + const char *pretty_hostname, + uint16_t system_capabilities, + uint16_t enabled_capabilities, + void **ret, size_t *sz) { + + size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, pretty_hostname_length = 0; + _cleanup_free_ void *packet = NULL; + struct ether_header *h; + uint8_t *p; + size_t l; + int r; + + assert(mode > LLDP_EMIT_NO); + assert(mode < _LLDP_EMIT_MAX); + assert(hwaddr); + assert(machine_id); + assert(ifname); + assert(ret); + assert(sz); + + machine_id_length = strlen(machine_id); + ifname_length = strlen(ifname); + + if (port_description) + port_description_length = strlen(port_description); + + if (hostname) + hostname_length = strlen(hostname); + + if (pretty_hostname) + pretty_hostname_length = strlen(pretty_hostname); + + l = sizeof(struct ether_header) + + /* Chassis ID */ + 2 + 1 + machine_id_length + + /* Port ID */ + 2 + 1 + ifname_length + + /* TTL */ + 2 + 2 + + /* System Capabilities */ + 2 + 4 + + /* End */ + 2; + + /* Port Description */ + if (port_description) + l += 2 + port_description_length; + + /* System Name */ + if (hostname) + l += 2 + hostname_length; + + /* System Description */ + if (pretty_hostname) + l += 2 + pretty_hostname_length; + + packet = malloc(l); + if (!packet) + return -ENOMEM; + + h = (struct ether_header*) packet; + h->ether_type = htobe16(ETHERTYPE_LLDP); + memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN); + memcpy(h->ether_shost, hwaddr, ETH_ALEN); + + p = (uint8_t*) packet + sizeof(struct ether_header); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length); + if (r < 0) + return r; + *(p++) = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED; + p = mempcpy(p, machine_id, machine_id_length); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_ID, 1 + ifname_length); + if (r < 0) + return r; + *(p++) = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME; + p = mempcpy(p, ifname, ifname_length); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_TTL, 2); + if (r < 0) + return r; + unaligned_write_be16(p, ttl); + p += 2; + + if (port_description) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_DESCRIPTION, port_description_length); + if (r < 0) + return r; + p = mempcpy(p, port_description, port_description_length); + } + + if (hostname) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_NAME, hostname_length); + if (r < 0) + return r; + p = mempcpy(p, hostname, hostname_length); + } + + if (pretty_hostname) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, pretty_hostname_length); + if (r < 0) + return r; + p = mempcpy(p, pretty_hostname, pretty_hostname_length); + } + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); + if (r < 0) + return r; + unaligned_write_be16(p, system_capabilities); + p += 2; + unaligned_write_be16(p, enabled_capabilities); + p += 2; + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_END, 0); + if (r < 0) + return r; + + assert(p == (uint8_t*) packet + l); + + *ret = packet; + *sz = l; + + packet = NULL; + return 0; +} + +static int lldp_send_packet( + int ifindex, + const struct ether_addr *address, + const void *packet, + size_t packet_size) { + + union sockaddr_union sa = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htobe16(ETHERTYPE_LLDP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + }; + + _cleanup_close_ int fd = -1; + ssize_t l; + + assert(ifindex > 0); + assert(address); + assert(packet || packet_size <= 0); + + memcpy(sa.ll.sll_addr, address, ETH_ALEN); + + fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW); + if (fd < 0) + return -errno; + + l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll)); + if (l < 0) + return -errno; + + if ((size_t) l != packet_size) + return -EIO; + + return 0; +} + +static int link_send_lldp(Link *link) { + char machine_id_string[SD_ID128_STRING_MAX]; + _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL; + _cleanup_free_ void *packet = NULL; + size_t packet_size = 0; + sd_id128_t machine_id; + uint16_t caps; + usec_t ttl; + int r; + + assert(link); + + if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) + return 0; + + assert(link->network->lldp_emit < _LLDP_EMIT_MAX); + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + (void) gethostname_strict(&hostname); + (void) parse_env_file("/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL); + + assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC); + ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC); + + caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ? + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : + SD_LLDP_SYSTEM_CAPABILITIES_STATION; + + r = lldp_make_packet(link->network->lldp_emit, + &link->mac, + sd_id128_to_string(machine_id, machine_id_string), + link->ifname, + (uint16_t) ttl, + link->network ? link->network->description : NULL, + hostname, + pretty_hostname, + SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER, + caps, + &packet, &packet_size); + if (r < 0) + return r; + + return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size); +} + +static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) { + Link *link = userdata; + usec_t current, delay, next; + int r; + + assert(s); + assert(userdata); + + log_link_debug(link, "Sending LLDP packet..."); + + r = link_send_lldp(link); + if (r < 0) + log_link_debug_errno(link, r, "Failed to send LLDP packet, ignoring: %m"); + + if (link->lldp_tx_fast > 0) + link->lldp_tx_fast--; + + assert_se(sd_event_now(sd_event_source_get_event(s), clock_boottime_or_monotonic(), ¤t) >= 0); + + delay = link->lldp_tx_fast > 0 ? LLDP_FAST_TX_USEC : LLDP_TX_INTERVAL_USEC; + next = usec_add(usec_add(current, delay), (usec_t) random_u64() % LLDP_JITTER_USEC); + + r = sd_event_source_set_time(s, next); + if (r < 0) + return log_link_error_errno(link, r, "Failed to restart LLDP timer: %m"); + + r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); + if (r < 0) + return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m"); + + return 0; +} + +int link_lldp_emit_start(Link *link) { + usec_t next; + int r; + + assert(link); + + if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) { + link_lldp_emit_stop(link); + return 0; + } + + /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */ + + link->lldp_tx_fast = LLDP_TX_FAST_INIT; + + next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC), + (usec_t) random_u64() % LLDP_JITTER_USEC); + + if (link->lldp_emit_event_source) { + usec_t old; + + /* Lower the timeout, maybe */ + r = sd_event_source_get_time(link->lldp_emit_event_source, &old); + if (r < 0) + return r; + + if (old <= next) + return 0; + + return sd_event_source_set_time(link->lldp_emit_event_source, next); + } else { + r = sd_event_add_time( + link->manager->event, + &link->lldp_emit_event_source, + clock_boottime_or_monotonic(), + next, + 0, + on_lldp_timer, + link); + if (r < 0) + return r; + + (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx"); + } + + return 0; +} + +void link_lldp_emit_stop(Link *link) { + assert(link); + + link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source); +} + +int config_parse_lldp_emit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + LLDPEmit *emit = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) + *emit = LLDP_EMIT_NO; + else if (streq(rvalue, "nearest-bridge")) + *emit = LLDP_EMIT_NEAREST_BRIDGE; + else if (streq(rvalue, "non-tpmr-bridge")) + *emit = LLDP_EMIT_NON_TPMR_BRIDGE; + else if (streq(rvalue, "customer-bridge")) + *emit = LLDP_EMIT_CUSTOMER_BRIDGE; + else { + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue); + return 0; + } + + *emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO; + } + + return 0; +} diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h new file mode 100644 index 0000000000..4680c9d950 --- /dev/null +++ b/src/network/networkd-lldp-tx.h @@ -0,0 +1,35 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "networkd-link.h" + +typedef enum LLDPEmit { + LLDP_EMIT_NO, + LLDP_EMIT_NEAREST_BRIDGE, + LLDP_EMIT_NON_TPMR_BRIDGE, + LLDP_EMIT_CUSTOMER_BRIDGE, + _LLDP_EMIT_MAX, +} LLDPEmit; + +int link_lldp_emit_start(Link *link); +void link_lldp_emit_stop(Link *link); + +int config_parse_lldp_emit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index c73d313c85..7af7abaa81 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -457,11 +457,9 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo break; case RTM_DELROUTE: - - if (route) - route_drop(route); - + route_free(route); break; + default: assert_not_reached("Received invalid RTNL message type"); } @@ -1037,6 +1035,8 @@ int manager_new(Manager **ret) { if (r < 0) return r; + m->duid.type = DUID_TYPE_EN; + *ret = m; m = NULL; @@ -1091,22 +1091,19 @@ static bool manager_check_idle(void *userdata) { assert(m); + /* Check whether we are idle now. The only case when we decide to be idle is when there's only a loopback + * device around, for which we have no configuration, and which already left the PENDING state. In all other + * cases we are not idle. */ + HASHMAP_FOREACH(link, m->links, i) { - /* we are not woken on udev activity, so let's just wait for the - * pending udev event */ + /* We are not woken on udev activity, so let's just wait for the pending udev event */ if (link->state == LINK_STATE_PENDING) return false; - if (!link->network) - continue; + if ((link->flags & IFF_LOOPBACK) == 0) + return false; - /* we are not woken on netork activity, so let's stay around */ - if (link_lldp_enabled(link) || - link_ipv4ll_enabled(link) || - link_dhcp4_server_enabled(link) || - link_dhcp4_enabled(link) || - link_dhcp6_enabled(link) || - link_ipv6_accept_ra_enabled(link)) + if (link->network) return false; } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index ae647b0293..f3a4fc0fa5 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -19,11 +19,12 @@ #include <netinet/ether.h> #include <netinet/icmp6.h> +#include <netinet/in.h> #include <linux/if.h> #include <systemd/sd-ndisc.h> -#include "networkd-link.h" +#include "networkd.h" static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; @@ -32,7 +33,7 @@ static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void * assert(link); assert(link->ndisc_messages > 0); - link->ndisc_messages --; + link->ndisc_messages--; r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -76,15 +77,15 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8); else { /* see RFC4291 section 2.5.1 */ - address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0]; - address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1; - address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1]; - address->in_addr.in6.__in6_u.__u6_addr8[10] = link->mac.ether_addr_octet[2]; - address->in_addr.in6.__in6_u.__u6_addr8[11] = 0xff; - address->in_addr.in6.__in6_u.__u6_addr8[12] = 0xfe; - address->in_addr.in6.__in6_u.__u6_addr8[13] = link->mac.ether_addr_octet[3]; - address->in_addr.in6.__in6_u.__u6_addr8[14] = link->mac.ether_addr_octet[4]; - address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5]; + address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0]; + address->in_addr.in6.s6_addr[8] ^= 1 << 1; + address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1]; + address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2]; + address->in_addr.in6.s6_addr[11] = 0xff; + address->in_addr.in6.s6_addr[12] = 0xfe; + address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3]; + address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4]; + address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5]; } address->prefixlen = prefixlen; address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; @@ -98,7 +99,7 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr return; } - link->ndisc_messages ++; + link->ndisc_messages++; } static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) { @@ -136,7 +137,7 @@ static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *pre return; } - link->ndisc_messages ++; + link->ndisc_messages++; } static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { @@ -156,6 +157,10 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a if (flags & ND_RA_FLAG_MANAGED) dhcp6_request_address(link); + r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); + if (r < 0 && r != -EBUSY) + log_link_warning_errno(link, r, "Could not set IPv6LL address in DHCP client: %m"); + r = sd_dhcp6_client_start(link->dhcp6_client); if (r < 0 && r != -EBUSY) log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); @@ -186,7 +191,7 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a return; } - link->ndisc_messages ++; + link->ndisc_messages++; } static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { @@ -202,6 +207,10 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { case SD_NDISC_EVENT_TIMEOUT: dhcp6_request_address(link); + r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); + if (r < 0 && r != -EBUSY) + log_link_warning_errno(link, r, "Could not set IPv6LL address in DHCP client: %m"); + r = sd_dhcp6_client_start(link->dhcp6_client); if (r < 0 && r != -EBUSY) log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m"); diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c index e04e78f870..7005b165d9 100644 --- a/src/network/networkd-netdev-bond.c +++ b/src/network/networkd-netdev-bond.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "conf-parser.h" +#include "extract-word.h" #include "missing.h" #include "networkd-netdev-bond.h" #include "string-table.h" @@ -375,7 +376,7 @@ int config_parse_arp_ip_target_address(const char *unit, } LIST_PREPEND(arp_ip_target, b->arp_ip_targets, buffer); - b->n_arp_ip_targets ++; + b->n_arp_ip_targets++; buffer = NULL; } diff --git a/src/network/networkd-netdev-bond.h b/src/network/networkd-netdev-bond.h index cb6baea24f..b941edb344 100644 --- a/src/network/networkd-netdev-bond.h +++ b/src/network/networkd-netdev-bond.h @@ -20,8 +20,7 @@ ***/ #include "in-addr-util.h" - -typedef struct Bond Bond; +#include "list.h" #include "networkd-netdev.h" @@ -106,7 +105,7 @@ typedef struct ArpIpTarget { LIST_FIELDS(struct ArpIpTarget, arp_ip_target); } ArpIpTarget; -struct Bond { +typedef struct Bond { NetDev meta; BondMode mode; @@ -133,8 +132,9 @@ struct Bond { int n_arp_ip_targets; ArpIpTarget *arp_ip_targets; -}; +} Bond; +DEFINE_NETDEV_CAST(BOND, Bond); extern const NetDevVTable bond_vtable; const char *bond_mode_to_string(BondMode d) _const_; diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c index cdcd08f057..4cfd00413f 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -22,6 +22,7 @@ #include "missing.h" #include "netlink-util.h" +#include "networkd.h" #include "networkd-netdev-bridge.h" /* callback for brige netdev's parameter set */ @@ -89,6 +90,18 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MAX_AGE attribute: %m"); } + if (b->mcast_querier >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_QUERIER, b->mcast_querier); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_QUERIER attribute: %m"); + } + + if (b->mcast_snooping >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_SNOOPING, b->mcast_snooping); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_SNOOPING attribute: %m"); + } + r = sd_netlink_message_close_container(req); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); @@ -106,8 +119,20 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return r; } +static void bridge_init(NetDev *n) { + Bridge *b; + + b = BRIDGE(n); + + assert(b); + + b->mcast_querier = -1; + b->mcast_snooping = -1; +} + const NetDevVTable bridge_vtable = { .object_size = sizeof(Bridge), + .init = bridge_init, .sections = "Match\0NetDev\0Bridge\0", .post_create = netdev_bridge_post_create, .create_type = NETDEV_CREATE_MASTER, diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h index b2bf7e15f1..f2ae21fc50 100644 --- a/src/network/networkd-netdev-bridge.h +++ b/src/network/networkd-netdev-bridge.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,18 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - -typedef struct Bridge Bridge; - #include "networkd-netdev.h" -struct Bridge { +typedef struct Bridge { NetDev meta; + int mcast_querier; + int mcast_snooping; + usec_t forward_delay; usec_t hello_time; usec_t max_age; -}; +} Bridge; +DEFINE_NETDEV_CAST(BRIDGE, Bridge); extern const NetDevVTable bridge_vtable; diff --git a/src/network/networkd-netdev-dummy.h b/src/network/networkd-netdev-dummy.h index 29f75a149b..efe302267e 100644 --- a/src/network/networkd-netdev-dummy.h +++ b/src/network/networkd-netdev-dummy.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,14 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - -typedef struct Dummy Dummy; - #include "networkd-netdev.h" -struct Dummy { +typedef struct Dummy { NetDev meta; -}; +} Dummy; +DEFINE_NETDEV_CAST(DUMMY, Dummy); extern const NetDevVTable dummy_vtable; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 8f506af092..ba04bb0165 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -1,11 +1,17 @@ %{ #include <stddef.h> #include "conf-parser.h" -#include "networkd-netdev.h" -#include "networkd-netdev-tunnel.h" +#include "network-internal.h" #include "networkd-netdev-bond.h" +#include "networkd-netdev-ipvlan.h" #include "networkd-netdev-macvlan.h" -#include "network-internal.h" +#include "networkd-netdev-tunnel.h" +#include "networkd-netdev-tuntap.h" +#include "networkd-netdev-veth.h" +#include "networkd-netdev-vlan.h" +#include "networkd-netdev-vxlan.h" +#include "networkd-netdev-bridge.h" +#include "networkd-netdev.h" %} struct ConfigPerfItem; %null_strings @@ -92,3 +98,5 @@ Bond.LearnPacketIntervalSec, config_parse_sec, 0, Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) +Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) +Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) diff --git a/src/network/networkd-netdev-ipvlan.h b/src/network/networkd-netdev-ipvlan.h index 5b85ef2150..10d4079844 100644 --- a/src/network/networkd-netdev-ipvlan.h +++ b/src/network/networkd-netdev-ipvlan.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,10 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - -typedef struct IPVlan IPVlan; - #include "missing.h" #include "networkd-netdev.h" @@ -31,12 +29,13 @@ typedef enum IPVlanMode { _NETDEV_IPVLAN_MODE_INVALID = -1 } IPVlanMode; -struct IPVlan { +typedef struct IPVlan { NetDev meta; IPVlanMode mode; -}; +} IPVlan; +DEFINE_NETDEV_CAST(IPVLAN, IPVlan); extern const NetDevVTable ipvlan_vtable; const char *ipvlan_mode_to_string(IPVlanMode d) _const_; diff --git a/src/network/networkd-netdev-macvlan.h b/src/network/networkd-netdev-macvlan.h index 8b42684de6..3663f4f051 100644 --- a/src/network/networkd-netdev-macvlan.h +++ b/src/network/networkd-netdev-macvlan.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct MacVlan MacVlan; #include "networkd-netdev.h" @@ -38,6 +38,8 @@ struct MacVlan { MacVlanMode mode; }; +DEFINE_NETDEV_CAST(MACVLAN, MacVlan); +DEFINE_NETDEV_CAST(MACVTAP, MacVlan); extern const NetDevVTable macvlan_vtable; extern const NetDevVTable macvtap_vtable; diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c index 408ceaf55e..26a9a972f1 100644 --- a/src/network/networkd-netdev-tunnel.c +++ b/src/network/networkd-netdev-tunnel.c @@ -54,7 +54,7 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin assert(link); assert(m); assert(t); - assert(t->family == AF_INET || t->family != -1); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) @@ -87,7 +87,7 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink assert(link); assert(m); assert(t); - assert(t->family == AF_INET || t->family != -1); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) @@ -124,7 +124,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink t = GRETAP(netdev); assert(t); - assert(t->family == AF_INET || t->family != -1); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); assert(link); assert(m); @@ -497,7 +497,7 @@ static void ipip_init(NetDev *n) { assert(t); t->pmtudisc = true; - t->family = -1; + t->family = AF_UNSPEC; } static void sit_init(NetDev *n) { @@ -507,7 +507,7 @@ static void sit_init(NetDev *n) { assert(t); t->pmtudisc = true; - t->family = -1; + t->family = AF_UNSPEC; } static void vti_init(NetDev *n) { @@ -538,7 +538,7 @@ static void gre_init(NetDev *n) { assert(t); t->pmtudisc = true; - t->family = -1; + t->family = AF_UNSPEC; } static void ip6gre_init(NetDev *n) { diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h index ea1d9a79e7..7d31e7b687 100644 --- a/src/network/networkd-netdev-tunnel.h +++ b/src/network/networkd-netdev-tunnel.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,9 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - -typedef struct Tunnel Tunnel; +#include "in-addr-util.h" #include "networkd-netdev.h" @@ -37,7 +37,7 @@ typedef enum IPv6FlowLabel { _NETDEV_IPV6_FLOWLABEL_INVALID = -1, } IPv6FlowLabel; -struct Tunnel { +typedef struct Tunnel { NetDev meta; uint8_t encap_limit; @@ -56,8 +56,17 @@ struct Tunnel { bool pmtudisc; bool copy_dscp; -}; - +} Tunnel; + +DEFINE_NETDEV_CAST(IPIP, Tunnel); +DEFINE_NETDEV_CAST(GRE, Tunnel); +DEFINE_NETDEV_CAST(GRETAP, Tunnel); +DEFINE_NETDEV_CAST(IP6GRE, Tunnel); +DEFINE_NETDEV_CAST(IP6GRETAP, Tunnel); +DEFINE_NETDEV_CAST(SIT, Tunnel); +DEFINE_NETDEV_CAST(VTI, Tunnel); +DEFINE_NETDEV_CAST(VTI6, Tunnel); +DEFINE_NETDEV_CAST(IP6TNL, Tunnel); extern const NetDevVTable ipip_vtable; extern const NetDevVTable sit_vtable; extern const NetDevVTable vti_vtable; diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c index ab9a1b0426..088a4d8d32 100644 --- a/src/network/networkd-netdev-tuntap.c +++ b/src/network/networkd-netdev-tuntap.c @@ -17,9 +17,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <fcntl.h> +#include <linux/if_tun.h> #include <net/if.h> +#include <netinet/if_ether.h> #include <sys/ioctl.h> -#include <linux/if_tun.h> +#include <sys/stat.h> +#include <sys/types.h> #include "alloc-util.h" #include "fd-util.h" @@ -87,7 +91,7 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { assert(t); - if(t->user_name) { + if (t->user_name) { user = t->user_name; @@ -126,7 +130,7 @@ static int netdev_create_tuntap(NetDev *netdev) { int r; r = netdev_fill_tuntap_message(netdev, &ifr); - if(r < 0) + if (r < 0) return r; return netdev_tuntap_add(netdev, &ifr); diff --git a/src/network/networkd-netdev-tuntap.h b/src/network/networkd-netdev-tuntap.h index b970b0ce3b..120f00a353 100644 --- a/src/network/networkd-netdev-tuntap.h +++ b/src/network/networkd-netdev-tuntap.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct TunTap TunTap; #include "networkd-netdev.h" @@ -34,5 +34,7 @@ struct TunTap { bool vnet_hdr; }; +DEFINE_NETDEV_CAST(TUN, TunTap); +DEFINE_NETDEV_CAST(TAP, TunTap); extern const NetDevVTable tun_vtable; extern const NetDevVTable tap_vtable; diff --git a/src/network/networkd-netdev-veth.h b/src/network/networkd-netdev-veth.h index f7fdf906ab..e69bfbc8f0 100644 --- a/src/network/networkd-netdev-veth.h +++ b/src/network/networkd-netdev-veth.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct Veth Veth; #include "networkd-netdev.h" @@ -30,4 +30,5 @@ struct Veth { struct ether_addr *mac_peer; }; +DEFINE_NETDEV_CAST(VETH, Veth); extern const NetDevVTable veth_vtable; diff --git a/src/network/networkd-netdev-vlan.h b/src/network/networkd-netdev-vlan.h index 8701c4b785..73aacf4a0f 100644 --- a/src/network/networkd-netdev-vlan.h +++ b/src/network/networkd-netdev-vlan.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct VLan VLan; #include "networkd-netdev.h" @@ -31,4 +31,5 @@ struct VLan { uint64_t id; }; +DEFINE_NETDEV_CAST(VLAN, VLan); extern const NetDevVTable vlan_vtable; diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c index 223d60f4db..363a6bdde6 100644 --- a/src/network/networkd-netdev-vxlan.c +++ b/src/network/networkd-netdev-vxlan.c @@ -23,8 +23,10 @@ #include "conf-parser.h" #include "alloc-util.h" +#include "extract-word.h" #include "parse-util.h" #include "missing.h" + #include "networkd-link.h" #include "networkd-netdev-vxlan.h" @@ -54,13 +56,13 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LINK attribute: %m"); - if(v->ttl) { + if (v->ttl) { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL attribute: %m"); } - if(v->tos) { + if (v->tos) { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TOS, v->tos); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TOS attribute: %m"); @@ -86,7 +88,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L3MISS attribute: %m"); - if(v->fdb_ageing) { + if (v->fdb_ageing) { r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_AGEING attribute: %m"); diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h index 459ce53f5e..4614c66fd1 100644 --- a/src/network/networkd-netdev-vxlan.h +++ b/src/network/networkd-netdev-vxlan.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct VxLan VxLan; #include "in-addr-util.h" @@ -55,6 +55,7 @@ struct VxLan { struct ifla_vxlan_port_range port_range; }; +DEFINE_NETDEV_CAST(VXLAN, VxLan); extern const NetDevVTable vxlan_vtable; int config_parse_vxlan_group_address(const char *unit, diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index d7d014f05d..851a36290c 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -656,7 +656,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { if (!netdev->filename) return log_oom(); - if (!netdev->mac) { + if (!netdev->mac && netdev->kind != NETDEV_KIND_VLAN) { r = netdev_get_mac(netdev->ifname, &netdev->mac); if (r < 0) return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname); diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h index 3eacee824b..ab3f068167 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/networkd-netdev.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,17 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once +#include <systemd/sd-netlink.h> #include "list.h" - -typedef struct NetDev NetDev; -typedef struct NetDevVTable NetDevVTable; - -#include "networkd-link.h" -#include "networkd.h" +#include "time-util.h" typedef struct netdev_join_callback netdev_join_callback; +typedef struct Link Link; struct netdev_join_callback { sd_netlink_message_handler_t callback; @@ -78,7 +76,10 @@ typedef enum NetDevCreateType { _NETDEV_CREATE_INVALID = -1, } NetDevCreateType; -struct NetDev { +typedef struct Manager Manager; +typedef struct Condition Condition; + +typedef struct NetDev { Manager *manager; int n_ref; @@ -99,20 +100,9 @@ struct NetDev { int ifindex; LIST_HEAD(netdev_join_callback, callbacks); -}; +} NetDev; -#include "networkd-netdev-bond.h" -#include "networkd-netdev-bridge.h" -#include "networkd-netdev-dummy.h" -#include "networkd-netdev-ipvlan.h" -#include "networkd-netdev-macvlan.h" -#include "networkd-netdev-tunnel.h" -#include "networkd-netdev-tuntap.h" -#include "networkd-netdev-veth.h" -#include "networkd-netdev-vlan.h" -#include "networkd-netdev-vxlan.h" - -struct NetDevVTable { +typedef struct NetDevVTable { /* How much memory does an object of this unit type need */ size_t object_size; @@ -144,14 +134,14 @@ struct NetDevVTable { /* verify that compulsory configuration options were specified */ int (*config_verify)(NetDev *netdev, const char *filename); -}; +} NetDevVTable; extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; #define NETDEV_VTABLE(n) netdev_vtable[(n)->kind] /* For casting a netdev into the various netdev kinds */ -#define DEFINE_CAST(UPPERCASE, MixedCase) \ +#define DEFINE_NETDEV_CAST(UPPERCASE, MixedCase) \ static inline MixedCase* UPPERCASE(NetDev *n) { \ if (_unlikely_(!n || n->kind != NETDEV_KIND_##UPPERCASE)) \ return NULL; \ @@ -162,27 +152,6 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; /* For casting the various netdev kinds into a netdev */ #define NETDEV(n) (&(n)->meta) -DEFINE_CAST(BRIDGE, Bridge); -DEFINE_CAST(BOND, Bond); -DEFINE_CAST(VLAN, VLan); -DEFINE_CAST(MACVLAN, MacVlan); -DEFINE_CAST(MACVTAP, MacVlan); -DEFINE_CAST(IPVLAN, IPVlan); -DEFINE_CAST(VXLAN, VxLan); -DEFINE_CAST(IPIP, Tunnel); -DEFINE_CAST(GRE, Tunnel); -DEFINE_CAST(GRETAP, Tunnel); -DEFINE_CAST(IP6GRE, Tunnel); -DEFINE_CAST(IP6GRETAP, Tunnel); -DEFINE_CAST(SIT, Tunnel); -DEFINE_CAST(VTI, Tunnel); -DEFINE_CAST(VTI6, Tunnel); -DEFINE_CAST(IP6TNL, Tunnel); -DEFINE_CAST(VETH, Veth); -DEFINE_CAST(DUMMY, Dummy); -DEFINE_CAST(TUN, TunTap); -DEFINE_CAST(TAP, TunTap); - int netdev_load(Manager *manager); void netdev_drop(NetDev *netdev); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 409df1709f..03e4e3b39f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -2,6 +2,7 @@ #include <stddef.h> #include "conf-parser.h" #include "networkd.h" +#include "networkd-conf.h" #include "network-internal.h" %} struct ConfigPerfItem; @@ -40,7 +41,8 @@ Network.DHCPServer, config_parse_bool, Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local) Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token) -Network.LLDP, config_parse_bool, 0, offsetof(Network, lldp) +Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) +Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) Network.Address, config_parse_address, 0, 0 Network.Gateway, config_parse_gateway, 0, 0 Network.Domains, config_parse_domains, 0, 0 @@ -56,17 +58,20 @@ Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra) Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits) Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit) +Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) 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 Address.Label, config_parse_label, 0, 0 +Address.PreferredLifetime, config_parse_lifetime, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 Route.Source, config_parse_destination, 0, 0 Route.Metric, config_parse_route_priority, 0, 0 Route.Scope, config_parse_route_scope, 0, 0 Route.PreferredSource, config_parse_preferred_src, 0, 0 +Route.Table, config_parse_route_table, 0, 0 DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) @@ -79,14 +84,18 @@ DHCP.Hostname, config_parse_hostname, DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid.type) +DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) +DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid) DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0 DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp) DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 +DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router) DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 4315790093..dd89b3770c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -113,12 +113,15 @@ static int network_load_one(Manager *manager, const char *filename) { network->dhcp_server_emit_dns = true; network->dhcp_server_emit_ntp = true; + network->dhcp_server_emit_router = true; network->dhcp_server_emit_timezone = true; network->use_bpdu = true; network->allow_port_to_be_root = true; network->unicast_flood = true; + network->lldp_mode = LLDP_MODE_ROUTERS_ONLY; + network->llmnr = RESOLVE_SUPPORT_YES; network->mdns = RESOLVE_SUPPORT_NO; network->dnssec_mode = _DNSSEC_MODE_INVALID; @@ -129,6 +132,8 @@ static int network_load_one(Manager *manager, const char *filename) { network->ipv6_accept_ra = -1; network->ipv6_dad_transmits = -1; network->ipv6_hop_limit = -1; + network->duid.type = _DUID_TYPE_INVALID; + network->proxy_arp = -1; r = config_parse(NULL, filename, file, "Match\0" @@ -394,6 +399,19 @@ int network_apply(Manager *manager, Network *network, Link *link) { return 0; } +bool network_has_static_ipv6_addresses(Network *network) { + Address *address; + + assert(network); + + LIST_FOREACH(addresses, address, network->static_addresses) { + if (address->family == AF_INET6) + return true; + } + + return false; +} + int config_parse_netdev(const char *unit, const char *filename, unsigned line, @@ -627,10 +645,7 @@ int config_parse_ipv4ll( * config_parse_address_family_boolean(), except that it * applies only to IPv4 */ - if (parse_boolean(rvalue)) - *link_local |= ADDRESS_FAMILY_IPV4; - else - *link_local &= ~ADDRESS_FAMILY_IPV4; + SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue)); return 0; } @@ -994,6 +1009,10 @@ int config_parse_dnssec_negative_trust_anchors( continue; } + r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops); + if (r < 0) + return log_oom(); + r = set_put(n->dnssec_negative_trust_anchors, w); if (r < 0) return log_oom(); @@ -1013,3 +1032,13 @@ static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES); + +DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting."); + +static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { + [LLDP_MODE_NO] = "no", + [LLDP_MODE_YES] = "yes", + [LLDP_MODE_ROUTERS_ONLY] = "routers-only", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 03c3f206c3..177bc11ec4 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -19,17 +19,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <systemd/sd-bus.h> +#include "udev.h" + #include "condition.h" +#include "dhcp-identifier.h" +#include "hashmap.h" #include "resolve-util.h" -typedef struct Network Network; - #include "networkd-address.h" #include "networkd-fdb.h" +#include "networkd-lldp-tx.h" #include "networkd-netdev.h" #include "networkd-route.h" #include "networkd-util.h" -#include "networkd.h" #define DHCP_ROUTE_METRIC 1024 #define IPV4LL_ROUTE_METRIC 2048 @@ -58,6 +61,24 @@ typedef enum DHCPUseDomains { _DHCP_USE_DOMAINS_INVALID = -1, } DHCPUseDomains; +typedef enum LLDPMode { + LLDP_MODE_NO = 0, + LLDP_MODE_YES = 1, + LLDP_MODE_ROUTERS_ONLY = 2, + _LLDP_MODE_MAX, + _LLDP_MODE_INVALID = -1, +} LLDPMode; + +typedef struct DUID { + /* Value of Type in [DHCP] section */ + DUIDType type; + + uint8_t raw_data_len; + uint8_t raw_data[MAX_DUID_LEN]; +} DUID; + +typedef struct Manager Manager; + struct Network { Manager *manager; @@ -106,6 +127,7 @@ struct Network { bool dhcp_server_emit_ntp; struct in_addr *dhcp_server_ntp; unsigned n_dhcp_server_ntp; + bool dhcp_server_emit_router; bool dhcp_server_emit_timezone; char *dhcp_server_timezone; usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec; @@ -130,14 +152,18 @@ struct Network { int ipv6_accept_ra; int ipv6_dad_transmits; int ipv6_hop_limit; + int proxy_arp; union in_addr_union ipv6_token; IPv6PrivacyExtensions ipv6_privacy_extensions; struct ether_addr *mac; unsigned mtu; + uint32_t iaid; + DUID duid; - bool lldp; + LLDPMode lldp_mode; /* LLDP reception */ + LLDPEmit lldp_emit; /* LLDP transmission */ LIST_HEAD(Address, static_addresses); LIST_HEAD(Route, static_routes); @@ -168,6 +194,8 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret); int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret); int network_apply(Manager *manager, Network *network, Link *link); +bool network_has_static_ipv6_addresses(Network *network); + int config_parse_netdev(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tunnel(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -181,6 +209,7 @@ int config_parse_dhcp_server_dns(const char *unit, const char *filename, unsigne int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_dnssec_negative_trust_anchors(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_dhcp_use_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_lldp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* Legacy IPv4LL support */ int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -197,3 +226,6 @@ IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_; DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_; + +const char* lldp_mode_to_string(LLDPMode m) _const_; +LLDPMode lldp_mode_from_string(const char *s) _pure_; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index e065a5a5a9..f001de772a 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -52,8 +52,7 @@ int route_new_static(Network *network, unsigned section, Route **ret) { int r; if (section) { - route = hashmap_get(network->routes_by_section, - UINT_TO_PTR(section)); + route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section)); if (route) { *ret = route; route = NULL; @@ -67,16 +66,18 @@ int route_new_static(Network *network, unsigned section, Route **ret) { return r; route->protocol = RTPROT_STATIC; - route->network = network; - - LIST_PREPEND(routes, network->static_routes, route); if (section) { route->section = section; - hashmap_put(network->routes_by_section, - UINT_TO_PTR(route->section), route); + + r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route); + if (r < 0) + return r; } + route->network = network; + LIST_PREPEND(routes, network->static_routes, route); + *ret = route; route = NULL; @@ -323,12 +324,6 @@ int route_update(Route *route, return 0; } -void route_drop(Route *route) { - assert(route); - - route_free(route); -} - int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; @@ -374,7 +369,7 @@ int route_remove(Route *route, Link *link, else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6); if (r < 0) - return log_error_errno(r, "Could not append RTA_DST attribute: %m"); + return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); if (r < 0) @@ -411,15 +406,45 @@ int route_remove(Route *route, Link *link, return 0; } +static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + Link *link = userdata; + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->ifname); + assert(link->link_messages > 0); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + link->link_messages--; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_warning_errno(link, r, "could not remove route: %m"); + + if (link->link_messages == 0) + log_link_debug(link, "route removed"); + + return 1; +} + int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { Route *route = userdata; int r; assert(route); - r = route_remove(route, route->link, NULL); + r = route_remove(route, route->link, route_expire_callback); if (r < 0) log_warning_errno(r, "Could not remove route: %m"); + else { + /* route may not be exist in kernel. If we fail still remove it */ + route->link->link_messages++; + route_free(route); + } return 1; } @@ -450,6 +475,10 @@ int route_configure(Route *route, Link *link, r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); + + r = sd_rtnl_message_route_set_family(req, route->family); + if (r < 0) + return log_error_errno(r, "Could not set route family: %m"); } if (route->dst_prefixlen) { @@ -493,7 +522,26 @@ int route_configure(Route *route, Link *link, r = sd_rtnl_message_route_set_flags(req, route->flags); if (r < 0) - return log_error_errno(r, "Colud not set flags: %m"); + return log_error_errno(r, "Could not set flags: %m"); + + if (route->table != RT_TABLE_DEFAULT) { + + if (route->table < 256) { + r = sd_rtnl_message_route_set_table(req, route->table); + if (r < 0) + return log_error_errno(r, "Could not set route table: %m"); + } else { + + r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); + if (r < 0) + return log_error_errno(r, "Could not set route table: %m"); + + /* Table attribute to allow more than 256. */ + r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table)); + if (r < 0) + return log_error_errno(r, "Could not append RTA_TABLE attribute: %m"); + } + } r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) @@ -776,3 +824,42 @@ int config_parse_route_scope(const char *unit, return 0; } + +int config_parse_route_table(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + _cleanup_route_free_ Route *n = NULL; + Network *network = userdata; + uint32_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, section_line, &n); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + + n->table = k; + + n = NULL; + + return 0; +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index a4a4bf2653..39de8363ed 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -22,7 +22,6 @@ typedef struct Route Route; #include "networkd-network.h" -#include "networkd.h" struct Route { Network *network; @@ -37,7 +36,7 @@ struct Route { unsigned char protocol; /* RTPROT_* */ unsigned char tos; uint32_t priority; /* note that ip(8) calls this 'metric' */ - unsigned char table; + uint32_t table; unsigned char pref; unsigned flags; @@ -62,7 +61,6 @@ int route_get(Link *link, int family, union in_addr_union *dst, unsigned char ds int route_add(Link *link, int family, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret); int route_add_foreign(Link *link, int family, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret); int route_update(Route *route, union in_addr_union *src, unsigned char src_prefixlen, union in_addr_union *gw, union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol); -void route_drop(Route *route); int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata); @@ -74,3 +72,4 @@ int config_parse_preferred_src(const char *unit, const char *filename, unsigned int config_parse_destination(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_route_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_route_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-wait-online.h b/src/network/networkd-wait-online.h index f1ea4a6494..7ac7f4018a 100644 --- a/src/network/networkd-wait-online.h +++ b/src/network/networkd-wait-online.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <systemd/sd-event.h> #include <systemd/sd-netlink.h> #include <systemd/sd-network.h> diff --git a/src/network/networkd.c b/src/network/networkd.c index 6cf396e478..9f5c75ac3d 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -21,6 +21,7 @@ #include "capability-util.h" #include "networkd.h" +#include "networkd-conf.h" #include "signal-util.h" #include "user-util.h" @@ -89,6 +90,10 @@ int main(int argc, char *argv[]) { goto out; } + r = manager_parse_config_file(m); + if (r < 0) + log_warning_errno(r, "Failed to parse configuration file: %m"); + r = manager_load_config(m); if (r < 0) { log_error_errno(r, "Could not load configuration files: %m"); diff --git a/src/network/networkd.h b/src/network/networkd.h index efe70970c3..b61e03920e 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,25 +19,34 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <arpa/inet.h> #include <systemd/sd-bus.h> #include <systemd/sd-event.h> #include <systemd/sd-netlink.h> +#include "udev.h" +#include "dhcp-identifier.h" #include "hashmap.h" #include "list.h" -#include "udev.h" - -typedef struct Manager Manager; #include "networkd-address-pool.h" #include "networkd-link.h" +#include "networkd-netdev-bond.h" +#include "networkd-netdev-bridge.h" +#include "networkd-netdev-dummy.h" +#include "networkd-netdev-ipvlan.h" +#include "networkd-netdev-macvlan.h" +#include "networkd-netdev-tunnel.h" +#include "networkd-netdev-tuntap.h" +#include "networkd-netdev-veth.h" +#include "networkd-netdev-vlan.h" +#include "networkd-netdev-vxlan.h" #include "networkd-network.h" #include "networkd-util.h" +extern const char* const network_dirs[]; + struct Manager { sd_netlink *rtnl; sd_event *event; @@ -61,11 +72,16 @@ struct Manager { LIST_HEAD(AddressPool, address_pools); usec_t network_dirs_ts_usec; -}; -extern const char* const network_dirs[]; + DUID duid; +}; -/* Manager */ +static inline const DUID* link_duid(const Link *link) { + if (link->network->duid.type != _DUID_TYPE_INVALID) + return &link->network->duid; + else + return &link->manager->duid; +} extern const sd_bus_vtable manager_vtable[]; diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c index ecbbe6c3c9..adbe09a5e1 100644 --- a/src/network/test-network-tables.c +++ b/src/network/test-network-tables.c @@ -9,7 +9,7 @@ int main(int argc, char **argv) { test_table(bond_mode, NETDEV_BOND_MODE); - /* test_table(link_state, LINK_STATE); -- not a reversible mapping */ + /* test_table(link_state, LINK_STATE); — not a reversible mapping */ test_table(link_operstate, LINK_OPERSTATE); test_table(address_family_boolean, ADDRESS_FAMILY_BOOLEAN); test_table(netdev_kind, NETDEV_KIND); diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c new file mode 100644 index 0000000000..0e1a18457d --- /dev/null +++ b/src/network/test-networkd-conf.c @@ -0,0 +1,142 @@ +/*** + This file is part of systemd. + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "hexdecoct.h" +#include "log.h" +#include "macro.h" +#include "string-util.h" +#include "ether-addr-util.h" + +#include "networkd-conf.h" +#include "networkd-network.h" +#include "network-internal.h" + +static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected) { + DUIDType actual = 0; + int r; + + r = config_parse_duid_type("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + log_info_errno(r, "\"%s\" → %d (%m)", rvalue, actual); + assert_se(r == ret); + assert_se(expected == actual); +} + +static void test_config_parse_duid_type(void) { + test_config_parse_duid_type_one("", 0, 0); + test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT); + test_config_parse_duid_type_one("vendor", 0, DUID_TYPE_EN); + test_config_parse_duid_type_one("link-layer", 0, DUID_TYPE_LL); + test_config_parse_duid_type_one("uuid", 0, DUID_TYPE_UUID); + test_config_parse_duid_type_one("foo", 0, 0); +} + +static void test_config_parse_duid_rawdata_one(const char *rvalue, int ret, const DUID* expected) { + DUID actual = {}; + int r; + _cleanup_free_ char *d = NULL; + + r = config_parse_duid_rawdata("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + d = hexmem(actual.raw_data, actual.raw_data_len); + log_info_errno(r, "\"%s\" → \"%s\" (%m)", + rvalue, strnull(d)); + assert_se(r == ret); + if (expected) { + assert_se(actual.raw_data_len == expected->raw_data_len); + assert_se(memcmp(actual.raw_data, expected->raw_data, expected->raw_data_len) == 0); + } +} + +static void test_config_parse_hwaddr_one(const char *rvalue, int ret, const struct ether_addr* expected) { + struct ether_addr *actual = NULL; + int r; + + r = config_parse_hwaddr("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + assert_se(ret == r); + if (expected) { + assert_se(actual); + assert(ether_addr_equal(expected, actual)); + } else { + assert_se(actual == NULL); + } + free(actual); +} + +#define BYTES_0_128 "0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:3e:3f:40:41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:50:51:52:53:54:55:56:57:58:59:5a:5b:5c:5d:5e:5f:60:61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:70:71:72:73:74:75:76:77:78:79:7a:7b:7c:7d:7e:7f:80" + +#define BYTES_1_128 {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80} + +static void test_config_parse_duid_rawdata(void) { + test_config_parse_duid_rawdata_one("", 0, &(DUID){}); + test_config_parse_duid_rawdata_one("00:11:22:33:44:55:66:77", 0, + &(DUID){0, 8, {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}}); + test_config_parse_duid_rawdata_one("00:11:22:", 0, + &(DUID){0, 3, {0x00,0x11,0x22}}); + test_config_parse_duid_rawdata_one("000:11:22", 0, &(DUID){}); /* error, output is all zeros */ + test_config_parse_duid_rawdata_one("00:111:22", 0, &(DUID){}); + test_config_parse_duid_rawdata_one("0:1:2:3:4:5:6:7", 0, + &(DUID){0, 8, {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}}); + test_config_parse_duid_rawdata_one("11::", 0, &(DUID){0, 1, {0x11}}); /* FIXME: should this be an error? */ + test_config_parse_duid_rawdata_one("abcdef", 0, &(DUID){}); + test_config_parse_duid_rawdata_one(BYTES_0_128, 0, &(DUID){}); + test_config_parse_duid_rawdata_one(BYTES_0_128 + 2, 0, &(DUID){0, 128, BYTES_1_128}); +} + +static void test_config_parse_hwaddr(void) { + const struct ether_addr t[] = { + { .ether_addr_octet = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } }, + { .ether_addr_octet = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab } }, + }; + test_config_parse_hwaddr_one("", 0, NULL); + test_config_parse_hwaddr_one("no:ta:ma:ca:dd:re", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:fx", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one(" aa:bb:cc:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\n", 0, &t[0]); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\nxxx", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc: dd:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:d d:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee", 0, NULL); + test_config_parse_hwaddr_one("9:aa:bb:cc:dd:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff:gg", 0, NULL); + test_config_parse_hwaddr_one("aa:Bb:CC:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one("01:23:45:67:89:aB", 0, &t[1]); + test_config_parse_hwaddr_one("1:23:45:67:89:aB", 0, &t[1]); + test_config_parse_hwaddr_one("aa-bb-cc-dd-ee-ff", 0, &t[0]); + test_config_parse_hwaddr_one("AA-BB-CC-DD-EE-FF", 0, &t[0]); + test_config_parse_hwaddr_one("01-23-45-67-89-ab", 0, &t[1]); + test_config_parse_hwaddr_one("aabb.ccdd.eeff", 0, &t[0]); + test_config_parse_hwaddr_one("0123.4567.89ab", 0, &t[1]); + test_config_parse_hwaddr_one("123.4567.89ab.", 0, NULL); + test_config_parse_hwaddr_one("aabbcc.ddeeff", 0, NULL); + test_config_parse_hwaddr_one("aabbccddeeff", 0, NULL); + test_config_parse_hwaddr_one("aabbccddee:ff", 0, NULL); + test_config_parse_hwaddr_one("012345.6789ab", 0, NULL); + test_config_parse_hwaddr_one("123.4567.89ab", 0, &t[1]); +} + +int main(int argc, char **argv) { + log_parse_environment(); + log_open(); + + test_config_parse_duid_type(); + test_config_parse_duid_rawdata(); + test_config_parse_hwaddr(); + + return 0; +} |