diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 7d081d22fe..5994869d97 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -227,7 +227,14 @@ yes, no, ipv4, or ipv6. - Please note that, by default, the domain name + Note that DHCPv6 will by default be triggered by Router + Advertisment, if that is enabled, regardless of this parameter. + By enabling DHCPv6 support explicitly, the DHCPv6 client will + be started regardless of the presence of routers on the link, + or what flags the routers pass. See + IPv6AcceptRouterAdvertisements=. + + Furthermore, note that by default the domain name specified through DHCP is not used for name resolution. See option below. @@ -414,6 +421,9 @@ When unset, the kernel default is used, and router advertisements are accepted only when local forwarding is disabled for that interface. + When router advertisements are accepted, they will + trigger the start of the DHCPv6 client if the relevant + flags are passed, or if no routers are found on the link. Takes a boolean. If true, router advertisements are accepted, when false, router advertisements are ignored, independently of the local forwarding state. diff --git a/src/basic/missing.h b/src/basic/missing.h index 306c56a156..d539ed00e4 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -23,19 +23,20 @@ /* Missing glibc definitions to access certain kernel APIs */ -#include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #ifdef HAVE_AUDIT #include @@ -900,6 +901,10 @@ static inline int setns(int fd, int nstype) { #define NDA_MAX (__NDA_MAX - 1) #endif +#ifndef RTA_PREF +#define RTA_PREF 20 +#endif + #ifndef IPV6_UNICAST_IF #define IPV6_UNICAST_IF 76 #endif diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index e29de60d24..e00ca2b00d 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1115,11 +1115,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { } int sd_dhcp6_client_stop(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); return 0; } +int sd_dhcp6_client_is_running(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + + return client->state != DHCP6_STATE_STOPPED; +} + int sd_dhcp6_client_start(sd_dhcp6_client *client) { int r = 0; enum DHCP6State state = DHCP6_STATE_SOLICITATION; diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index c494b9d6d8..f35b3a33b8 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -29,6 +29,7 @@ #include "alloc-util.h" #include "async.h" #include "icmp6-util.h" +#include "in-addr-util.h" #include "list.h" #include "socket-util.h" @@ -47,6 +48,12 @@ enum NDiscState { #define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr)) #define NDISC_OPT_LEN_UNITS 8 +#define ND_RA_FLAG_PREF 0x18 +#define ND_RA_FLAG_PREF_LOW 0x03 +#define ND_RA_FLAG_PREF_MEDIUM 0x0 +#define ND_RA_FLAG_PREF_HIGH 0x1 +#define ND_RA_FLAG_PREF_INVALID 0x2 + typedef struct NDiscPrefix NDiscPrefix; struct NDiscPrefix { @@ -75,6 +82,9 @@ struct sd_ndisc { sd_event_source *recv; sd_event_source *timeout; int nd_sent; + sd_ndisc_router_callback_t router_callback; + sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback; + sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback; sd_ndisc_callback_t callback; void *userdata; }; @@ -119,15 +129,17 @@ static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) { return 0; } -static void ndisc_notify(sd_ndisc *nd, int event) { - if (nd->callback) - nd->callback(nd, event, nd->userdata); -} - -int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t callback, - void *userdata) { +int sd_ndisc_set_callback(sd_ndisc *nd, + sd_ndisc_router_callback_t router_callback, + sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback, + sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback, + sd_ndisc_callback_t callback, + void *userdata) { assert(nd); + nd->router_callback = router_callback; + nd->prefix_onlink_callback = prefix_onlink_callback; + nd->prefix_autonomous_callback = prefix_autonomous_callback; nd->callback = callback; nd->userdata = userdata; @@ -320,7 +332,7 @@ static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr, static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, const struct nd_opt_prefix_info *prefix_opt) { NDiscPrefix *prefix; - uint32_t lifetime; + uint32_t lifetime_valid, lifetime_preferred; usec_t time_now; char time_string[FORMAT_TIMESPAN_MAX]; int r; @@ -331,10 +343,17 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, if (len < prefix_opt->nd_opt_pi_len) return -ENOMSG; - if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)) + if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO))) return 0; - lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time); + if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0) + return 0; + + lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time); + lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time); + + if (lifetime_valid < lifetime_preferred) + return 0; r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix, prefix_opt->nd_opt_pi_prefix_len, &prefix); @@ -357,8 +376,8 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); + prefix->len, lifetime_valid, + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC)); LIST_PREPEND(prefixes, nd->prefixes, prefix); @@ -378,17 +397,24 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); + prefix->len, lifetime_valid, + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC)); } r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) return r; - prefix->valid_until = time_now + lifetime * USEC_PER_SEC; + prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC; - return r; + if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback) + nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata); + + if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback) + nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid, + nd->userdata); + + return 0; } static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, @@ -453,11 +479,13 @@ static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, } static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - sd_ndisc *nd = userdata; - int r, buflen = 0; - ssize_t len; _cleanup_free_ struct nd_router_advert *ra = NULL; - int event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE; + sd_ndisc *nd = userdata; + int r, buflen = 0, pref, stateful; + union sockaddr_union router = {}; + socklen_t router_len = sizeof(router); + unsigned lifetime; + ssize_t len; assert(s); assert(nd); @@ -471,9 +499,12 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r if (!ra) return -ENOMEM; - len = read(fd, ra, buflen); + len = recvfrom(fd, ra, buflen, 0, &router.sa, &router_len); if (len < 0) { - log_ndisc(nd, "Could not receive message from UDP socket: %m"); + log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m"); + return 0; + } else if (router_len != sizeof(router.in6) && router_len != 0) { + log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len); return 0; } @@ -487,26 +518,33 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; - if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ) - event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER; + stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER); + pref = ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3; - if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) - event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED; - - log_ndisc(nd, "Received Router Advertisement flags %s/%s", - ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none", - ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none"); - - if (event != SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE) { - r = ndisc_ra_parse(nd, ra, len); - if (r < 0) { - log_ndisc(nd, "Could not parse Router Advertisement: %s", - strerror(-r)); - return 0; - } + switch (pref) { + case ND_RA_FLAG_PREF_LOW: + case ND_RA_FLAG_PREF_HIGH: + break; + default: + pref = ND_RA_FLAG_PREF_MEDIUM; + break; } - ndisc_notify(nd, event); + lifetime = be16toh(ra->nd_ra_router_lifetime); + + log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec", + stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none", + pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium", + lifetime); + + r = ndisc_ra_parse(nd, ra, len); + if (r < 0) { + log_ndisc(nd, "Could not parse Router Advertisement: %s", strerror(-r)); + return 0; + } + + if (nd->router_callback) + nd->router_callback(nd, stateful, router_len != 0 ? &router.in6.sin6_addr : NULL, lifetime, pref, nd->userdata); return 0; } @@ -525,7 +563,8 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, nd->timeout = sd_event_source_unref(nd->timeout); if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) { - ndisc_notify(nd, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT); + if (nd->callback) + nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata); nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; } else { if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr))) @@ -549,7 +588,8 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, next_timeout, 0, ndisc_router_solicitation_timeout, nd); if (r < 0) { - ndisc_notify(nd, r); + /* we cannot continue if we are unable to rearm the timer */ + sd_ndisc_stop(nd); return 0; } @@ -575,6 +615,9 @@ int sd_ndisc_stop(sd_ndisc *nd) { nd->state = NDISC_STATE_IDLE; + if (nd->callback) + nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata); + return 0; } diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index 44eab94e8b..a485be704e 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -85,29 +85,28 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { return send_ra_function(0); } -static void test_rs_done(sd_ndisc *nd, int event, void *userdata) { +static void test_rs_done(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { sd_event *e = userdata; - static int idx = 0; - struct { - uint8_t flag; - int event; - } flag_event[] = { - { 0, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE }, - { ND_RA_FLAG_OTHER, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER }, - { ND_RA_FLAG_MANAGED, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED } + static unsigned idx = 0; + uint8_t flags_array[] = { + 0, + 0, + 0, + ND_RA_FLAG_OTHER, + ND_RA_FLAG_MANAGED }; uint32_t mtu; assert_se(nd); - assert_se(event == flag_event[idx].event); + assert_se(flags == flags_array[idx]); idx++; if (verbose) - printf(" got event %d\n", event); + printf(" got event 0x%02x\n", flags); - if (idx < 3) { - send_ra(flag_event[idx].flag); + if (idx < ELEMENTSOF(flags_array)) { + send_ra(flags_array[idx]); return; } @@ -135,7 +134,7 @@ static void test_rs(void) { assert_se(sd_ndisc_set_index(nd, 42) >= 0); assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); - assert_se(sd_ndisc_set_callback(nd, test_rs_done, e) >= 0); + assert_se(sd_ndisc_set_callback(nd, test_rs_done, NULL, NULL, NULL, e) >= 0); assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), time_now + 2 *USEC_PER_SEC, 0, diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index cf0a6248d6..135354e5f3 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -83,20 +83,20 @@ static const NLTypeSystem empty_type_system = { .types = empty_types, }; -static const NLType rtnl_link_info_data_veth_types[VETH_INFO_MAX + 1] = { +static const NLType rtnl_link_info_data_veth_types[] = { [VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, }; -static const NLType rtnl_link_info_data_ipvlan_types[IFLA_IPVLAN_MAX + 1] = { +static const NLType rtnl_link_info_data_ipvlan_types[] = { [IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_macvlan_types[IFLA_MACVLAN_MAX + 1] = { +static const NLType rtnl_link_info_data_macvlan_types[] = { [IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 }, [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_bridge_management_types[IFLA_BRIDGE_MAX + 1] = { +static const NLType rtnl_link_bridge_management_types[] = { [IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 }, [IFLA_BRIDGE_MODE] = { .type = NETLINK_TYPE_U16 }, /* @@ -105,7 +105,7 @@ static const NLType rtnl_link_bridge_management_types[IFLA_BRIDGE_MAX + 1] = { */ }; -static const NLType rtnl_link_info_data_bridge_types[IFLA_BR_MAX + 1] = { +static const NLType rtnl_link_info_data_bridge_types[] = { [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 }, @@ -114,7 +114,7 @@ static const NLType rtnl_link_info_data_bridge_types[IFLA_BR_MAX + 1] = { [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = { +static const NLType rtnl_link_info_data_vlan_types[] = { [IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 }, /* [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, @@ -124,7 +124,7 @@ static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = { [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = { +static const NLType rtnl_link_info_data_vxlan_types[] = { [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 }, [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR }, [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 }, @@ -151,7 +151,7 @@ static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = { [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG }, }; -static const NLType rtnl_bond_arp_target_types[BOND_ARP_TARGETS_MAX + 1] = { +static const NLType rtnl_bond_arp_target_types[] = { [BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 }, [BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 }, [BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 }, @@ -175,7 +175,7 @@ static const NLTypeSystem rtnl_bond_arp_type_system = { .types = rtnl_bond_arp_target_types, }; -static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = { +static const NLType rtnl_link_info_data_bond_types[] = { [IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 }, [IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 }, [IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 }, @@ -201,7 +201,7 @@ static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = { [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED }, }; -static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = { +static const NLType rtnl_link_info_data_iptun_types[] = { [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, @@ -220,7 +220,7 @@ static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = { +static const NLType rtnl_link_info_data_ipgre_types[] = { [IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 }, [IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 }, [IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 }, @@ -239,7 +239,7 @@ static const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = { [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = { +static const NLType rtnl_link_info_data_ipvti_types[] = { [IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 }, [IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 }, [IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 }, @@ -247,7 +247,7 @@ static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = { [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, }; -static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = { +static const NLType rtnl_link_info_data_ip6tnl_types[] = { [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, @@ -259,7 +259,7 @@ static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = { }; /* these strings must match the .kind entries in the kernel */ -static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_MAX] = { +static const char* const nl_union_link_info_data_table[] = { [NL_UNION_LINK_INFO_DATA_BOND] = "bond", [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge", [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan", @@ -282,7 +282,7 @@ static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_ DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); -static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_DATA_MAX] = { +static const NLTypeSystem rtnl_link_info_data_type_systems[] = { [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types), .types = rtnl_link_info_data_bond_types }, [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types), @@ -328,7 +328,7 @@ static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { .match = IFLA_INFO_KIND, }; -static const NLType rtnl_link_info_types[IFLA_INFO_MAX + 1] = { +static const NLType rtnl_link_info_types[] = { [IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING }, [IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union}, /* @@ -343,7 +343,7 @@ static const NLTypeSystem rtnl_link_info_type_system = { .types = rtnl_link_info_types, }; -static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1] = { +static const struct NLType rtnl_prot_info_bridge_port_types[] = { [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 }, [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 }, [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 }, @@ -357,7 +357,7 @@ static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1] [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 }, }; -static const NLTypeSystem rtnl_prot_info_type_systems[AF_MAX] = { +static const NLTypeSystem rtnl_prot_info_type_systems[] = { [AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_prot_info_bridge_port_types), .types = rtnl_prot_info_bridge_port_types }, }; @@ -368,7 +368,7 @@ static const NLTypeSystemUnion rtnl_prot_info_type_system_union = { .match_type = NL_MATCH_PROTOCOL, }; -static const struct NLType rtnl_af_spec_inet6_types[IFLA_INET6_MAX + 1] = { +static const struct NLType rtnl_af_spec_inet6_types[] = { [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 }, /* IFLA_INET6_CONF, @@ -386,7 +386,7 @@ static const NLTypeSystem rtnl_af_spec_inet6_type_system = { .types = rtnl_af_spec_inet6_types, }; -static const NLType rtnl_af_spec_types[AF_MAX + 1] = { +static const NLType rtnl_af_spec_types[] = { [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system }, }; @@ -395,7 +395,7 @@ static const NLTypeSystem rtnl_af_spec_type_system = { .types = rtnl_af_spec_types, }; -static const NLType rtnl_link_types[IFLA_MAX + 1 ] = { +static const NLType rtnl_link_types[] = { [IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR }, [IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR }, [IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, @@ -454,7 +454,7 @@ static const NLTypeSystem rtnl_link_type_system = { /* IFA_FLAGS was defined in kernel 3.14, but we still support older * kernels where IFA_MAX is lower. */ -static const NLType rtnl_address_types[CONST_MAX(IFA_MAX, IFA_FLAGS) + 1] = { +static const NLType rtnl_address_types[] = { [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR }, [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, @@ -472,7 +472,7 @@ static const NLTypeSystem rtnl_address_type_system = { .types = rtnl_address_types, }; -static const NLType rtnl_route_types[RTA_MAX + 1] = { +static const NLType rtnl_route_types[] = { [RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ [RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ [RTA_IIF] = { .type = NETLINK_TYPE_U32 }, @@ -490,7 +490,11 @@ static const NLType rtnl_route_types[RTA_MAX + 1] = { RTA_TABLE, RTA_MARK, RTA_MFC_STATS, + RTA_VIA, + RTA_NEWDST, */ + [RTA_PREF] = { .type = NETLINK_TYPE_U8 }, + }; static const NLTypeSystem rtnl_route_type_system = { @@ -498,7 +502,7 @@ static const NLTypeSystem rtnl_route_type_system = { .types = rtnl_route_types, }; -static const NLType rtnl_neigh_types[NDA_MAX + 1] = { +static const NLType rtnl_neigh_types[] = { [NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, [NDA_LLADDR] = { .type = NETLINK_TYPE_ETHER_ADDR }, [NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) }, @@ -514,7 +518,7 @@ static const NLTypeSystem rtnl_neigh_type_system = { .types = rtnl_neigh_types, }; -static const NLType rtnl_types[RTM_MAX + 1] = { +static const NLType rtnl_types[] = { [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index 7cccb9b1d5..3e605db661 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -84,6 +84,35 @@ int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope) return 0; } +int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_flags = flags; + + return 0; +} + +int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(flags, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *flags = rtm->rtm_flags; + + return 0; +} + int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { struct rtmsg *rtm; diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 8b6acf2e1d..c0562e5788 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -248,6 +248,8 @@ static int address_add_internal(Link *link, Set **addresses, address->family = family; address->in_addr = *in_addr; address->prefixlen = prefixlen; + /* Consider address tentative until we get the real flags from the kernel */ + address->flags = IFA_F_TENTATIVE; r = set_ensure_allocated(addresses, &address_hash_ops); if (r < 0) @@ -327,13 +329,13 @@ static int address_release(Address *address) { int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) { bool ready; + int r; assert(address); assert(cinfo); ready = address_is_ready(address); - address->added = true; address->flags = flags; address->scope = scope; address->cinfo = *cinfo; @@ -341,8 +343,17 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s if (address->link) { link_update_operstate(address->link); - if (!ready && address_is_ready(address)) + 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) && + !address->link->ipv6ll_address) { + r = link_ipv6ll_gained(address->link); + if (r < 0) + return r; + } + } } return 0; @@ -769,5 +780,5 @@ int config_parse_label(const char *unit, bool address_is_ready(const Address *a) { assert(a); - return a->added && !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); + return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 0b1f3b688b..4049a23bdc 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -52,7 +52,6 @@ struct Address { union in_addr_union in_addr; union in_addr_union in_addr_peer; - bool added:1; bool ip_masquerade_done:1; LIST_FIELDS(Address, addresses); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index b58fc5808c..b9c60a3c77 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -34,7 +34,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, int r; assert(link); - assert(link->dhcp4_messages); + assert(link->dhcp4_messages > 0); link->dhcp4_messages --; @@ -44,7 +44,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, link_enter_failed(link); } - if (!link->dhcp4_messages) { + if (link->dhcp4_messages == 0) { link->dhcp4_configured = true; link_check_ready(link); } diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index c3332bb1ac..3ec9b84a93 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -165,83 +165,82 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { link_check_ready(link); } -int dhcp6_configure(Link *link, bool inf_req) { - int r, information_request; +int dhcp6_request_address(Link *link) { + int r, inf_req; + bool running; - assert_return(link, -EINVAL); - - link->dhcp6_configured = false; - - if (link->dhcp6_client) { - r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &information_request); - if (r < 0) { - log_link_warning_errno(link, r, "Could not get DHCPv6 Information request setting: %m"); - goto error; - } - - if (information_request && !inf_req) { - r = sd_dhcp6_client_stop(link->dhcp6_client); - if (r < 0) { - log_link_warning_errno(link, r, "Could not stop DHCPv6 while setting Managed mode: %m"); - goto error; - } - - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false); - if (r < 0) { - log_link_warning_errno(link, r, "Could not unset DHCPv6 Information request: %m"); - goto error; - } - - } - - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0 && r != -EALREADY) { - log_link_warning_errno(link, r, "Could not restart DHCPv6: %m"); - goto error; - } - - if (r == -EALREADY) - link->dhcp6_configured = true; + assert(link); + assert(link->dhcp6_client); + r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); + if (r < 0) return r; + + if (!inf_req) + return 0; + + r = sd_dhcp6_client_is_running(link->dhcp6_client); + if (r < 0) + return r; + else + running = !!r; + + if (running) { + r = sd_dhcp6_client_stop(link->dhcp6_client); + if (r < 0) + return r; } - r = sd_dhcp6_client_new(&link->dhcp6_client); + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false); + 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; + + assert(link); + + r = sd_dhcp6_client_new(&client); + if (r < 0) + return r; + + r = sd_dhcp6_client_attach_event(client, NULL, 0); if (r < 0) goto error; - r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); + r = sd_dhcp6_client_set_information_request(client, true); if (r < 0) - goto error; + return r; - r = sd_dhcp6_client_set_mac(link->dhcp6_client, + r = sd_dhcp6_client_set_mac(client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) goto error; - r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); + r = sd_dhcp6_client_set_index(client, link->ifindex); if (r < 0) goto error; - r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler, - link); + r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); if (r < 0) goto error; - if (inf_req) { - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, true); - if (r < 0) - goto error; - } + link->dhcp6_client = client; - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - goto error; + return 0; - return r; - - error: - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); +error: + sd_dhcp6_client_unref(client); return r; } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 13d2fc6d0d..a7d0908bdd 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -124,6 +124,28 @@ static bool link_ipv6_forward_enabled(Link *link) { return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } +bool link_ipv6_accept_ra_enabled(Link *link) { + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + 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. + */ + if (link->network->ipv6_accept_ra < 0) + /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ + return !link_ipv6_forward_enabled(link); + else if (link->network->ipv6_accept_ra > 0) + /* accept RA even if ip_forward is enabled */ + return true; + else + /* ignore RA */ + return false; +} + static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { if (link->flags & IFF_LOOPBACK) return _IPV6_PRIVACY_EXTENSIONS_INVALID; @@ -485,13 +507,13 @@ static int link_stop_clients(Link *link) { r = log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"); } - if(link->ndisc_router_discovery) { - 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"); - } + 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"); + } + 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"); @@ -581,6 +603,10 @@ void link_check_ready(Link *link) { !link->ipv4ll_route) return; + if (link_ipv6ll_enabled(link)) + if (!link->ipv6ll_address) + return; + if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && !link->dhcp4_configured) || (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && @@ -589,6 +615,9 @@ void link_check_ready(Link *link) { !link->dhcp4_configured && !link->dhcp6_configured)) return; + if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) + return; + SET_FOREACH(a, link->addresses, i) if (!address_is_ready(a)) return; @@ -1213,6 +1242,34 @@ static void lldp_handler(sd_lldp *lldp, int event, void *userdata) { } } +static int link_acquire_ipv6_conf(Link *link) { + int r; + + assert(link); + + if (link_dhcp6_enabled(link)) { + assert(link->dhcp6_client); + + log_link_debug(link, "Acquiring DHCPv6 lease"); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0) + return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); + } + + if (link_ipv6_accept_ra_enabled(link)) { + assert(link->ndisc_router_discovery); + + log_link_debug(link, "Discovering IPv6 routers"); + + r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery); + if (r < 0) + return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); + } + + return 0; +} + static int link_acquire_conf(Link *link) { int r; @@ -1241,16 +1298,6 @@ static int link_acquire_conf(Link *link) { return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); } - if (link_dhcp6_enabled(link)) { - assert(link->ndisc_router_discovery); - - log_link_debug(link, "Discovering IPv6 routers"); - - r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery); - if (r < 0) - return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); - } - if (link_lldp_enabled(link)) { assert(link->lldp); @@ -1883,7 +1930,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) { } static int link_set_ipv6_accept_ra(Link *link) { - const char *p = NULL, *v = NULL; + const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -1893,29 +1940,16 @@ static int link_set_ipv6_accept_ra(Link *link) { if (link->flags & IFF_LOOPBACK) return 0; - /* 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. - */ - if (link->network->ipv6_accept_ra < 0) - /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ - v = "1"; - else if (link->network->ipv6_accept_ra > 0) - /* "2" means accept RA even if ip_forward is enabled */ - v = "2"; - else - /* "0" means ignore RA */ - v = "0"; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); - r = write_string_file(p, v, 0); + /* we handle router advertisments ourselves, tell the kernel to GTFO */ + r = write_string_file(p, "0", 0); if (r < 0) { /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, v) > 0) + if (verify_one_line_file(p, "0") > 0) return 0; - log_link_warning_errno(link, r, "Cannot configure IPv6 accept_ra for interface: %m"); + log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m"); } return 0; @@ -2041,6 +2075,12 @@ static int link_configure(Link *link) { } if (link_dhcp6_enabled(link)) { + r = dhcp6_configure(link); + if (r < 0) + return r; + } + + if (link_ipv6_accept_ra_enabled(link)) { r = ndisc_configure(link); if (r < 0) return r; @@ -2065,6 +2105,12 @@ static int link_configure(Link *link) { r = link_acquire_conf(link); if (r < 0) return r; + + if (link->ipv6ll_address) { + r = link_acquire_ipv6_conf(link); + if (r < 0) + return r; + } } return link_enter_join_netdev(link); @@ -2411,6 +2457,27 @@ failed: return r; } +int link_ipv6ll_gained(Link *link) { + int r; + + assert(link); + + log_link_info(link, "Gained IPv6LL"); + + link->ipv6ll_address = true; + link_check_ready(link); + + if (link->network) { + r = link_acquire_ipv6_conf(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + return 0; +} + static int link_carrier_gained(Link *link) { int r; @@ -2639,9 +2706,8 @@ int link_save(Link *link) { sd_dhcp6_lease *dhcp6_lease = NULL; if (link->dhcp6_client) { - r = sd_dhcp6_client_get_lease(link->dhcp6_client, - &dhcp6_lease); - if (r < 0) + r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); + if (r < 0 && r != -ENOMSG) log_link_debug(link, "No DHCPv6 lease"); } diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index a22041870e..b564bcbca0 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -95,10 +95,13 @@ struct Link { unsigned dhcp4_messages; bool dhcp4_configured; bool dhcp6_configured; + unsigned ndisc_messages; + bool ndisc_configured; sd_ipv4ll *ipv4ll; - bool ipv4ll_address; - bool ipv4ll_route; + bool ipv4ll_address:1; + bool ipv4ll_route:1; + bool ipv6ll_address:1; bool static_configured; @@ -141,13 +144,16 @@ int link_save(Link *link); int link_carrier_reset(Link *link); bool link_has_carrier(Link *link); +int link_ipv6ll_gained(Link *link); + int link_set_mtu(Link *link, uint32_t mtu); int link_set_hostname(Link *link, const char *hostname); int link_set_timezone(Link *link, const char *timezone); int ipv4ll_configure(Link *link); int dhcp4_configure(Link *link); -int dhcp6_configure(Link *link, bool information_request); +int dhcp6_configure(Link *link); +int dhcp6_request_address(Link *link); int ndisc_configure(Link *link); bool link_lldp_enabled(Link *link); @@ -156,6 +162,7 @@ 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-manager.c b/src/network/networkd-manager.c index a5701001c1..42f58fed19 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -577,9 +577,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); if (r >= 0) { - if (cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME) - valid_str = "ever"; - else + if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, cinfo.ifa_valid * USEC_PER_SEC, USEC_PER_SEC); @@ -590,7 +588,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, switch (type) { case RTM_NEWADDR: if (address) - log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Updating address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); else { /* An address appeared that we did not request */ r = address_add_foreign(link, family, &in_addr, prefixlen, &address); @@ -598,7 +597,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen); return 0; } else - log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); } address_update(address, flags, scope, &cinfo); @@ -608,10 +608,12 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, case RTM_DELADDR: if (address) { - log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); address_drop(address); } else - log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); break; default: @@ -1091,7 +1093,8 @@ static bool manager_check_idle(void *userdata) { link_ipv4ll_enabled(link) || link_dhcp4_server_enabled(link) || link_dhcp4_enabled(link) || - link_dhcp6_enabled(link)) + link_dhcp6_enabled(link) || + link_ipv6_accept_ra_enabled(link)) return false; } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 33e692f97f..126f9c0fe9 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -20,14 +20,132 @@ ***/ #include +#include #include #include "sd-ndisc.h" #include "networkd-link.h" -static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { +static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + assert(link->ndisc_messages > 0); + + link->ndisc_messages --; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Could not set NDisc route or address: %m"); + link_enter_failed(link); + } + + if (link->ndisc_messages == 0) { + link->ndisc_configured = true; + link_check_ready(link); + } + + return 1; +} + +static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime_preferred, unsigned lifetime_valid, void *userdata) { + _cleanup_address_free_ Address *address = NULL; Link *link = userdata; + usec_t time_now; + int r; + + assert(nd); + assert(link); + assert(link->network); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + r = address_new(&address); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate address: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + address->family = AF_INET6; + address->in_addr.in6 = *prefix; + if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0) + memcpy(&address->in_addr.in6 + 8, &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->prefixlen = prefixlen; + address->flags = IFA_F_NOPREFIXROUTE; + address->cinfo.ifa_prefered = lifetime_preferred; + address->cinfo.ifa_valid = lifetime_valid; + + r = address_configure(address, link, ndisc_netlink_handler, true); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set SLAAC address: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) { + _cleanup_route_free_ Route *route = NULL; + Link *link = userdata; + usec_t time_now; + int r; + + assert(nd); + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->flags = RTM_F_PREFIX; + route->dst.in6 = *prefix; + route->dst_prefixlen = prefixlen; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set prefix route: %m"); + link_enter_failed(link); + return; + } + + 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) { + _cleanup_route_free_ Route *route = NULL; + Link *link = userdata; + usec_t time_now; + int r; assert(link); assert(link->network); @@ -36,27 +154,64 @@ static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - switch(event) { - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE: + if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) { + if (flags & ND_RA_FLAG_MANAGED) + dhcp6_request_address(link); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EALREADY) + log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); + } + + if (!gateway) return; - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER: - dhcp6_configure(link, true); + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->pref = pref; + route->gw.in6 = *gateway; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set default route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { + Link *link = userdata; + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch (event) { + case SD_NDISC_EVENT_TIMEOUT: + dhcp6_request_address(link); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EALREADY) + log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m"); break; - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT: - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED: - dhcp6_configure(link, false); - + case SD_NDISC_EVENT_STOP: break; - default: - if (event < 0) - log_link_warning_errno(link, event, "IPv6 Neighbor Discover error: %m"); - else - log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); - - break; + log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); } } @@ -82,7 +237,11 @@ int ndisc_configure(Link *link) { return r; r = sd_ndisc_set_callback(link->ndisc_router_discovery, - ndisc_router_handler, link); + ndisc_router_handler, + ndisc_prefix_onlink_handler, + ndisc_prefix_autonomous_handler, + ndisc_handler, + link); return r; } diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index f4bbd06af1..ed06c21160 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -494,10 +494,18 @@ int route_configure(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not set scope: %m"); + r = sd_rtnl_message_route_set_flags(req, route->flags); + if (r < 0) + return log_error_errno(r, "Colud not set flags: %m"); + r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); + r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref); + if (r < 0) + return log_error_errno(r, "Could not append RTA_PREF attribute: %m"); + r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index d0a51838ed..b276756674 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -40,6 +40,8 @@ struct Route { unsigned char tos; uint32_t priority; /* note that ip(8) calls this 'metric' */ unsigned char table; + unsigned char pref; + unsigned flags; union in_addr_union gw; union in_addr_union dst; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 13182a481d..9f0e92806e 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -62,6 +62,7 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret); int sd_dhcp6_client_stop(sd_dhcp6_client *client); int sd_dhcp6_client_start(sd_dhcp6_client *client); +int sd_dhcp6_client_is_running(sd_dhcp6_client *client); int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority); int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h index 570e1741d6..80e24325f7 100644 --- a/src/systemd/sd-ndisc.h +++ b/src/systemd/sd-ndisc.h @@ -31,19 +31,25 @@ _SD_BEGIN_DECLARATIONS; enum { - SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE = 0, - SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1, - SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER = 2, - SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3, + SD_NDISC_EVENT_STOP = 0, + SD_NDISC_EVENT_TIMEOUT = 1, }; typedef struct sd_ndisc sd_ndisc; -typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, - void *userdata); +typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata); +typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime, void *userdata); +typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata); +typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata); -int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, - void *userdata); +int sd_ndisc_set_callback(sd_ndisc *nd, + sd_ndisc_router_callback_t rcb, + sd_ndisc_prefix_onlink_callback_t plcb, + sd_ndisc_prefix_autonomous_callback_t pacb, + sd_ndisc_callback_t cb, + void *userdata); int sd_ndisc_set_index(sd_ndisc *nd, int interface_index); int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 8e1b06ee9a..2960deda0a 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -136,6 +136,8 @@ int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned *type); int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen); int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen); int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope); +int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags); +int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags); int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family); int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol); int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope);