#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
+#include "networkd-radv.h"
+#include "networkd-routing-policy-rule.h"
#include "set.h"
#include "socket-util.h"
#include "stdio-util.h"
return link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network);
}
+static bool link_radv_enabled(Link *link) {
+ assert(link);
+
+ if (!link_ipv6ll_enabled(link))
+ return false;
+
+ return link->network->router_prefix_delegation;
+}
+
static bool link_lldp_rx_enabled(Link *link) {
assert(link);
if (!link->network)
return false;
- if (link->network->bridge)
+ /* LLDP should be handled on bridge slaves as those have a direct
+ * connection to their peers not on the bridge master. Linux doesn't
+ * even (by default) forward lldp packets to the bridge master.*/
+ if (streq_ptr("bridge", link->kind))
return false;
return link->network->lldp_mode != LLDP_MODE_NO;
if (!link->network)
return false;
+ if (!link_ipv6ll_enabled(link))
+ return false;
+
/* If unset use system default (enabled if local forwarding is disabled.
* disabled if local forwarding is enabled).
* If set, ignore or enforce RA independent of local forwarding state.
static void link_free(Link *link) {
Address *address;
- Iterator i;
Link *carrier;
+ Iterator i;
if (!link)
return;
sd_ipv4ll_unref(link->ipv4ll);
sd_dhcp6_client_unref(link->dhcp6_client);
sd_ndisc_unref(link->ndisc);
+ sd_radv_unref(link->radv);
if (link->manager)
hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
}
+ if (link->radv) {
+ k = sd_radv_stop(link->radv);
+ if (k < 0)
+ r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m");
+ }
+
link_lldp_emit_stop(link);
return r;
}
return 1;
}
+static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ _cleanup_link_unref_ Link *link = userdata;
+ int r;
+
+ assert(rtnl);
+ assert(m);
+ assert(link);
+ assert(link->ifname);
+ assert(link->link_messages > 0);
+
+ link->link_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST)
+ log_link_warning_errno(link, r, "could not set address label: %m");
+ else if (r >= 0)
+ manager_rtnl_process_address(rtnl, m, link->manager);
+
+ if (link->link_messages == 0) {
+ log_link_debug(link, "Addresses label set");
+ link_enter_set_routes(link);
+ }
+
+ return 1;
+}
+
static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
_cleanup_free_ struct in_addr *addresses = NULL;
size_t n_addresses = 0, n_allocated = 0;
}
static int link_enter_set_addresses(Link *link) {
+ RoutingPolicyRule *rule, *rrule = NULL;
+ AddressLabel *label;
Address *ad;
int r;
link->link_messages++;
}
+ LIST_FOREACH(labels, label, link->network->address_labels) {
+ r = address_label_configure(label, link, address_label_handler, false);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set address label: %m");
+ link_enter_failed(link);
+ return r;
+ }
+
+ link->link_messages++;
+ }
+
+ LIST_FOREACH(rules, rule, link->network->rules) {
+ r = routing_policy_rule_get(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
+ rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, &rrule);
+ if (r == 1) {
+ (void) routing_policy_rule_make_local(link->manager, rrule);
+ continue;
+ }
+
+ r = routing_policy_rule_configure(rule, link, link_routing_policy_rule_handler, false);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set routing policy rules: %m");
+ link_enter_failed(link);
+ return r;
+ }
+
+ link->link_messages++;
+ }
+
+ routing_policy_rule_purge(link->manager, link);
+
/* now that we can figure out a default address for the dhcp server,
start it */
if (link_dhcp4_server_enabled(link)) {
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m");
}
+ if (link->network->priority != LINK_BRIDGE_PORT_PRIORITY_INVALID) {
+ r = sd_netlink_message_append_u16(req, IFLA_BRPORT_PRIORITY, link->network->priority);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PRIORITY attribute: %m");
+ }
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
}
+ if (link_radv_enabled(link)) {
+ assert(link->radv);
+ assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
+
+ log_link_debug(link, "Starting IPv6 Router Advertisements");
+
+ r = sd_radv_start(link->radv);
+ if (r < 0 && r != -EBUSY)
+ return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
+ }
+
return 0;
}
return 1;
}
-static int link_up(Link *link) {
+int link_up(Link *link) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
uint8_t ipv6ll_mode;
int r;
return 1;
}
-static int link_down(Link *link) {
+int link_down(Link *link) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
log_link_error_errno(link, r, "Could not set bridge vlan: %m");
}
+ /* Skip setting up addresses until it gets carrier,
+ or it would try to set addresses twice,
+ which is bad for non-idempotent steps. */
+ if (!link_has_carrier(link) && !link->network->configure_without_carrier)
+ return 0;
+
return link_enter_set_addresses(link);
}
}
static int link_drop_config(Link *link) {
- Address *address;
+ Address *address, *pool_address;
Route *route;
Iterator i;
int r;
r = address_remove(address, link, link_address_remove_handler);
if (r < 0)
return r;
+
+ /* If this address came from an address pool, clean up the pool */
+ LIST_FOREACH(addresses, pool_address, link->pool_addresses) {
+ if (address_equal(address, pool_address)) {
+ LIST_REMOVE(addresses, link->pool_addresses, pool_address);
+ address_free(pool_address);
+ break;
+ }
+ }
}
SET_FOREACH(route, link->routes, i) {
return r;
}
+ if (link_radv_enabled(link)) {
+ r = radv_configure(link);
+ if (r < 0)
+ return r;
+ }
+
if (link_lldp_rx_enabled(link)) {
r = sd_lldp_new(&link->lldp);
if (r < 0)
return r;
}
- if (link_has_carrier(link)) {
+ if (link_has_carrier(link) || link->network->configure_without_carrier) {
r = link_acquire_conf(link);
if (r < 0)
return r;
goto dhcp4_address_fail;
}
- r = sd_dhcp_client_new(&link->dhcp_client);
+ r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
if (r < 0)
return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m");
return r;
}
+ if (link_dhcp4_server_enabled(link))
+ (void) sd_dhcp_server_stop(link->dhcp_server);
+
r = link_drop_config(link);
if (r < 0)
return r;
r = sd_netlink_message_read_string(m, IFLA_IFNAME, &ifname);
if (r >= 0 && !streq(ifname, link->ifname)) {
- log_link_info(link, "Renamed to %s", ifname);
+ log_link_info(link, "Interface name change detected, %s has been renamed to %s.", link->ifname, ifname);
link_free_carrier_maps(link);
return r;
}
}
+
+ if (link->radv) {
+ r = sd_radv_set_mtu(link->radv, link->mtu);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set MTU for Router Advertisement: %m");
+ }
}
/* The kernel may broadcast NEWLINK messages without the MAC address
}
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),
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");
+ switch (link->network->dhcp_client_identifier) {
+ case DHCP_CLIENT_ID_DUID: {
+ 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 log_link_warning_errno(link, r, "Could not update DUID/IAID in DHCP client: %m");
+ 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));
+ if(r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC client id in DHCP client: %m");
+ break;
+ default:
+ assert_not_reached("Unknown client identifier type.");
+ }
}
if (link->dhcp6_client) {
if (r < 0)
return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m");
}
+
+ if (link->radv) {
+ r = sd_radv_set_mac(link->radv, &link->mac);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m");
+ }
}
}
if (hashmap_isempty(h))
return;
- fputs(prefix, f);
+ fputs_unlocked(prefix, f);
HASHMAP_FOREACH(link, h, i) {
if (space)
- fputc(' ', f);
+ fputc_unlocked(' ', f);
fprintf(f, "%i", link->ifindex);
space = true;
}
- fputc('\n', f);
+ fputc_unlocked('\n', f);
}
int link_save(Link *link) {
sd_dhcp6_lease *dhcp6_lease = NULL;
const char *dhcp_domainname = NULL;
char **dhcp6_domains = NULL;
+ char **dhcp_domains = NULL;
unsigned j;
if (link->dhcp6_client) {
fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
- fputs("DNS=", f);
+ fputs_unlocked("DNS=", f);
space = false;
for (j = 0; j < link->network->n_dns; j++) {
}
if (space)
- fputc(' ', f);
- fputs(b, f);
+ fputc_unlocked(' ', f);
+ fputs_unlocked(b, f);
space = true;
}
r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
if (r > 0) {
if (space)
- fputc(' ', f);
+ fputc_unlocked(' ', f);
serialize_in_addrs(f, addresses, r);
space = true;
}
r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs);
if (r > 0) {
if (space)
- fputc(' ', f);
+ fputc_unlocked(' ', f);
serialize_in6_addrs(f, in6_addrs, r);
space = true;
}
SET_FOREACH(dd, link->ndisc_rdnss, i) {
if (space)
- fputc(' ', f);
+ fputc_unlocked(' ', f);
serialize_in6_addrs(f, &dd->address, 1);
space = true;
}
}
- fputc('\n', f);
+ fputc_unlocked('\n', f);
- fputs("NTP=", f);
+ fputs_unlocked("NTP=", f);
space = false;
fputstrv(f, link->network->ntp, NULL, &space);
r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
if (r > 0) {
if (space)
- fputc(' ', f);
+ fputc_unlocked(' ', f);
serialize_in_addrs(f, addresses, r);
space = true;
}
&in6_addrs);
if (r > 0) {
if (space)
- fputc(' ', f);
+ fputc_unlocked(' ', f);
serialize_in6_addrs(f, in6_addrs, r);
space = true;
}
fputstrv(f, hosts, NULL, &space);
}
- fputc('\n', f);
+ fputc_unlocked('\n', f);
if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
- if (link->dhcp_lease)
+ if (link->dhcp_lease) {
(void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
+ (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains);
+ }
if (dhcp6_lease)
(void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
}
- fputs("DOMAINS=", f);
+ fputs_unlocked("DOMAINS=", f);
+ space = false;
fputstrv(f, link->network->search_domains, NULL, &space);
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
if (dhcp_domainname)
fputs_with_space(f, dhcp_domainname, NULL, &space);
+ if (dhcp_domains)
+ fputstrv(f, dhcp_domains, NULL, &space);
if (dhcp6_domains)
fputstrv(f, dhcp6_domains, NULL, &space);
fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
}
- fputc('\n', f);
+ fputc_unlocked('\n', f);
- fputs("ROUTE_DOMAINS=", f);
- fputstrv(f, link->network->route_domains, NULL, NULL);
+ fputs_unlocked("ROUTE_DOMAINS=", f);
+ space = false;
+ fputstrv(f, link->network->route_domains, NULL, &space);
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
NDiscDNSSL *dd;
if (dhcp_domainname)
fputs_with_space(f, dhcp_domainname, NULL, &space);
+ if (dhcp_domains)
+ fputstrv(f, dhcp_domains, NULL, &space);
if (dhcp6_domains)
fputstrv(f, dhcp6_domains, NULL, &space);
fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
}
- fputc('\n', f);
+ fputc_unlocked('\n', f);
fprintf(f, "LLMNR=%s\n",
resolve_support_to_string(link->network->llmnr));
if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
const char *n;
- fputs("DNSSEC_NTA=", f);
+ fputs_unlocked("DNSSEC_NTA=", f);
space = false;
SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i)
fputs_with_space(f, n, NULL, &space);
- fputc('\n', f);
+ fputc_unlocked('\n', f);
}
- fputs("ADDRESSES=", f);
+ fputs_unlocked("ADDRESSES=", f);
space = false;
SET_FOREACH(a, link->addresses, i) {
_cleanup_free_ char *address_str = NULL;
fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen);
space = true;
}
- fputc('\n', f);
+ fputc_unlocked('\n', f);
- fputs("ROUTES=", f);
+ fputs_unlocked("ROUTES=", f);
space = false;
SET_FOREACH(route, link->routes, i) {
_cleanup_free_ char *route_str = NULL;
if (r < 0)
goto fail;
- fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%hhu/"USEC_FMT, space ? " " : "", route_str,
+ fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%"PRIu32"/"USEC_FMT,
+ space ? " " : "", route_str,
route->dst_prefixlen, route->tos, route->priority, route->table, route->lifetime);
space = true;
}
- fputc('\n', f);
+ fputc_unlocked('\n', f);
}
print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links);
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
if (r >= 0) {
- fputs("DHCP4_ADDRESS=", f);
+ fputs_unlocked("DHCP4_ADDRESS=", f);
serialize_in_addrs(f, &address, 1);
- fputc('\n', f);
+ fputc_unlocked('\n', f);
}
r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
r = sd_ipv4ll_get_address(link->ipv4ll, &address);
if (r >= 0) {
- fputs("IPV4LL_ADDRESS=", f);
+ fputs_unlocked("IPV4LL_ADDRESS=", f);
serialize_in_addrs(f, &address, 1);
- fputc('\n', f);
+ fputc_unlocked('\n', f);
}
}