add hostsfile output in addition to statefile
[project/odhcpd.git] / src / config.c
index 4e3db86596b82548b8f3ac48dd73164a5cb260c1..5ba4d9ecd4dcbfc9e63d1a6ed534c520bfd201ef 100644 (file)
@@ -29,7 +29,7 @@ static void lease_update(struct vlist_tree *tree, struct vlist_node *node_new,
 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
@@ -39,6 +39,8 @@ struct config config = {.legacy = false, .main_dhcpv4 = false,
 #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 {
@@ -64,6 +66,7 @@ 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,
@@ -82,6 +85,7 @@ enum {
        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,
@@ -115,6 +119,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [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 },
@@ -135,6 +140,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [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 },
@@ -174,6 +180,7 @@ enum {
        ODHCPD_ATTR_LEASEFILE,
        ODHCPD_ATTR_LEASETRIGGER,
        ODHCPD_ATTR_LOGLEVEL,
+       ODHCPD_ATTR_HOSTSFILE,
        ODHCPD_ATTR_MAX
 };
 
@@ -183,6 +190,7 @@ static const struct blobmsg_policy odhcpd_attrs[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 = {
@@ -212,6 +220,7 @@ static void set_interface_defaults(struct interface *iface)
        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;
@@ -319,6 +328,11 @@ static void set_config(struct uci_section *s)
                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));
@@ -518,8 +532,10 @@ static int parse_ntp_fqdn(uint16_t *dhcpv6_ntp_len, char *fqdn, uint8_t **dhcpv6
 
 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;
@@ -582,19 +598,34 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
                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;
@@ -832,6 +863,15 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
        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);
 
@@ -941,6 +981,24 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
        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);
 
@@ -1197,6 +1255,27 @@ struct lease *config_find_lease_by_ipaddr(const uint32_t ipaddr)
        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();
@@ -1300,7 +1379,7 @@ void odhcpd_reload(void)
 
 
        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) ?
@@ -1314,12 +1393,7 @@ void odhcpd_reload(void)
                                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);
        }