struct addr_info {
int ifindex;
+ int af;
struct odhcpd_ipaddr **addrs;
int pending;
ssize_t ret;
struct odhcpd_ipaddr *addrs = *(ctxt->addrs);
struct nlmsghdr *hdr = nlmsg_hdr(msg);
struct ifaddrmsg *ifa;
- struct nlattr *nla[__IFA_MAX];
+ struct nlattr *nla[__IFA_MAX], *nla_addr = NULL;
if (hdr->nlmsg_type != RTM_NEWADDR)
return NL_SKIP;
ifa = NLMSG_DATA(hdr);
if (ifa->ifa_scope != RT_SCOPE_UNIVERSE ||
+ (ctxt->af != ifa->ifa_family) ||
(ctxt->ifindex && ifa->ifa_index != (unsigned)ctxt->ifindex))
return NL_SKIP;
nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
- if (!nla[IFA_ADDRESS])
+
+ switch (ifa->ifa_family) {
+ case AF_INET6:
+ if (nla[IFA_ADDRESS])
+ nla_addr = nla[IFA_ADDRESS];
+ break;
+
+ case AF_INET:
+ if (nla[IFA_LOCAL])
+ nla_addr = nla[IFA_LOCAL];
+ break;
+
+ default:
+ break;
+ }
+ if (!nla_addr)
return NL_SKIP;
addrs = realloc(addrs, sizeof(*addrs)*(ctxt->ret + 1));
memset(&addrs[ctxt->ret], 0, sizeof(addrs[ctxt->ret]));
addrs[ctxt->ret].prefix = ifa->ifa_prefixlen;
- nla_memcpy(&addrs[ctxt->ret].addr, nla[IFA_ADDRESS],
+ nla_memcpy(&addrs[ctxt->ret].addr, nla_addr,
sizeof(addrs[ctxt->ret].addr));
+ if (nla[IFA_BROADCAST])
+ nla_memcpy(&addrs[ctxt->ret].broadcast, nla[IFA_BROADCAST],
+ sizeof(addrs[ctxt->ret].broadcast));
+
if (nla[IFA_CACHEINFO]) {
struct ifa_cacheinfo *ifc = nla_data(nla[IFA_CACHEINFO]);
return NL_STOP;
}
+static int prefix_cmp(const void *va, const void *vb)
+{
+ const struct odhcpd_ipaddr *a = va, *b = vb;
+ return (ntohl(a->addr.in.s_addr) < ntohl(b->addr.in.s_addr)) ? 1 :
+ (ntohl(a->addr.in.s_addr) > ntohl(b->addr.in.s_addr)) ? -1 : 0;
+}
+
+// compare IPv6 prefixes
+static int prefix6_cmp(const void *va, const void *vb)
+{
+ const struct odhcpd_ipaddr *a = va, *b = vb;
+ uint32_t a_pref = IN6_IS_ADDR_ULA(&a->addr.in6) ? 1 : a->preferred;
+ uint32_t b_pref = IN6_IS_ADDR_ULA(&b->addr.in6) ? 1 : b->preferred;
+ return (a_pref < b_pref) ? 1 : (a_pref > b_pref) ? -1 : 0;
+}
+
// Detect an IPV6-address currently assigned to the given interface
-ssize_t odhcpd_get_interface_addresses(int ifindex, struct odhcpd_ipaddr **addrs)
+ssize_t odhcpd_get_interface_addresses(int ifindex, bool v6, struct odhcpd_ipaddr **addrs)
{
struct nl_msg *msg;
struct ifaddrmsg ifa = {
- .ifa_family = AF_INET6,
+ .ifa_family = v6? AF_INET6: AF_INET,
.ifa_prefixlen = 0,
.ifa_flags = 0,
.ifa_scope = 0,
struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
struct addr_info ctxt = {
.ifindex = ifindex,
+ .af = v6? AF_INET6: AF_INET,
.addrs = addrs,
.ret = 0,
.pending = 1,
nl_recvmsgs(rtnl_socket, cb);
nlmsg_free(msg);
+
+ if (ctxt.ret <= 0)
+ goto out;
+
+ time_t now = odhcpd_time();
+ struct odhcpd_ipaddr *addr = *addrs;
+
+ qsort(addr, ctxt.ret, sizeof(*addr), v6 ? prefix6_cmp : prefix_cmp);
+
+ for (ssize_t i = 0; i < ctxt.ret; ++i) {
+ if (addr[i].preferred < UINT32_MAX - now)
+ addr[i].preferred += now;
+
+ if (addr[i].valid < UINT32_MAX - now)
+ addr[i].valid += now;
+ }
+
out:
nl_cb_put(cb);
iface->ia_addr[i].preferred < (uint32_t)now)
continue;
- if (IN6_IS_ADDR_ULA(&iface->ia_addr[i].addr)) {
- if (!IN6_IS_ADDR_ULA(&iface->ia_addr[m].addr)) {
+ if (IN6_IS_ADDR_ULA(&iface->ia_addr[i].addr.in6)) {
+ if (!IN6_IS_ADDR_ULA(&iface->ia_addr[m].addr.in6)) {
m = i;
continue;
}
- } else if (IN6_IS_ADDR_ULA(&iface->ia_addr[m].addr))
+ } else if (IN6_IS_ADDR_ULA(&iface->ia_addr[m].addr.in6))
continue;
if (iface->ia_addr[i].preferred > iface->ia_addr[m].preferred)
}
if (m >= 0) {
- *addr = iface->ia_addr[m].addr;
+ *addr = iface->ia_addr[m].addr.in6;
return 0;
}
{
struct odhcpd_event *e = container_of(u, struct odhcpd_event, uloop);
- uint8_t data_buf[RELAYD_BUFFER_SIZE], cmsg_buf[128];
+ uint8_t data_buf[8192], cmsg_buf[128];
union {
struct sockaddr_in6 in6;
struct sockaddr_in in;