@@ -1623,7 +1623,7 @@ LIB_EXPORT bool l_dhcp6_client_set_link_local_address(
return false;
if (!client->nora)
- l_icmp6_client_set_link_local_address(client->icmp6, ll);
+ l_icmp6_client_set_link_local_address(client->icmp6, ll, false);
return true;
}
@@ -206,7 +206,8 @@ static uint16_t icmp6_checksum(const struct iovec *iov, unsigned int iov_len)
static int icmp6_send_router_solicitation(int s, int ifindex,
const uint8_t src_mac[static 6],
- const struct in6_addr *src_ip)
+ const struct in6_addr *src_ip,
+ bool src_ip_optimistic)
{
struct nd_router_solicit rs = {
.nd_rs_type = ND_ROUTER_SOLICIT,
@@ -247,11 +248,17 @@ static int icmp6_send_router_solicitation(int s, int ifindex,
memcpy(&ip_hdr.ip6_src, src_ip, 16);
- if (l_memeqzero(src_ip, 16)) {
+ if (l_memeqzero(src_ip, 16) || src_ip_optimistic) {
/*
- * radvd will discard and warn about RSs from the unspecified
- * address with the SLLAO, omit that option by dropping the
- * last two iov buffers.
+ * RFC 4429 Section 3.2: "A node MUST NOT send a Router
+ * Solicitation with a SLLAO from an Optimistic Address.
+ * Router Solicitations SHOULD be sent from a non-Optimistic
+ * or the Unspecified Address; however, they MAY be sent from
+ * an Optimistic Address as long as the SLLAO is not included."
+ *
+ * Additionally radvd will also discard and warn about RSs
+ * from the unspecified address with the SLLAO. Omit that
+ * option by dropping the last two iov buffers.
*/
msg.msg_iovlen -= 2;
ip_hdr.ip6_plen = htons(ntohs(ip_hdr.ip6_plen) - rs_sllao_size);
@@ -347,6 +354,7 @@ struct l_icmp6_client {
uint64_t retransmit_time;
struct l_io *io;
struct in6_addr src_ip;
+ bool src_ip_optimistic;
struct l_icmp6_router *ra;
struct l_netlink *rtnl;
@@ -547,7 +555,8 @@ static void icmp6_client_timeout_send(struct l_timeout *timeout,
r = icmp6_send_router_solicitation(l_io_get_fd(client->io),
client->ifindex, client->mac,
- &client->src_ip);
+ &client->src_ip,
+ client->src_ip_optimistic);
if (r < 0) {
CLIENT_DEBUG("Error sending Router Solicitation: %s",
strerror(-r));
@@ -766,7 +775,7 @@ LIB_EXPORT bool l_icmp6_client_set_route_priority(
LIB_EXPORT bool l_icmp6_client_set_link_local_address(
struct l_icmp6_client *client,
- const char *ll)
+ const char *ll, bool optimistic)
{
if (unlikely(!client))
return false;
@@ -777,7 +786,11 @@ LIB_EXPORT bool l_icmp6_client_set_link_local_address(
* is fine. Once we have a confirmed link-local address we use that
* as the source address.
*/
- return inet_pton(AF_INET6, ll, &client->src_ip) == 1;
+ if (inet_pton(AF_INET6, ll, &client->src_ip) != 1)
+ return false;
+
+ client->src_ip_optimistic = optimistic;
+ return true;
}
struct l_icmp6_router *_icmp6_router_new()
@@ -66,7 +66,8 @@ bool l_icmp6_client_set_rtnl(struct l_icmp6_client *client,
bool l_icmp6_client_set_route_priority(struct l_icmp6_client *client,
uint32_t priority);
bool l_icmp6_client_set_link_local_address(struct l_icmp6_client *client,
- const char *ll);
+ const char *ll,
+ bool optimistic);
char *l_icmp6_router_get_address(const struct l_icmp6_router *r);
bool l_icmp6_router_get_managed(const struct l_icmp6_router *r);
@@ -93,6 +93,7 @@ struct l_netconfig {
unsigned int orig_optimistic_dad;
uint8_t mac[ETH_ALEN];
struct l_timeout *ra_timeout;
+ bool have_lla;
enum {
NETCONFIG_V6_METHOD_UNSET,
NETCONFIG_V6_METHOD_DHCP,
@@ -702,7 +703,7 @@ static bool netconfig_check_start_dhcp6(struct l_netconfig *nc)
return true;
/* Don't start DHCPv6 while waiting for the link-local address */
- if (l_queue_find(addr_wait_list, netconfig_match, nc))
+ if (!nc->have_lla)
return true;
return l_dhcp6_client_start(nc->dhcp6_client);
@@ -1844,6 +1845,7 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
{
struct in6_addr in6;
_auto_(l_free) char *ip = NULL;
+ bool new_lla;
if ((ifa->ifa_flags & IFA_F_TENTATIVE) &&
!(ifa->ifa_flags & IFA_F_OPTIMISTIC))
@@ -1858,16 +1860,28 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
if (!IN6_IS_ADDR_LINKLOCAL(&in6))
return;
- netconfig_addr_wait_unregister(nc, true);
+ new_lla = !nc->have_lla;
+ nc->have_lla = true;
+
+ if (!(ifa->ifa_flags & IFA_F_TENTATIVE))
+ netconfig_addr_wait_unregister(nc, true);
+ else if (nc->ifaddr6_dump_cmd_id) {
+ struct l_netlink *rtnl = l_rtnl_get();
+ unsigned int cmd_id = nc->ifaddr6_dump_cmd_id;
+
+ nc->ifaddr6_dump_cmd_id = 0;
+ l_netlink_cancel(rtnl, cmd_id);
+ }
l_dhcp6_client_set_link_local_address(nc->dhcp6_client, ip);
- l_icmp6_client_set_link_local_address(nc->icmp6_client, ip);
+ l_icmp6_client_set_link_local_address(nc->icmp6_client, ip,
+ !!(ifa->ifa_flags & IFA_F_OPTIMISTIC));
/*
* Only now that we have a link-local address see if we can start
* actual DHCPv6 setup.
*/
- if (netconfig_check_start_dhcp6(nc))
+ if (new_lla && netconfig_check_start_dhcp6(nc))
return;
netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED);
@@ -2022,6 +2036,13 @@ configure_ipv6:
* before we start the dump, instead of after it ends, to eliminate
* the possibility of missing an RTM_NEWADDR between the end of
* the dump command and registering for the events.
+ *
+ * We stay on that list until we receive a non-tentative LL address.
+ * Note that we may set .have_lla earlier, specifically when we
+ * receive a tentative LL address that is also optimistic. We will
+ * however stay on addr_wait_list because we want to notify
+ * l_icmp6_client again when the LL address completes DAD and becomes
+ * non-tentative.
*/
if (!addr_wait_list) {
addr_wait_list = l_queue_new();
@@ -2041,6 +2062,7 @@ configure_ipv6:
goto unregister;
l_queue_push_tail(addr_wait_list, netconfig);
+ netconfig->have_lla = false;
if (!l_net_get_mac_address(netconfig->ifindex, netconfig->mac))
goto unregister;