if (len > 0)
iface->addr6_len = len;
+ for (size_t i = 0; i < iface->addr6_len; i++) {
+ struct odhcpd_ipaddr *addr = &iface->addr6[i];
+
+ if (!addr->tentative) {
+ iface->have_link_local = true;
+ break;
+ }
+ }
+
len = netlink_get_interface_addrs(iface->ifindex,
false, &iface->addr4);
if (len > 0)
nla_memcpy(&event_info.addr, nla[IFA_ADDRESS], sizeof(event_info.addr));
- if (IN6_IS_ADDR_LINKLOCAL(&event_info.addr) || IN6_IS_ADDR_MULTICAST(&event_info.addr))
+ if (IN6_IS_ADDR_MULTICAST(&event_info.addr))
return NL_SKIP;
inet_ntop(AF_INET6, &event_info.addr, buf, sizeof(buf));
if (iface->ifindex != (int)ifa->ifa_index)
continue;
+ if (add && IN6_IS_ADDR_LINKLOCAL(&event_info.addr)) {
+ iface->have_link_local = true;
+ return NL_SKIP;
+ }
+
syslog(LOG_DEBUG, "Netlink %s %s on %s", add ? "newaddr" : "deladdr",
buf, iface->name);
if (ifa->ifa_flags & IFA_F_DEPRECATED)
addrs[ctxt->ret].preferred = 0;
+ if (ifa->ifa_family == AF_INET6 &&
+ ifa->ifa_flags & IFA_F_TENTATIVE)
+ addrs[ctxt->ret].tentative = true;
+
ctxt->ret++;
*(ctxt->addrs) = addrs;
struct {
uint8_t dprefix;
uint8_t invalid_advertisements;
+ bool tentative;
};
/* ipv4 only */
bool ra_useleasetime;
bool ra_dns;
bool no_dynamic_dhcp;
+ bool have_link_local;
uint8_t pio_filter_length;
struct in6_addr pio_filter_addr;
int default_router;
msecs = calc_adv_interval(iface, minvalid, &maxival);
lifetime = calc_ra_lifetime(iface, maxival);
+ if (!iface->have_link_local) {
+ syslog(LOG_NOTICE, "Skip sending a RA on %s as no link local address is available", iface->name);
+ goto out;
+ }
+
if (default_route && valid_prefix) {
adv.h.nd_ra_router_lifetime = htons(lifetime < UINT16_MAX ? lifetime : UINT16_MAX);
} else {
if (odhcpd_send(iface->router_event.uloop.fd, &dest, iov, ARRAY_SIZE(iov), iface) > 0)
iface->ra_sent++;
+out:
free(pfxs);
free(routes);