Align IPv4 address logic with IPv6 by caching per interface the assigned
IPv4 addresses. This allows to get rid of different ioctl calls in the
DHCPv4 logic to retrieve the IPv4 address and netmask in use by an
interface.
Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
setup_dhcpv4_interface(iface, false);
clean_interface(iface);
setup_dhcpv4_interface(iface, false);
clean_interface(iface);
free(iface->ia_addr);
free(iface->ifname);
free(iface);
free(iface->ia_addr);
free(iface->ifname);
free(iface);
if (len > 0)
iface->ia_addr_len = len;
if (len > 0)
iface->ia_addr_len = len;
+
+ len = odhcpd_get_interface_addresses(iface->ifindex,
+ false, &iface->addr4);
+ if (len > 0)
+ iface->addr4_len = len;
- /* Create a range if not specified */
- struct ifreq ifreq;
- strncpy(ifreq.ifr_name, iface->ifname, sizeof(ifreq.ifr_name));
+ uint32_t mask = iface->addr4[0].prefix ? htonl(~((1 << (32 - iface->addr4[0].prefix)) - 1)) : 0;
- struct sockaddr_in *saddr = (struct sockaddr_in*)&ifreq.ifr_addr;
- struct sockaddr_in *smask = (struct sockaddr_in*)&ifreq.ifr_netmask;
+ /* Create a range if not specified */
if (!(iface->dhcpv4_start.s_addr & htonl(0xffff0000)) &&
!(iface->dhcpv4_end.s_addr & htonl(0xffff0000)) &&
if (!(iface->dhcpv4_start.s_addr & htonl(0xffff0000)) &&
!(iface->dhcpv4_end.s_addr & htonl(0xffff0000)) &&
- !ioctl(sock, SIOCGIFADDR, &ifreq)) {
- struct in_addr addr = saddr->sin_addr;
-
- ioctl(sock, SIOCGIFNETMASK, &ifreq);
- struct in_addr mask = smask->sin_addr;
+ iface->addr4_len) {
+ struct in_addr *addr = &iface->addr4[0].addr.in;
uint32_t start = ntohl(iface->dhcpv4_start.s_addr);
uint32_t end = ntohl(iface->dhcpv4_end.s_addr);
if (start && end && start < end &&
uint32_t start = ntohl(iface->dhcpv4_start.s_addr);
uint32_t end = ntohl(iface->dhcpv4_end.s_addr);
if (start && end && start < end &&
- start > ntohl(addr.s_addr & ~mask.s_addr) &&
- (start & ntohl(~mask.s_addr)) == start &&
- (end & ntohl(~mask.s_addr)) == end) {
+ start > ntohl(addr->s_addr & ~mask) &&
+ (start & ntohl(~mask)) == start &&
+ (end & ntohl(~mask)) == end) {
iface->dhcpv4_start.s_addr = htonl(start) |
iface->dhcpv4_start.s_addr = htonl(start) |
- (addr.s_addr & mask.s_addr);
iface->dhcpv4_end.s_addr = htonl(end) |
iface->dhcpv4_end.s_addr = htonl(end) |
- (addr.s_addr & mask.s_addr);
- } else if (ntohl(mask.s_addr) <= 0xfffffff0) {
- start = addr.s_addr & mask.s_addr;
- end = addr.s_addr & mask.s_addr;
+ (addr->s_addr & mask);
+ } else if (ntohl(mask) <= 0xfffffff0) {
+ start = addr->s_addr & mask;
+ end = addr->s_addr & mask;
- if (ntohl(mask.s_addr) <= 0xffffff00) {
+ if (ntohl(mask) <= 0xffffff00) {
iface->dhcpv4_start.s_addr = start | htonl(100);
iface->dhcpv4_end.s_addr = end | htonl(250);
iface->dhcpv4_start.s_addr = start | htonl(100);
iface->dhcpv4_end.s_addr = end | htonl(250);
- } else if (ntohl(mask.s_addr) <= 0xffffffc0) {
+ } else if (ntohl(mask) <= 0xffffffc0) {
iface->dhcpv4_start.s_addr = start | htonl(10);
iface->dhcpv4_end.s_addr = end | htonl(60);
iface->dhcpv4_start.s_addr = start | htonl(10);
iface->dhcpv4_end.s_addr = end | htonl(60);
- } else if (ntohl(mask.s_addr) <= 0xffffffe0) {
+ } else if (ntohl(mask) <= 0xffffffe0) {
iface->dhcpv4_start.s_addr = start | htonl(10);
iface->dhcpv4_end.s_addr = end | htonl(30);
} else {
iface->dhcpv4_start.s_addr = start | htonl(10);
iface->dhcpv4_end.s_addr = end | htonl(30);
} else {
// Clean invalid assignments
struct dhcpv4_assignment *a, *n;
list_for_each_entry_safe(a, n, &iface->dhcpv4_assignments, head) {
// Clean invalid assignments
struct dhcpv4_assignment *a, *n;
list_for_each_entry_safe(a, n, &iface->dhcpv4_assignments, head) {
- if ((htonl(a->addr) & smask->sin_addr.s_addr) !=
- (iface->dhcpv4_start.s_addr & smask->sin_addr.s_addr))
+ if ((htonl(a->addr) & mask) !=
+ (iface->dhcpv4_start.s_addr & mask))
free_dhcpv4_assignment(a);
}
free_dhcpv4_assignment(a);
}
req->op != DHCPV4_BOOTREQUEST || req->hlen != 6)
return;
req->op != DHCPV4_BOOTREQUEST || req->hlen != 6)
return;
- int sock = iface->dhcpv4_event.uloop.fd;
- struct sockaddr_in ifaddr;
- struct sockaddr_in ifnetmask;
syslog(LOG_NOTICE, "Got DHCPv4 request");
syslog(LOG_NOTICE, "Got DHCPv4 request");
- struct ifreq ifreq;
- memcpy(ifreq.ifr_name, iface->ifname, sizeof(ifreq.ifr_name));
- if (ioctl(sock, SIOCGIFADDR, &ifreq)) {
- syslog(LOG_WARNING, "DHCPv4 failed to detect address: %s", strerror(errno));
+ if (!iface->addr4_len) {
+ syslog(LOG_WARNING, "DHCPv4 no address found on %s", iface->name);
- memcpy(&ifaddr, &ifreq.ifr_addr, sizeof(ifaddr));
- if (ioctl(sock, SIOCGIFNETMASK, &ifreq))
- return;
-
- memcpy(&ifnetmask, &ifreq.ifr_netmask, sizeof(ifnetmask));
- uint32_t network = ifaddr.sin_addr.s_addr & ifnetmask.sin_addr.s_addr;
+ struct in_addr *ifaddr = &iface->addr4[0].addr.in;
+ struct in_addr *ifbroadcast = &iface->addr4[0].broadcast;
+ uint32_t mask = iface->addr4[0].prefix ? htonl(~((1 << (32 - iface->addr4[0].prefix)) - 1)) : 0;
+ uint32_t network = iface->addr4[0].addr.in.s_addr & mask;
+ int sock = iface->dhcpv4_event.uloop.fd;
- if ((iface->dhcpv4_start.s_addr & ifnetmask.sin_addr.s_addr) != network ||
- (iface->dhcpv4_end.s_addr & ifnetmask.sin_addr.s_addr) != network) {
+ if ((iface->dhcpv4_start.s_addr & mask) != network ||
+ (iface->dhcpv4_end.s_addr & mask) != network) {
syslog(LOG_WARNING, "DHCPv4 range out of assigned network");
return;
}
syslog(LOG_WARNING, "DHCPv4 range out of assigned network");
return;
}
- struct ifreq ifr = {.ifr_name = ""};
- strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name));
-
struct dhcpv4_message reply = {
.op = DHCPV4_BOOTREPLY,
.htype = 1,
struct dhcpv4_message reply = {
.op = DHCPV4_BOOTREPLY,
.htype = 1,
.flags = req->flags,
.ciaddr = {INADDR_ANY},
.giaddr = req->giaddr,
.flags = req->flags,
.ciaddr = {INADDR_ANY},
.giaddr = req->giaddr,
- .siaddr = ifaddr.sin_addr,
};
memcpy(reply.chaddr, req->chaddr, sizeof(reply.chaddr));
};
memcpy(reply.chaddr, req->chaddr, sizeof(reply.chaddr));
} else if (opt->type == DHCPV4_OPT_IPADDRESS && opt->len == 4)
memcpy(&reqaddr, opt->data, 4);
else if (opt->type == DHCPV4_OPT_SERVERID && opt->len == 4) {
} else if (opt->type == DHCPV4_OPT_IPADDRESS && opt->len == 4)
memcpy(&reqaddr, opt->data, 4);
else if (opt->type == DHCPV4_OPT_SERVERID && opt->len == 4) {
- if (memcmp(opt->data, &ifaddr.sin_addr, 4))
+ if (memcmp(opt->data, ifaddr, 4))
return;
} else if (iface->filter_class && opt->type == DHCPV4_OPT_USER_CLASS) {
uint8_t *c = opt->data, *cend = &opt->data[opt->len];
return;
} else if (iface->filter_class && opt->type == DHCPV4_OPT_USER_CLASS) {
uint8_t *c = opt->data, *cend = &opt->data[opt->len];
return;
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_MESSAGE, 1, &msg);
return;
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_MESSAGE, 1, &msg);
- dhcpv4_put(&reply, &cookie, DHCPV4_OPT_SERVERID, 4, &ifaddr.sin_addr);
+ dhcpv4_put(&reply, &cookie, DHCPV4_OPT_SERVERID, 4, ifaddr);
if (lease) {
uint32_t val;
if (lease) {
uint32_t val;
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_REBIND, 4, &val);
}
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_REBIND, 4, &val);
}
- dhcpv4_put(&reply, &cookie, DHCPV4_OPT_NETMASK, 4, &ifnetmask.sin_addr);
+ dhcpv4_put(&reply, &cookie, DHCPV4_OPT_NETMASK, 4, &mask);
if (lease->hostname)
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_HOSTNAME,
strlen(lease->hostname), lease->hostname);
if (lease->hostname)
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_HOSTNAME,
strlen(lease->hostname), lease->hostname);
- if (!ioctl(sock, SIOCGIFBRDADDR, &ifr)) {
- struct sockaddr_in *ina = (struct sockaddr_in*)&ifr.ifr_broadaddr;
- dhcpv4_put(&reply, &cookie, DHCPV4_OPT_BROADCAST, 4, &ina->sin_addr);
- }
+ if (ifbroadcast->s_addr != INADDR_ANY)
+ dhcpv4_put(&reply, &cookie, DHCPV4_OPT_BROADCAST, 4, ifbroadcast);
+ struct ifreq ifr = {.ifr_name = ""};
+ strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name));
+
if (!ioctl(sock, SIOCGIFMTU, &ifr)) {
uint16_t mtu = htons(ifr.ifr_mtu);
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_MTU, 2, &mtu);
if (!ioctl(sock, SIOCGIFMTU, &ifr)) {
uint16_t mtu = htons(ifr.ifr_mtu);
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_MTU, 2, &mtu);
}
if (iface->dhcpv4_router_cnt == 0)
}
if (iface->dhcpv4_router_cnt == 0)
- dhcpv4_put(&reply, &cookie, DHCPV4_OPT_ROUTER, 4, &ifaddr.sin_addr);
+ dhcpv4_put(&reply, &cookie, DHCPV4_OPT_ROUTER, 4, ifaddr);
else
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_ROUTER,
4 * iface->dhcpv4_router_cnt, iface->dhcpv4_router);
if (iface->dhcpv4_dns_cnt == 0)
else
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_ROUTER,
4 * iface->dhcpv4_router_cnt, iface->dhcpv4_router);
if (iface->dhcpv4_dns_cnt == 0)
- dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DNSSERVER, 4, &ifaddr.sin_addr);
+ dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DNSSERVER, 4, ifaddr);
else
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DNSSERVER,
4 * iface->dhcpv4_dns_cnt, iface->dhcpv4_dns);
else
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DNSSERVER,
4 * iface->dhcpv4_dns_cnt, iface->dhcpv4_dns);
nl_socket_modify_cb(rtnl_event.sock, NL_CB_VALID, NL_CB_CUSTOM,
cb_rtnl_valid, NULL);
nl_socket_modify_cb(rtnl_event.sock, NL_CB_VALID, NL_CB_CUSTOM,
cb_rtnl_valid, NULL);
- // Receive IPv6 address, IPv6 routes and neighbor events
- if (nl_socket_add_memberships(rtnl_event.sock, RTNLGRP_IPV6_IFADDR,
- RTNLGRP_IPV6_ROUTE, RTNLGRP_NEIGH, 0))
+ // Receive IPv4 address, IPv6 address, IPv6 routes and neighbor events
+ if (nl_socket_add_memberships(rtnl_event.sock, RTNLGRP_IPV4_IFADDR,
+ RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE,
+ RTNLGRP_NEIGH, 0))
goto err;
odhcpd_register(&rtnl_event.ev);
goto err;
odhcpd_register(&rtnl_event.ev);
}
// Check address update
}
// Check address update
+static void check_addr_updates(struct interface *iface)
+{
+ struct odhcpd_ipaddr *addr = NULL;
+ ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, false, &addr);
+
+ if (len < 0)
+ return;
+
+ bool change = len != (ssize_t)iface->addr4_len;
+ for (ssize_t i = 0; !change && i < len; ++i)
+ if (addr[i].addr.in.s_addr != iface->ia_addr[i].addr.in.s_addr)
+ change = true;
+
+ free(iface->addr4);
+ iface->addr4 = addr;
+ iface->addr4_len = len;
+}
+
+// Check v6 address update
static void check_addr6_updates(struct interface *iface)
{
struct odhcpd_ipaddr *addr = NULL;
static void check_addr6_updates(struct interface *iface)
{
struct odhcpd_ipaddr *addr = NULL;
static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
{
struct nlmsghdr *hdr = nlmsg_hdr(msg);
static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
{
struct nlmsghdr *hdr = nlmsg_hdr(msg);
- struct in6_addr *addr = NULL;
+ struct in6_addr *addr6 = NULL;
struct interface *iface = NULL;
bool add = false;
char ipbuf[INET6_ADDRSTRLEN];
struct interface *iface = NULL;
bool add = false;
char ipbuf[INET6_ADDRSTRLEN];
struct nlattr *nla[__IFA_MAX];
if (!nlmsg_valid_hdr(hdr, sizeof(*ifa)) ||
struct nlattr *nla[__IFA_MAX];
if (!nlmsg_valid_hdr(hdr, sizeof(*ifa)) ||
- ifa->ifa_family != AF_INET6)
+ (ifa->ifa_family != AF_INET6 &&
+ ifa->ifa_family != AF_INET))
return NL_SKIP;
iface = odhcpd_get_interface_by_index(ifa->ifa_index);
return NL_SKIP;
iface = odhcpd_get_interface_by_index(ifa->ifa_index);
return NL_SKIP;
nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
return NL_SKIP;
nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
- if (!nla[IFA_ADDRESS])
- return NL_SKIP;
- addr = nla_data(nla[IFA_ADDRESS]);
- if (!addr || IN6_IS_ADDR_LINKLOCAL(addr) ||
- IN6_IS_ADDR_MULTICAST(addr))
- return NL_SKIP;
+ if (ifa->ifa_family == AF_INET6) {
+ if (!nla[IFA_ADDRESS])
+ return NL_SKIP;
- inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
- syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr",
- ipbuf, iface->ifname);
+ addr6 = nla_data(nla[IFA_ADDRESS]);
+ if (!addr6 || IN6_IS_ADDR_LINKLOCAL(addr6) ||
+ IN6_IS_ADDR_MULTICAST(addr6))
+ return NL_SKIP;
- check_addr6_updates(iface);
+ inet_ntop(AF_INET6, addr6, ipbuf, sizeof(ipbuf));
+ syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr",
+ ipbuf, iface->ifname);
- if (iface->ndp != RELAYD_RELAY)
- break;
+ check_addr6_updates(iface);
- /* handle the relay logic below */
- setup_addr_for_relaying(addr, iface, add);
+ if (iface->ndp != RELAYD_RELAY)
+ break;
- if (!add)
- dump_neigh_table(false);
+ /* handle the relay logic below */
+ setup_addr_for_relaying(addr6, iface, add);
+
+ if (!add)
+ dump_neigh_table(false);
+ } else {
+ if (!nla[IFA_LOCAL])
+ return NL_SKIP;
+
+ struct in_addr *addr = nla_data(nla[IFA_ADDRESS]);
+
+ inet_ntop(AF_INET, addr, ipbuf, sizeof(ipbuf));
+ syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr",
+ ipbuf, iface->ifname);
+
+ check_addr_updates(iface);
+ }
if (!nla[NDA_DST])
return NL_SKIP;
if (!nla[NDA_DST])
return NL_SKIP;
- addr = nla_data(nla[NDA_DST]);
- if (!addr || IN6_IS_ADDR_LINKLOCAL(addr) ||
- IN6_IS_ADDR_MULTICAST(addr))
+ addr6 = nla_data(nla[NDA_DST]);
+ if (!addr6 || IN6_IS_ADDR_LINKLOCAL(addr6) ||
+ IN6_IS_ADDR_MULTICAST(addr6))
- inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
- syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newneigh" : "delneigh",
+ inet_ntop(AF_INET6, addr6, ipbuf, sizeof(ipbuf));
+ syslog(LOG_DEBUG, "Netlink %s %s%%%s", true ? "newneigh" : "delneigh",
ipbuf, iface->ifname);
if (ndm->ndm_flags & NTF_PROXY) {
/* Dump and flush proxy entries */
if (hdr->nlmsg_type == RTM_NEWNEIGH) {
ipbuf, iface->ifname);
if (ndm->ndm_flags & NTF_PROXY) {
/* Dump and flush proxy entries */
if (hdr->nlmsg_type == RTM_NEWNEIGH) {
- odhcpd_setup_proxy_neigh(addr, iface, false);
- setup_route(addr, iface, false);
+ odhcpd_setup_proxy_neigh(addr6, iface, false);
+ setup_route(addr6, iface, false);
dump_neigh_table(false);
}
dump_neigh_table(false);
}
NUD_PERMANENT | NUD_NOARP)))
return NL_OK;
NUD_PERMANENT | NUD_NOARP)))
return NL_OK;
- setup_addr_for_relaying(addr, iface, add);
- setup_route(addr, iface, add);
+ setup_addr_for_relaying(addr6, iface, add);
+ setup_route(addr6, iface, add);
if (!add)
dump_neigh_table(false);
if (!add)
dump_neigh_table(false);
struct odhcpd_ipaddr *addrs = *(ctxt->addrs);
struct nlmsghdr *hdr = nlmsg_hdr(msg);
struct ifaddrmsg *ifa;
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;
if (hdr->nlmsg_type != RTM_NEWADDR)
return NL_SKIP;
return NL_SKIP;
nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
return NL_SKIP;
nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
+
+ 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));
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;
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));
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]);
if (nla[IFA_CACHEINFO]) {
struct ifa_cacheinfo *ifc = nla_data(nla[IFA_CACHEINFO]);
-// compare prefixes
-static int prefixcmp(const void *va, const void *vb)
+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;
{
const struct odhcpd_ipaddr *a = va, *b = vb;
uint32_t a_pref = IN6_IS_ADDR_ULA(&a->addr.in6) ? 1 : a->preferred;
time_t now = odhcpd_time();
struct odhcpd_ipaddr *addr = *addrs;
time_t now = odhcpd_time();
struct odhcpd_ipaddr *addr = *addrs;
- qsort(addr, ctxt.ret, sizeof(*addr), prefixcmp);
+ 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)
for (ssize_t i = 0; i < ctxt.ret; ++i) {
if (addr[i].preferred < UINT32_MAX - now)
struct odhcpd_ipaddr {
union if_addr addr;
uint8_t prefix;
struct odhcpd_ipaddr {
union if_addr addr;
uint8_t prefix;
+ uint32_t preferred;
+ uint32_t valid;
/* ipv6 only */
uint8_t dprefix;
/* ipv6 only */
uint8_t dprefix;
- uint32_t preferred;
- uint32_t valid;
+
+ /* ipv4 only */
+ struct in_addr broadcast;
// Runtime data
struct uloop_timeout timer_rs;
struct list_head ia_assignments;
// Runtime data
struct uloop_timeout timer_rs;
struct list_head ia_assignments;
+ struct odhcpd_ipaddr *addr4;
+ size_t addr4_len;
struct odhcpd_ipaddr *ia_addr;
size_t ia_addr_len;
struct odhcpd_ipaddr *ia_addr;
size_t ia_addr_len;