treewide: replace RELAYD prefix naming in macros
[project/odhcpd.git] / src / odhcpd.c
index ee628a5..837004b 100644 (file)
@@ -222,6 +222,7 @@ ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest,
 
 struct addr_info {
        int ifindex;
+       int af;
        struct odhcpd_ipaddr **addrs;
        int pending;
        ssize_t ret;
@@ -233,18 +234,34 @@ static int cb_valid_handler(struct nl_msg *msg, void *arg)
        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));
@@ -254,9 +271,13 @@ static int cb_valid_handler(struct nl_msg *msg, void *arg)
        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]);
 
@@ -293,12 +314,28 @@ static int cb_error_handler(_unused struct sockaddr_nl *nla, struct nlmsgerr *er
        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,
@@ -306,6 +343,7 @@ ssize_t odhcpd_get_interface_addresses(int ifindex, struct odhcpd_ipaddr **addrs
        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,
@@ -334,6 +372,23 @@ ssize_t odhcpd_get_interface_addresses(int ifindex, struct odhcpd_ipaddr **addrs
                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);
 
@@ -383,12 +438,12 @@ int odhcpd_get_interface_dns_addr(const struct interface *iface, struct in6_addr
                                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)
@@ -396,7 +451,7 @@ int odhcpd_get_interface_dns_addr(const struct interface *iface, struct in6_addr
        }
 
        if (m >= 0) {
-               *addr = iface->ia_addr[m].addr;
+               *addr = iface->ia_addr[m].addr.in6;
                return 0;
        }
 
@@ -511,7 +566,7 @@ static void odhcpd_receive_packets(struct uloop_fd *u, _unused unsigned int even
 {
        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;