From 6cf4a01c3630d87e208ef261009c8144796b64a5 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 3 Nov 2015 13:01:43 +0100 Subject: [PATCH 01/13] networkd: dhcp4 - style fixes --- src/network/networkd-dhcp4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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); } From 63bbe5c767a39ee4d6acaadc5e643d6d3556d9cd Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 10 Nov 2015 13:52:34 +0100 Subject: [PATCH 02/13] networkd: address - drop the 'added' flag Instead instantiate the kernel flags to TENTATIVE until the kernel updates us with the real value. --- src/network/networkd-address.c | 5 +++-- src/network/networkd-address.h | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 8b6acf2e1d..5231427b14 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) @@ -333,7 +335,6 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s ready = address_is_ready(address); - address->added = true; address->flags = flags; address->scope = scope; address->cinfo = *cinfo; @@ -769,5 +770,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); From f667c150a9116022f348cb6d8f2a553dce2386c3 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 10 Nov 2015 15:41:30 +0100 Subject: [PATCH 03/13] sd-dhcp6-client: add is_running() method --- src/libsystemd-network/sd-dhcp6-client.c | 8 ++++++++ src/systemd/sd-dhcp6-client.h | 1 + 2 files changed, 9 insertions(+) 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/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); From 6b9e85a7dea3a14be8d6229a667222f104cec208 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 3 Nov 2015 15:11:38 +0100 Subject: [PATCH 04/13] sd-netlink: add sd_rtnl_message_route_{s,g}et_flags() --- src/libsystemd/sd-netlink/rtnl-message.c | 29 ++++++++++++++++++++++++ src/systemd/sd-netlink.h | 2 ++ 2 files changed, 31 insertions(+) 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/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); From 8ae4b6d12ca93fcfa17d209c40ea0b75c8a7f88e Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Wed, 11 Nov 2015 15:17:29 +0100 Subject: [PATCH 05/13] sd-netlink: types - let tables be sized implicitly This way we do not rely on the size MAX* constants from the kernel headers, as these will be out-of-sync in case we have old headers and new defines in missing.h. --- src/basic/missing.h | 19 +++++---- src/libsystemd/sd-netlink/netlink-types.c | 50 +++++++++++------------ 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/basic/missing.h b/src/basic/missing.h index 306c56a156..71dad8f0ad 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 diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index cf0a6248d6..b2575404bf 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 }, @@ -498,7 +498,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 +514,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) }, From b69015efd2752fd2719beec85496221304130b0b Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 3 Nov 2015 15:25:10 +0100 Subject: [PATCH 06/13] sd-netlink: add support for RTA_PREF --- src/basic/missing.h | 4 ++++ src/libsystemd/sd-netlink/netlink-types.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/basic/missing.h b/src/basic/missing.h index 71dad8f0ad..d539ed00e4 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -901,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/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index b2575404bf..135354e5f3 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -490,7 +490,11 @@ static const NLType rtnl_route_types[] = { RTA_TABLE, RTA_MARK, RTA_MFC_STATS, + RTA_VIA, + RTA_NEWDST, */ + [RTA_PREF] = { .type = NETLINK_TYPE_U8 }, + }; static const NLTypeSystem rtnl_route_type_system = { From cb53894d3b6e90edaee4219fe716850d01242f46 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Thu, 22 Oct 2015 17:46:35 +0200 Subject: [PATCH 07/13] sd-ndisc: notify user on STOP Also, stop the state machine when we get into a broken state, rather than just notify the user. --- src/libsystemd-network/sd-ndisc.c | 9 ++++++--- src/libsystemd-network/test-ndisc-rs.c | 6 ++++-- src/network/networkd-ndisc.c | 6 ++---- src/systemd/sd-ndisc.h | 9 +++++---- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index c494b9d6d8..b8bfc6c549 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -494,8 +494,8 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r 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"); + 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); @@ -549,7 +549,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 +576,8 @@ int sd_ndisc_stop(sd_ndisc *nd) { nd->state = NDISC_STATE_IDLE; + ndisc_notify(nd, SD_NDISC_EVENT_STOP); + return 0; } diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index 44eab94e8b..59c720d48d 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -87,11 +87,13 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { static void test_rs_done(sd_ndisc *nd, int event, void *userdata) { sd_event *e = userdata; - static int idx = 0; + static unsigned idx = 0; struct { uint8_t flag; int event; } flag_event[] = { + { 0, SD_NDISC_EVENT_STOP }, + { 0, SD_NDISC_EVENT_STOP }, { 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 } @@ -106,7 +108,7 @@ static void test_rs_done(sd_ndisc *nd, int event, void *userdata) { if (verbose) printf(" got event %d\n", event); - if (idx < 3) { + if (idx < ELEMENTSOF(flag_event)) { send_ra(flag_event[idx].flag); return; } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 33e692f97f..50661c65f7 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -37,6 +37,7 @@ static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { return; switch(event) { + case SD_NDISC_EVENT_STOP: case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE: return; @@ -51,10 +52,7 @@ static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { 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); + log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); break; } diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h index 570e1741d6..d0dd098560 100644 --- a/src/systemd/sd-ndisc.h +++ b/src/systemd/sd-ndisc.h @@ -31,10 +31,11 @@ _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_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 = 4, }; typedef struct sd_ndisc sd_ndisc; From 9d96e6c3efbe5ef52b2855612d51db52c469beb2 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 19 Oct 2015 22:15:50 +0200 Subject: [PATCH 08/13] sd-ndisc: introduce separate callbacks As the data passed is very different, we introduce four different callbacks: - Generic - router discovery timed out or state machine stopped - Router - router and link configuration received - Prefix onlink - configuration for an onlink prefix received - Prefix autonomous - configuration for to configure a SLAAC address for a prefix received --- src/libsystemd-network/sd-ndisc.c | 125 ++++++++++++++++--------- src/libsystemd-network/test-ndisc-rs.c | 27 +++--- src/network/networkd-ndisc.c | 39 ++++---- src/systemd/sd-ndisc.h | 23 +++-- 4 files changed, 132 insertions(+), 82 deletions(-) diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index b8bfc6c549..de3f26211e 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,34 @@ 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; - - 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; - } + stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER); + switch (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3) { + case ND_RA_FLAG_PREF_LOW: + pref = -1; + break; + case ND_RA_FLAG_PREF_HIGH: + pref = 1; + break; + default: + pref = 0; + 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 == 1 ? "high" : pref == -1 ? "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 +564,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))) @@ -576,7 +616,8 @@ int sd_ndisc_stop(sd_ndisc *nd) { nd->state = NDISC_STATE_IDLE; - ndisc_notify(nd, SD_NDISC_EVENT_STOP); + 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 59c720d48d..a485be704e 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -85,31 +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 unsigned idx = 0; - struct { - uint8_t flag; - int event; - } flag_event[] = { - { 0, SD_NDISC_EVENT_STOP }, - { 0, SD_NDISC_EVENT_STOP }, - { 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 } + 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 < ELEMENTSOF(flag_event)) { - send_ra(flag_event[idx].flag); + if (idx < ELEMENTSOF(flags_array)) { + send_ra(flags_array[idx]); return; } @@ -137,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/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 50661c65f7..dfbc5a866e 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -20,41 +20,44 @@ ***/ #include +#include #include #include "sd-ndisc.h" #include "networkd-link.h" -static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { +static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { Link *link = userdata; assert(link); assert(link->network); - assert(link->manager); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - switch(event) { - case SD_NDISC_EVENT_STOP: - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE: + if (flags & ND_RA_FLAG_MANAGED) + dhcp6_configure(link, false); + else if (flags & ND_RA_FLAG_OTHER) + dhcp6_configure(link, true); +} + +static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { + Link *link = userdata; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER: - dhcp6_configure(link, true); - - break; - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT: - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED: + switch (event) { + case SD_NDISC_EVENT_TIMEOUT: dhcp6_configure(link, false); - break; - + case SD_NDISC_EVENT_STOP: + break; default: log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); - - break; } } @@ -80,7 +83,11 @@ int ndisc_configure(Link *link) { return r; r = sd_ndisc_set_callback(link->ndisc_router_discovery, - ndisc_router_handler, link); + ndisc_router_handler, + NULL, + NULL, + ndisc_handler, + link); return r; } diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h index d0dd098560..80e24325f7 100644 --- a/src/systemd/sd-ndisc.h +++ b/src/systemd/sd-ndisc.h @@ -31,20 +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 = 4, + 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); From 7a695d8e1fda59857c4c23bcb50cd1e0aaf4a854 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 10 Nov 2015 15:43:52 +0100 Subject: [PATCH 09/13] networkd: dhcp6 - split up configure() method Enabling address acquisition, configuring the client and starting the client are now split out. This to better handle the client being repeatedly enabled due to router advertisements. --- src/network/networkd-dhcp6.c | 109 +++++++++++++++++------------------ src/network/networkd-link.h | 3 +- src/network/networkd-ndisc.c | 20 +++++-- 3 files changed, 71 insertions(+), 61 deletions(-) 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.h b/src/network/networkd-link.h index a22041870e..d915e7c9c0 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -147,7 +147,8 @@ 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); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index dfbc5a866e..bd5a5d675c 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -29,6 +29,7 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { Link *link = userdata; + int r; assert(link); assert(link->network); @@ -36,14 +37,19 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - if (flags & ND_RA_FLAG_MANAGED) - dhcp6_configure(link, false); - else if (flags & ND_RA_FLAG_OTHER) - dhcp6_configure(link, true); + 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 != -EBUSY) + log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); + } } static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { Link *link = userdata; + int r; assert(link); @@ -52,7 +58,11 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { switch (event) { case SD_NDISC_EVENT_TIMEOUT: - dhcp6_configure(link, false); + dhcp6_request_address(link); + + 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"); break; case SD_NDISC_EVENT_STOP: break; From f5a8c43f39937d97c9ed75e3fe8621945b42b0db Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Sun, 18 Oct 2015 18:25:58 +0200 Subject: [PATCH 10/13] networkd: IPv6 router discovery - follow IPv6AcceptRouterAdvertisemnt= The previous behavior: When DHCPv6 was enabled, router discover was performed first, and then DHCPv6 was enabled only if the relevant flags were passed in the Router Advertisement message. Moreover, router discovery was performed even if AcceptRouterAdvertisements=false, moreover, even if router advertisements were accepted (by the kernel) the flags indicating that DHCPv6 should be performed were ignored. New behavior: If RouterAdvertisements are accepted, and either no routers are found, or an advertisement is received indicating DHCPv6 should be performed, the DHCPv6 client is started. Moreover, the DHCP option now truly enables the DHCPv6 client regardless of router discovery (though it will probably not be very useful to get a lease withotu any routes, this seems the more consistent approach). The recommended default setting should be to set DHCP=ipv4 and to leave IPv6AcceptRouterAdvertisements unset. --- man/systemd.network.xml | 12 +++++++- src/network/networkd-link.c | 50 ++++++++++++++++++++++++++++++---- src/network/networkd-link.h | 1 + src/network/networkd-manager.c | 3 +- src/network/networkd-ndisc.c | 4 +-- 5 files changed, 60 insertions(+), 10 deletions(-) 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/network/networkd-link.c b/src/network/networkd-link.c index 13d2fc6d0d..3ca9ceb114 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"); @@ -1242,6 +1264,16 @@ static int link_acquire_conf(Link *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"); @@ -2041,6 +2073,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; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index d915e7c9c0..f453665655 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -157,6 +157,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..6350164074 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1091,7 +1091,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 bd5a5d675c..68277d6ad4 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -42,7 +42,7 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a dhcp6_request_address(link); r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0 && r != -EBUSY) + if (r < 0 && r != -EALREADY) log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); } } @@ -61,7 +61,7 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { dhcp6_request_address(link); r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0 && r != -EBUSY) + if (r < 0 && r != -EALREADY) log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m"); break; case SD_NDISC_EVENT_STOP: From 3b015d40c19d9338b66bf916d84dec601019c811 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 3 Nov 2015 13:02:16 +0100 Subject: [PATCH 11/13] networkd: ndisc - handle router advertisement in userspace Router Discovery is a core part of IPv6, which by default is handled by the kernel. However, the kernel implementation is meant as a fall-back, and to fully support the protocol a userspace implementation is desired. The protocol essentially listens for Router Advertisement packets from routers on the local link and use these to configure the client automatically. The four main pieces of information are: what kind (if any) of DHCPv6 configuration should be performed; a default gateway; the prefixes that should be considered to be on the local link; and the prefixes with which we can preform SLAAC in order to pick a global IPv6 address. A lot of additional information is also available, which we do not yet fully support, but which will eventually allow us to avoid the need for DHCPv6 in the common case. Short-term, the reason for wanting this is in userspace was the desire to fully track all the addresses on links we manage, and that is not possible for addresses managed by the kernel (as the kernel does not expose to us the fact that it manages these addresses). Moreover, we would like to support stable privacy addresses, which will soon be mandated and the legacy MAC-based global addresses deprecated, to do this well we need to handle the generation in userspace. Lastly, more long-term we wish to support more RA options than what the kernel exposes. --- src/libsystemd-network/sd-ndisc.c | 11 +-- src/network/networkd-link.c | 26 ++---- src/network/networkd-link.h | 2 + src/network/networkd-ndisc.c | 150 +++++++++++++++++++++++++++++- src/network/networkd-route.c | 8 ++ src/network/networkd-route.h | 2 + 6 files changed, 172 insertions(+), 27 deletions(-) diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index de3f26211e..f35b3a33b8 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -519,15 +519,14 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER); - switch (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3) { + pref = ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3; + + switch (pref) { case ND_RA_FLAG_PREF_LOW: - pref = -1; - break; case ND_RA_FLAG_PREF_HIGH: - pref = 1; break; default: - pref = 0; + pref = ND_RA_FLAG_PREF_MEDIUM; break; } @@ -535,7 +534,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r 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 == 1 ? "high" : pref == -1 ? "low" : "medium", + pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium", lifetime); r = ndisc_ra_parse(nd, ra, len); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 3ca9ceb114..19dff893ac 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -611,6 +611,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; @@ -1915,7 +1918,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 */ @@ -1925,29 +1928,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; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index f453665655..440e33227a 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -95,6 +95,8 @@ struct Link { unsigned dhcp4_messages; bool dhcp4_configured; bool dhcp6_configured; + unsigned ndisc_messages; + bool ndisc_configured; sd_ipv4ll *ipv4ll; bool ipv4ll_address; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 68277d6ad4..126f9c0fe9 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -27,12 +27,129 @@ #include "networkd-link.h" -static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, 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); + assert(link->manager); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; @@ -45,6 +162,33 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a if (r < 0 && r != -EALREADY) log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); } + + if (!gateway) + 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->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) { @@ -94,8 +238,8 @@ int ndisc_configure(Link *link) { r = sd_ndisc_set_callback(link->ndisc_router_discovery, ndisc_router_handler, - NULL, - NULL, + ndisc_prefix_onlink_handler, + ndisc_prefix_autonomous_handler, ndisc_handler, link); 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; From e7ab854c6cff0d7330d4ddf2d89bf431ca9a0939 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 10 Nov 2015 21:30:59 +0100 Subject: [PATCH 12/13] networkd: link - track state of IPv6LL address This is managed by the kernel, but we should track whether or not we have a configured IPv6LL address. This fixes two issues: - we now wait for IPv6LL before considering the link ready - we now wait for IPv6LL before attempting to do NDisc or DHCPv6 these protocols relies on an LL address being available. --- src/network/networkd-address.c | 12 +++++- src/network/networkd-link.c | 79 +++++++++++++++++++++++++--------- src/network/networkd-link.h | 7 ++- 3 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 5231427b14..c0562e5788 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -329,6 +329,7 @@ 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); @@ -342,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; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 19dff893ac..1194491797 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -603,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) && @@ -1238,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; @@ -1266,26 +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->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"); - } - if (link_lldp_enabled(link)) { assert(link->lldp); @@ -2093,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); @@ -2439,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; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 440e33227a..b564bcbca0 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -99,8 +99,9 @@ struct Link { 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; @@ -143,6 +144,8 @@ 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); From 4058d339f5b290ec970e1de0d3b804e4934acc20 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 10 Nov 2015 21:45:41 +0100 Subject: [PATCH 13/13] networkd: improve logging --- src/network/networkd-link.c | 5 ++--- src/network/networkd-manager.c | 16 +++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 1194491797..a7d0908bdd 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2706,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-manager.c b/src/network/networkd-manager.c index 6350164074..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: