+int odhcpd_setup_addr(struct odhcpd_ipaddr *addr,
+ const struct interface *iface, const bool v6,
+ const bool add)
+{
+ struct nl_msg *msg;
+ struct ifaddrmsg ifa = {
+ .ifa_family = v6 ? AF_INET6 : AF_INET,
+ .ifa_prefixlen = addr->prefix,
+ .ifa_flags = 0,
+ .ifa_scope = 0,
+ .ifa_index = iface->ifindex, };
+ int ret = 0, flags = NLM_F_REQUEST;
+
+ if (add)
+ flags |= NLM_F_REPLACE | NLM_F_CREATE;
+
+ msg = nlmsg_alloc_simple(add ? RTM_NEWADDR : RTM_DELADDR, 0);
+ if (!msg)
+ return -1;
+
+ nlmsg_append(msg, &ifa, sizeof(ifa), flags);
+ nla_put(msg, IFA_LOCAL, v6 ? 16 : 4, &addr->addr);
+ if (v6) {
+ struct ifa_cacheinfo cinfo = { .ifa_prefered = 0xffffffffU,
+ .ifa_valid = 0xffffffffU,
+ .cstamp = 0,
+ .tstamp = 0 };
+ time_t now = odhcpd_time();
+
+ if (addr->preferred) {
+ int64_t preferred = addr->preferred - now;
+ if (preferred < 0)
+ preferred = 0;
+ else if (preferred > UINT32_MAX)
+ preferred = UINT32_MAX;
+
+ cinfo.ifa_prefered = preferred;
+ }
+
+ if (addr->valid) {
+ int64_t valid = addr->valid - now;
+ if (valid <= 0) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ else if (valid > UINT32_MAX)
+ valid = UINT32_MAX;
+
+ cinfo.ifa_valid = valid;
+ }
+
+ nla_put(msg, IFA_CACHEINFO, sizeof(cinfo), &cinfo);
+
+ nla_put_u32(msg, IFA_FLAGS, IFA_F_NOPREFIXROUTE);
+ } else {
+ if (addr->broadcast.s_addr)
+ nla_put_u32(msg, IFA_BROADCAST, addr->broadcast.s_addr);
+ }
+
+ ret = nl_send_auto_complete(rtnl_socket, msg);
+ nlmsg_free(msg);
+
+ if (ret < 0)
+ return ret;
+
+ return nl_wait_for_ack(rtnl_socket);
+}
+