struct vlist_tree leases = VLIST_TREE_INIT(leases, lease_cmp, lease_update, true, false);
AVL_TREE(interfaces, avl_strcmp, false, NULL);
struct config config = {.legacy = false, .main_dhcpv4 = false,
- .dhcp_cb = NULL, .dhcp_statefile = NULL,
+ .dhcp_cb = NULL, .dhcp_statefile = NULL, .dhcp_hostsfile = NULL,
.log_level = LOG_WARNING};
#define START_DEFAULT 100
#define HOSTID_LEN_MAX 64
#define HOSTID_LEN_DEFAULT HOSTID_LEN_MIN
+#define PD_MIN_LEN_MAX (64-2) // must delegate at least 2 bits of prefix
+
#define OAF_DHCPV6 (OAF_DHCPV6_NA | OAF_DHCPV6_PD)
enum {
IFACE_ATTR_DHCPV6_RAW,
IFACE_ATTR_DHCPV6_ASSIGNALL,
IFACE_ATTR_DHCPV6_PD,
+ IFACE_ATTR_DHCPV6_PD_MIN_LEN,
IFACE_ATTR_DHCPV6_NA,
IFACE_ATTR_DHCPV6_HOSTID_LEN,
IFACE_ATTR_RA_DEFAULT,
IFACE_ATTR_RA_HOPLIMIT,
IFACE_ATTR_RA_MTU,
IFACE_ATTR_RA_DNS,
+ IFACE_ATTR_RA_PREF64,
IFACE_ATTR_PD_MANAGER,
IFACE_ATTR_PD_CER,
IFACE_ATTR_NDPROXY_ROUTING,
[IFACE_ATTR_DHCPV6_RAW] = { .name = "dhcpv6_raw", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DHCPV6_ASSIGNALL] = { .name ="dhcpv6_assignall", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_DHCPV6_PD] = { .name = "dhcpv6_pd", .type = BLOBMSG_TYPE_BOOL },
+ [IFACE_ATTR_DHCPV6_PD_MIN_LEN] = { .name = "dhcpv6_pd_min_len", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_DHCPV6_NA] = { .name = "dhcpv6_na", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_DHCPV6_HOSTID_LEN] = { .name = "dhcpv6_hostidlength", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_PD_MANAGER] = { .name = "pd_manager", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_RA_HOPLIMIT] = { .name = "ra_hoplimit", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_RA_MTU] = { .name = "ra_mtu", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_RA_DNS] = { .name = "ra_dns", .type = BLOBMSG_TYPE_BOOL },
+ [IFACE_ATTR_RA_PREF64] = { .name = "ra_pref64", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_NDPROXY_ROUTING] = { .name = "ndproxy_routing", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter", .type = BLOBMSG_TYPE_STRING },
ODHCPD_ATTR_LEASEFILE,
ODHCPD_ATTR_LEASETRIGGER,
ODHCPD_ATTR_LOGLEVEL,
+ ODHCPD_ATTR_HOSTSFILE,
ODHCPD_ATTR_MAX
};
[ODHCPD_ATTR_LEASEFILE] = { .name = "leasefile", .type = BLOBMSG_TYPE_STRING },
[ODHCPD_ATTR_LEASETRIGGER] = { .name = "leasetrigger", .type = BLOBMSG_TYPE_STRING },
[ODHCPD_ATTR_LOGLEVEL] = { .name = "loglevel", .type = BLOBMSG_TYPE_INT32 },
+ [ODHCPD_ATTR_HOSTSFILE] = { .name = "hostsfile", .type = BLOBMSG_TYPE_STRING },
};
const struct uci_blob_param_list odhcpd_attr_list = {
iface->dhcpv4_end.s_addr = htonl(START_DEFAULT + LIMIT_DEFAULT - 1);
iface->dhcpv6_assignall = true;
iface->dhcpv6_pd = true;
+ iface->dhcpv6_pd_min_len = 0;
iface->dhcpv6_na = true;
iface->dhcpv6_hostid_len = HOSTID_LEN_DEFAULT;
iface->dns_service = true;
config.dhcp_statefile = strdup(blobmsg_get_string(c));
}
+ if ((c = tb[ODHCPD_ATTR_HOSTSFILE])) {
+ free(config.dhcp_hostsfile);
+ config.dhcp_hostsfile = strdup(blobmsg_get_string(c));
+ }
+
if ((c = tb[ODHCPD_ATTR_LEASETRIGGER])) {
free(config.dhcp_cb);
config.dhcp_cb = strdup(blobmsg_get_string(c));
int config_parse_interface(void *data, size_t len, const char *name, bool overwrite)
{
+ struct odhcpd_ipaddr *addrs = NULL;
struct interface *iface;
struct blob_attr *tb[IFACE_ATTR_MAX], *c;
+ ssize_t addrs_len;
bool get_addrs = false;
int mode;
const char *ifname = NULL;
if (!iface->ifindex &&
(iface->ifindex = if_nametoindex(iface->ifname)) <= 0)
goto err;
+
+ if ((iface->ifflags = odhcpd_get_flags(iface)) < 0)
+ goto err;
}
if (get_addrs) {
- ssize_t len = netlink_get_interface_addrs(iface->ifindex,
+ addrs_len = netlink_get_interface_addrs(iface->ifindex,
true, &iface->addr6);
- if (len > 0)
- iface->addr6_len = len;
+ if (addrs_len > 0)
+ iface->addr6_len = addrs_len;
- len = netlink_get_interface_addrs(iface->ifindex,
+ addrs_len = netlink_get_interface_addrs(iface->ifindex,
false, &iface->addr4);
- if (len > 0)
- iface->addr4_len = len;
+ if (addrs_len > 0)
+ iface->addr4_len = addrs_len;
+ }
+
+ addrs_len = netlink_get_interface_linklocal(iface->ifindex, &addrs);
+ if (addrs_len > 0) {
+ for (ssize_t i = 0; i < addrs_len; i++) {
+ struct odhcpd_ipaddr *addr = &addrs[i];
+
+ if (!addr->tentative) {
+ iface->have_link_local = true;
+ break;
+ }
+ }
}
iface->inuse = true;
if ((c = tb[IFACE_ATTR_DHCPV6_PD]))
iface->dhcpv6_pd = blobmsg_get_bool(c);
+ if ((c = tb[IFACE_ATTR_DHCPV6_PD_MIN_LEN])) {
+ uint32_t pd_min_len = blobmsg_get_u32(c);
+ if (pd_min_len != 0 && pd_min_len <= PD_MIN_LEN_MAX)
+ iface->dhcpv6_pd_min_len = pd_min_len;
+ else
+ syslog(LOG_ERR, "Invalid %s value configured for interface '%s'",
+ iface_attrs[IFACE_ATTR_DHCPV6_PD_MIN_LEN].name, iface->name);
+ }
+
if ((c = tb[IFACE_ATTR_DHCPV6_NA]))
iface->dhcpv6_na = blobmsg_get_bool(c);
if ((c = tb[IFACE_ATTR_RA_DNS]))
iface->ra_dns = blobmsg_get_bool(c);
+ if ((c = tb[IFACE_ATTR_RA_PREF64])) {
+ const char *str = blobmsg_get_string(c);
+ char *astr = malloc(strlen(str) + 1);
+ char *delim;
+ int l;
+
+ if (!astr || !strcpy(astr, str) ||
+ (delim = strchr(astr, '/')) == NULL || (*(delim++) = 0) ||
+ sscanf(delim, "%i", &l) == 0 || l > 128 ||
+ inet_pton(AF_INET6, astr, &iface->pref64_addr) == 0)
+ iface->pref64_length = 0;
+ else
+ iface->pref64_length = l;
+
+ if (astr)
+ free(astr);
+ }
+
if ((c = tb[IFACE_ATTR_RA_PREFERENCE])) {
const char *prio = blobmsg_get_string(c);
return NULL;
}
+void reload_services(struct interface *iface)
+{
+ if (iface->ifflags & IFF_RUNNING) {
+ syslog(LOG_DEBUG, "Enabling services with %s running", iface->ifname);
+ router_setup_interface(iface, iface->ra != MODE_DISABLED);
+ dhcpv6_setup_interface(iface, iface->dhcpv6 != MODE_DISABLED);
+ ndp_setup_interface(iface, iface->ndp != MODE_DISABLED);
+#ifdef DHCPV4_SUPPORT
+ dhcpv4_setup_interface(iface, iface->dhcpv4 != MODE_DISABLED);
+#endif
+ } else {
+ syslog(LOG_DEBUG, "Disabling services with %s not running", iface->ifname);
+ router_setup_interface(iface, false);
+ dhcpv6_setup_interface(iface, false);
+ ndp_setup_interface(iface, false);
+#ifdef DHCPV4_SUPPORT
+ dhcpv4_setup_interface(iface, false);
+#endif
+ }
+}
+
void odhcpd_reload(void)
{
struct uci_context *uci = uci_alloc_context();
avl_for_each_element_safe(&interfaces, i, avl, tmp) {
- if (i->inuse) {
+ if (i->inuse && i->ifflags & IFF_RUNNING) {
/* Resolve hybrid mode */
if (i->dhcpv6 == MODE_HYBRID)
i->dhcpv6 = (master && master->dhcpv6 == MODE_RELAY) ?
i->ndp = (master && master->ndp == MODE_RELAY) ?
MODE_RELAY : MODE_DISABLED;
- router_setup_interface(i, i->ra != MODE_DISABLED);
- dhcpv6_setup_interface(i, i->dhcpv6 != MODE_DISABLED);
- ndp_setup_interface(i, i->ndp != MODE_DISABLED);
-#ifdef DHCPV4_SUPPORT
- dhcpv4_setup_interface(i, i->dhcpv4 != MODE_DISABLED);
-#endif
+ reload_services(i);
} else
close_interface(i);
}