int config_parse_interface(void *data, size_t len, const char *name, bool overwrite)
{
struct blob_attr *tb[IFACE_ATTR_MAX], *c;
+ bool get_addrs = false;
+
blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, data, len);
if (tb[IFACE_ATTR_INTERFACE])
set_interface_defaults(iface);
list_add(&iface->head, &interfaces);
- overwrite = true;
+ get_addrs = overwrite = true;
}
const char *ifname = NULL;
if ((iface->ifindex = if_nametoindex(iface->ifname)) <= 0)
goto err;
+ if (get_addrs) {
+ ssize_t len = odhcpd_get_interface_addresses(iface->ifindex,
+ true, &iface->ia_addr);
+
+ if (len > 0)
+ iface->ia_addr_len = len;
+ }
+
iface->inuse = true;
if ((c = tb[IFACE_ATTR_DYNAMICDHCP]))
close_interface(i);
}
- ndp_handle_addr6_dump();
uci_unload(uci, dhcp);
uci_free_context(uci);
}
if (!valid_addr(&addrs[i], now))
continue;
- addr = addrs[i].addr;
+ addr = addrs[i].addr.in6;
pref = addrs[i].preferred;
valid = addrs[i].valid;
if (prefix == 128) {
size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->ia_addr_len;
for (size_t i = 0; i < addrlen; ++i) {
- struct in6_addr prefix = addrs[i].addr;
+ struct in6_addr prefix = addrs[i].addr.in6;
prefix.s6_addr32[1] |= htonl(a->assigned);
prefix.s6_addr32[2] = prefix.s6_addr32[3] = 0;
odhcpd_setup_route(&prefix, (a->managed_size) ? addrs[i].prefix : a->length,
apply_lease(iface, c, false);
}
-void dhcpv6_ia_postupdate(struct interface *iface, time_t now)
+void dhcpv6_ia_postupdate(struct interface *iface)
{
if (iface->dhcpv6 != RELAYD_SERVER)
return;
+ time_t now = odhcpd_time();
int minprefix = -1;
for (size_t i = 0; i < iface->ia_addr_len; ++i) {
if (iface->ia_addr[i].preferred > (uint32_t)now &&
.preferred = htonl(prefix_pref),
.valid = htonl(prefix_valid),
.prefix = (a->managed_size) ? addrs[i].prefix : a->length,
- .addr = addrs[i].addr
+ .addr = addrs[i].addr.in6,
};
p.addr.s6_addr32[1] |= htonl(a->assigned);
p.addr.s6_addr32[2] = p.addr.s6_addr32[3] = 0;
struct dhcpv6_ia_addr n = {
.type = htons(DHCPV6_OPT_IA_ADDR),
.len = htons(sizeof(n) - 4),
- .addr = addrs[i].addr,
+ .addr = addrs[i].addr.in6,
.preferred = htonl(prefix_pref),
.valid = htonl(prefix_valid)
};
if (!valid_addr(&addrs[i], now))
continue;
- struct in6_addr addr = addrs[i].addr;
+ struct in6_addr addr = addrs[i].addr.in6;
if (ia->type == htons(DHCPV6_OPT_IA_PD)) {
addr.s6_addr32[1] |= htonl(a->assigned);
addr.s6_addr32[2] = addr.s6_addr32[3] = 0;
if (iface->dhcpv6 == RELAYD_SERVER)
setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &server, sizeof(server));
- if (iface->dhcpv6 != RELAYD_RELAY || !iface->master)
- ndp_rqs_addr6_dump();
-
iface->dhcpv6_event.uloop.fd = sock;
iface->dhcpv6_event.handle_dgram = handle_dhcpv6;
odhcpd_register(&iface->dhcpv6_event);
if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)) {
struct odhcpd_ipaddr *addrs;
- ssize_t len = odhcpd_get_interface_addresses(0, &addrs);
+ ssize_t len = odhcpd_get_interface_addresses(0, true, &addrs);
for (ssize_t i = 0; i < len; ++i)
if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)
odhcpd_send(iface->dhcpv6_event.uloop.fd, &target, &iov, 1, iface);
}
+static struct odhcpd_ipaddr *relay_link_address(struct interface *iface)
+{
+ struct odhcpd_ipaddr *addr = NULL;
+ time_t now = odhcpd_time();
+
+ for (size_t i = 0; i < iface->ia_addr_len; i++) {
+ if (iface->ia_addr[i].valid <= (uint32_t)now)
+ continue;
+
+ if (iface->ia_addr[i].preferred > (uint32_t)now) {
+ addr = &iface->ia_addr[i];
+ break;
+ }
+
+ if (!addr || (iface->ia_addr[i].valid > addr->valid))
+ addr = &iface->ia_addr[i];
+ }
+
+ return addr;
+}
// Relay client request (regular DHCPv6-relay)
static void relay_client_request(struct sockaddr_in6 *source,
memcpy(&hdr.interface_id_data, &ifindex, sizeof(ifindex));
// Detect public IP of slave interface to use as link-address
- struct odhcpd_ipaddr *ip = NULL;
- if (odhcpd_get_interface_addresses(iface->ifindex, &ip) < 1) {
+ struct odhcpd_ipaddr *ip = relay_link_address(iface);
+ if (!ip) {
// No suitable address! Is the slave not configured yet?
// Detect public IP of master interface and use it instead
// This is WRONG and probably violates the RFC. However
// otherwise we have a hen and egg problem because the
// slave-interface cannot be auto-configured.
- if (odhcpd_get_interface_addresses(master->ifindex, &ip) < 1)
+ ip = relay_link_address(master);
+ if (!ip)
return; // Could not obtain a suitable address
}
- memcpy(&hdr.link_address, &ip[0].addr, sizeof(hdr.link_address));
- free(ip);
+
+ memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address));
struct sockaddr_in6 dhcpv6_servers = {AF_INET6,
htons(DHCPV6_SERVER_PORT), 0, ALL_DHCPV6_SERVERS, 0};
dhcpv6_binding_cb_handler_t func, void *arg);
void dhcpv6_write_statefile(void);
void dhcpv6_ia_preupdate(struct interface *iface);
-void dhcpv6_ia_postupdate(struct interface *iface, time_t now);
+void dhcpv6_ia_postupdate(struct interface *iface);
static int cb_rtnl_valid(struct nl_msg *msg, void *arg);
static void catch_rtnl_err(struct odhcpd_event *e, int error);
-static int addr6_dump_rqs = 0;
static int ping_socket = -1;
static struct event_socket rtnl_event = {
.ev = {
nlmsg_free(msg);
}
-static void dump_addr6_table(void)
+static void dump_addr_table(bool v6)
{
struct nl_msg *msg;
struct ifaddrmsg ifa = {
- .ifa_family = AF_INET6,
+ .ifa_family = v6 ? AF_INET6 : AF_INET,
};
msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP);
nlmsg_free(msg);
}
-void ndp_handle_addr6_dump(void)
-{
- if (!addr6_dump_rqs)
- return;
-
- dump_addr6_table();
- addr6_dump_rqs = 0;
-}
-
-inline void ndp_rqs_addr6_dump(void)
-{
- addr6_dump_rqs++;
-}
-
int setup_ndp_interface(struct interface *iface, bool enable)
{
int ret = 0, procfd;
dump_neigh_table(false);
else
dump_neigh = false;
-
- ndp_rqs_addr6_dump();
}
if (dump_neigh)
odhcpd_setup_route(addr, 128, iface, NULL, 1024, add);
}
-// compare prefixes
-static int prefixcmp(const void *va, const void *vb)
-{
- const struct odhcpd_ipaddr *a = va, *b = vb;
- uint32_t a_pref = IN6_IS_ADDR_ULA(&a->addr) ? 1 : a->preferred;
- uint32_t b_pref = IN6_IS_ADDR_ULA(&b->addr) ? 1 : b->preferred;
- return (a_pref < b_pref) ? 1 : (a_pref > b_pref) ? -1 : 0;
-}
-
// Check address update
-static void check_addr_updates(struct interface *iface)
+static void check_addr6_updates(struct interface *iface)
{
struct odhcpd_ipaddr *addr = NULL;
- time_t now = odhcpd_time();
- ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, &addr);
+ ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, true, &addr);
if (len < 0)
return;
- qsort(addr, len, sizeof(*addr), prefixcmp);
-
- for (int i = 0; i < len; ++i) {
- if (addr[i].preferred < UINT32_MAX - now)
- addr[i].preferred += now;
-
- if (addr[i].valid < UINT32_MAX - now)
- addr[i].valid += now;
- }
-
bool change = len != (ssize_t)iface->ia_addr_len;
for (ssize_t i = 0; !change && i < len; ++i)
- if (!IN6_ARE_ADDR_EQUAL(&addr[i].addr, &iface->ia_addr[i].addr) ||
+ if (!IN6_ARE_ADDR_EQUAL(&addr[i].addr.in6, &iface->ia_addr[i].addr.in6) ||
(addr[i].preferred > 0) != (iface->ia_addr[i].preferred > 0) ||
addr[i].valid < iface->ia_addr[i].valid ||
addr[i].preferred < iface->ia_addr[i].preferred)
iface->ia_addr_len = len;
if (change)
- dhcpv6_ia_postupdate(iface, now);
+ dhcpv6_ia_postupdate(iface);
if (change) {
syslog(LOG_INFO, "Raising SIGUSR1 due to address change on %s", iface->ifname);
syslog(LOG_DEBUG, "Netlink %s %s%%%s", true ? "newaddr" : "deladdr",
ipbuf, iface->ifname);
- check_addr_updates(iface);
+ check_addr6_updates(iface);
if (iface->ndp != RELAYD_RELAY)
break;
if (nl_socket_set_buffer_size(ev_sock->sock, ev_sock->sock_bufsize, 0))
goto err;
- dump_addr6_table();
+ dump_addr_table(true);
return;
err:
struct addr_info {
int ifindex;
+ int af;
struct odhcpd_ipaddr **addrs;
int pending;
ssize_t ret;
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;
return NL_STOP;
}
+// compare prefixes
+static int prefixcmp(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), prefixcmp);
+
+ 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;
}
void (*recv_msgs)(struct odhcpd_event *e);
};
+union if_addr {
+ struct in_addr in;
+ struct in6_addr in6;
+};
struct odhcpd_ipaddr {
- struct in6_addr addr;
+ union if_addr addr;
uint8_t prefix;
+
+ /* ipv6 only */
uint8_t dprefix;
uint32_t preferred;
uint32_t valid;
ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest,
struct iovec *iov, size_t iov_len,
const struct interface *iface);
-ssize_t odhcpd_get_interface_addresses(int ifindex,
+ssize_t odhcpd_get_interface_addresses(int ifindex, bool v6,
struct odhcpd_ipaddr **addrs);
int odhcpd_get_interface_dns_addr(const struct interface *iface,
struct in6_addr *addr);
int config_parse_interface(void *data, size_t len, const char *iname, bool overwrite);
-void ndp_handle_addr6_dump(void);
-void ndp_rqs_addr6_dump(void);
-
#ifdef WITH_UBUS
int init_ubus(void);
const char* ubus_get_ifname(const char *name);
} else if (iface->ra == RELAYD_SERVER && !iface->master) {
iface->timer_rs.cb = trigger_router_advert;
uloop_timeout_set(&iface->timer_rs, 1000);
- ndp_rqs_addr6_dump();
}
if (iface->ra == RELAYD_RELAY || (iface->ra == RELAYD_SERVER && !iface->master))
char line[512], ifname[16];
bool found_default = false;
- struct odhcpd_ipaddr p = {IN6ADDR_ANY_INIT, 0, 0, 0, 0};
+ struct odhcpd_ipaddr p = { .addr.in6 = IN6ADDR_ANY_INIT, .prefix = 0,
+ .dprefix = 0, .preferred = 0, .valid = 0};
+
while (fgets(line, sizeof(line), fp_route)) {
uint32_t rflags;
if (sscanf(line, "00000000000000000000000000000000 00 "
found_default = true;
} else if (sscanf(line, "%8" SCNx32 "%8" SCNx32 "%*8" SCNx32 "%*8" SCNx32 " %hhx %*s "
"%*s 00000000000000000000000000000000 %*s %*s %*s %" SCNx32 " lo",
- &p.addr.s6_addr32[0], &p.addr.s6_addr32[1], &p.prefix, &rflags) &&
+ &p.addr.in6.s6_addr32[0], &p.addr.in6.s6_addr32[1], &p.prefix, &rflags) &&
p.prefix > 0 && (rflags & RTF_NONEXTHOP) && (rflags & RTF_REJECT)) {
// Find source prefixes by scanning through unreachable-routes
- p.addr.s6_addr32[0] = htonl(p.addr.s6_addr32[0]);
- p.addr.s6_addr32[1] = htonl(p.addr.s6_addr32[1]);
+ p.addr.in6.s6_addr32[0] = htonl(p.addr.in6.s6_addr32[0]);
+ p.addr.in6.s6_addr32[1] = htonl(p.addr.in6.s6_addr32[1]);
for (ssize_t i = 0; i < len; ++i) {
if (n[i].prefix <= 64 && n[i].prefix >= p.prefix &&
- !odhcpd_bmemcmp(&p.addr, &n[i].addr, p.prefix)) {
+ !odhcpd_bmemcmp(&p.addr.in6, &n[i].addr.in6, p.prefix)) {
n[i].dprefix = p.prefix;
break;
}
if (addr->prefix > 96 || addr->valid <= (uint32_t)now) {
char namebuf[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6, addr, namebuf, sizeof(namebuf));
+ inet_ntop(AF_INET6, &addr->addr.in6, namebuf, sizeof(namebuf));
syslog(LOG_INFO, "Address %s (prefix %d, valid %u) not suitable as RA prefix on %s",
namebuf, addr->prefix, addr->valid, iface->ifname);
continue;
for (size_t i = 0; i < pfxs_cnt; ++i) {
if (addr->prefix == pfxs[i].nd_opt_pi_prefix_len &&
!odhcpd_bmemcmp(&pfxs[i].nd_opt_pi_prefix,
- &addr->addr, addr->prefix))
+ &addr->addr.in6, addr->prefix))
p = &pfxs[i];
}
if (minvalid > valid)
minvalid = valid;
- if (!IN6_IS_ADDR_ULA(&addr->addr) || iface->default_router)
+ if (!IN6_IS_ADDR_ULA(&addr->addr.in6) || iface->default_router)
valid_prefix = true;
- odhcpd_bmemcpy(&p->nd_opt_pi_prefix, &addr->addr,
+ odhcpd_bmemcpy(&p->nd_opt_pi_prefix, &addr->addr.in6,
(iface->ra_advrouter) ? 128 : addr->prefix);
p->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
p->nd_opt_pi_len = 4;
(addr->dprefix == 64 && addr->prefix == 64)) {
continue; // Address not suitable
} else if (addr->dprefix > 32) {
- addr->addr.s6_addr32[1] &= htonl(~((1U << (64 - addr->dprefix)) - 1));
+ addr->addr.in6.s6_addr32[1] &= htonl(~((1U << (64 - addr->dprefix)) - 1));
} else if (addr->dprefix <= 32) {
- addr->addr.s6_addr32[0] &= htonl(~((1U << (32 - addr->dprefix)) - 1));
- addr->addr.s6_addr32[1] = 0;
+ addr->addr.in6.s6_addr32[0] &= htonl(~((1U << (32 - addr->dprefix)) - 1));
+ addr->addr.in6.s6_addr32[1] = 0;
}
tmp = realloc(routes, sizeof(*routes) * (routes_cnt + 1));
else if (iface->route_preference > 0)
routes[routes_cnt].flags |= ND_RA_PREF_HIGH;
routes[routes_cnt].lifetime = htonl(TIME_LEFT(addr->valid, now));
- routes[routes_cnt].addr[0] = addr->addr.s6_addr32[0];
- routes[routes_cnt].addr[1] = addr->addr.s6_addr32[1];
+ routes[routes_cnt].addr[0] = addr->addr.in6.s6_addr32[0];
+ routes[routes_cnt].addr[1] = addr->addr.in6.s6_addr32[1];
routes[routes_cnt].addr[2] = 0;
routes[routes_cnt].addr[3] = 0;