dc1dcdcaa0d9e12d628540479929f272b8552c6e
[project/odhcpd.git] / src / config.c
1 #include <fcntl.h>
2 #include <resolv.h>
3 #include <signal.h>
4 #include <arpa/inet.h>
5 #include <unistd.h>
6
7 #include <uci.h>
8 #include <uci_blob.h>
9
10 #include "odhcpd.h"
11
12 static struct blob_buf b;
13 static int reload_pipe[2];
14 struct list_head leases = LIST_HEAD_INIT(leases);
15 struct list_head interfaces = LIST_HEAD_INIT(interfaces);
16 struct config config = {false, NULL, NULL};
17
18 enum {
19         IFACE_ATTR_INTERFACE,
20         IFACE_ATTR_IFNAME,
21         IFACE_ATTR_NETWORKID,
22         IFACE_ATTR_DYNAMICDHCP,
23         IFACE_ATTR_IGNORE,
24         IFACE_ATTR_LEASETIME,
25         IFACE_ATTR_LIMIT,
26         IFACE_ATTR_START,
27         IFACE_ATTR_MASTER,
28         IFACE_ATTR_UPSTREAM,
29         IFACE_ATTR_RA,
30         IFACE_ATTR_DHCPV4,
31         IFACE_ATTR_DHCPV6,
32         IFACE_ATTR_NDP,
33         IFACE_ATTR_ROUTER,
34         IFACE_ATTR_DNS,
35         IFACE_ATTR_DOMAIN,
36         IFACE_ATTR_FILTER_CLASS,
37         IFACE_ATTR_DHCPV6_RAW,
38         IFACE_ATTR_RA_DEFAULT,
39         IFACE_ATTR_RA_MANAGEMENT,
40         IFACE_ATTR_RA_OFFLINK,
41         IFACE_ATTR_RA_PREFERENCE,
42         IFACE_ATTR_RA_ADVROUTER,
43         IFACE_ATTR_PD_MANAGER,
44         IFACE_ATTR_PD_CER,
45         IFACE_ATTR_NDPROXY_ROUTING,
46         IFACE_ATTR_NDPROXY_SLAVE,
47         IFACE_ATTR_NDPROXY_STATIC,
48         IFACE_ATTR_MAX
49 };
50
51 static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
52         [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
53         [IFACE_ATTR_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
54         [IFACE_ATTR_NETWORKID] = { .name = "networkid", .type = BLOBMSG_TYPE_STRING },
55         [IFACE_ATTR_DYNAMICDHCP] = { .name = "dynamicdhcp", .type = BLOBMSG_TYPE_BOOL },
56         [IFACE_ATTR_IGNORE] = { .name = "ignore", .type = BLOBMSG_TYPE_BOOL },
57         [IFACE_ATTR_LEASETIME] = { .name = "leasetime", .type = BLOBMSG_TYPE_STRING },
58         [IFACE_ATTR_START] = { .name = "start", .type = BLOBMSG_TYPE_INT32 },
59         [IFACE_ATTR_LIMIT] = { .name = "limit", .type = BLOBMSG_TYPE_INT32 },
60         [IFACE_ATTR_MASTER] = { .name = "master", .type = BLOBMSG_TYPE_BOOL },
61         [IFACE_ATTR_UPSTREAM] = { .name = "upstream", .type = BLOBMSG_TYPE_ARRAY },
62         [IFACE_ATTR_RA] = { .name = "ra", .type = BLOBMSG_TYPE_STRING },
63         [IFACE_ATTR_DHCPV4] = { .name = "dhcpv4", .type = BLOBMSG_TYPE_STRING },
64         [IFACE_ATTR_DHCPV6] = { .name = "dhcpv6", .type = BLOBMSG_TYPE_STRING },
65         [IFACE_ATTR_NDP] = { .name = "ndp", .type = BLOBMSG_TYPE_STRING },
66         [IFACE_ATTR_ROUTER] = { .name = "router", .type = BLOBMSG_TYPE_ARRAY },
67         [IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
68         [IFACE_ATTR_DOMAIN] = { .name = "domain", .type = BLOBMSG_TYPE_ARRAY },
69         [IFACE_ATTR_FILTER_CLASS] = { .name = "filter_class", .type = BLOBMSG_TYPE_STRING },
70         [IFACE_ATTR_DHCPV6_RAW] = { .name = "dhcpv6_raw", .type = BLOBMSG_TYPE_STRING },
71         [IFACE_ATTR_PD_MANAGER] = { .name = "pd_manager", .type = BLOBMSG_TYPE_STRING },
72         [IFACE_ATTR_PD_CER] = { .name = "pd_cer", .type = BLOBMSG_TYPE_STRING },
73         [IFACE_ATTR_RA_DEFAULT] = { .name = "ra_default", .type = BLOBMSG_TYPE_INT32 },
74         [IFACE_ATTR_RA_MANAGEMENT] = { .name = "ra_management", .type = BLOBMSG_TYPE_INT32 },
75         [IFACE_ATTR_RA_OFFLINK] = { .name = "ra_offlink", .type = BLOBMSG_TYPE_BOOL },
76         [IFACE_ATTR_RA_PREFERENCE] = { .name = "ra_preference", .type = BLOBMSG_TYPE_STRING },
77         [IFACE_ATTR_RA_ADVROUTER] = { .name = "ra_advrouter", .type = BLOBMSG_TYPE_BOOL },
78         [IFACE_ATTR_NDPROXY_ROUTING] = { .name = "ndproxy_routing", .type = BLOBMSG_TYPE_BOOL },
79         [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
80         [IFACE_ATTR_NDPROXY_STATIC] = { .name = "ndproxy_static", .type = BLOBMSG_TYPE_ARRAY },
81 };
82
83 static const struct uci_blob_param_info iface_attr_info[IFACE_ATTR_MAX] = {
84         [IFACE_ATTR_UPSTREAM] = { .type = BLOBMSG_TYPE_STRING },
85         [IFACE_ATTR_DNS] = { .type = BLOBMSG_TYPE_STRING },
86         [IFACE_ATTR_DOMAIN] = { .type = BLOBMSG_TYPE_STRING },
87         [IFACE_ATTR_NDPROXY_STATIC] = { .type = BLOBMSG_TYPE_STRING },
88 };
89
90 const struct uci_blob_param_list interface_attr_list = {
91         .n_params = IFACE_ATTR_MAX,
92         .params = iface_attrs,
93         .info = iface_attr_info,
94 };
95
96
97 enum {
98         LEASE_ATTR_IP,
99         LEASE_ATTR_MAC,
100         LEASE_ATTR_DUID,
101         LEASE_ATTR_HOSTID,
102         LEASE_ATTR_NAME,
103         LEASE_ATTR_MAX
104 };
105
106
107 static const struct blobmsg_policy lease_attrs[LEASE_ATTR_MAX] = {
108         [LEASE_ATTR_IP] = { .name = "ip", .type = BLOBMSG_TYPE_STRING },
109         [LEASE_ATTR_MAC] = { .name = "mac", .type = BLOBMSG_TYPE_STRING },
110         [LEASE_ATTR_DUID] = { .name = "duid", .type = BLOBMSG_TYPE_STRING },
111         [LEASE_ATTR_HOSTID] = { .name = "hostid", .type = BLOBMSG_TYPE_STRING },
112         [LEASE_ATTR_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
113 };
114
115
116 const struct uci_blob_param_list lease_attr_list = {
117         .n_params = LEASE_ATTR_MAX,
118         .params = lease_attrs,
119 };
120
121
122 enum {
123         ODHCPD_ATTR_MAINDHCP,
124         ODHCPD_ATTR_LEASEFILE,
125         ODHCPD_ATTR_LEASETRIGGER,
126         ODHCPD_ATTR_MAX
127 };
128
129
130 static const struct blobmsg_policy odhcpd_attrs[LEASE_ATTR_MAX] = {
131         [ODHCPD_ATTR_MAINDHCP] = { .name = "maindhcp", .type = BLOBMSG_TYPE_BOOL },
132         [ODHCPD_ATTR_LEASEFILE] = { .name = "leasefile", .type = BLOBMSG_TYPE_STRING },
133         [ODHCPD_ATTR_LEASETRIGGER] = { .name = "leasetrigger", .type = BLOBMSG_TYPE_STRING },
134 };
135
136
137 const struct uci_blob_param_list odhcpd_attr_list = {
138         .n_params = ODHCPD_ATTR_MAX,
139         .params = odhcpd_attrs,
140 };
141
142
143 static struct interface* get_interface(const char *name)
144 {
145         struct interface *c;
146         list_for_each_entry(c, &interfaces, head)
147                 if (!strcmp(c->name, name))
148                         return c;
149         return NULL;
150 }
151
152
153 static void clean_interface(struct interface *iface)
154 {
155         free(iface->dns);
156         free(iface->search);
157         free(iface->upstream);
158         free(iface->static_ndp);
159         free(iface->dhcpv4_router);
160         free(iface->dhcpv4_dns);
161         free(iface->dhcpv6_raw);
162         free(iface->filter_class);
163         memset(&iface->ra, 0, sizeof(*iface) - offsetof(struct interface, ra));
164 }
165
166
167 static void close_interface(struct interface *iface)
168 {
169         if (iface->head.next)
170                 list_del(&iface->head);
171
172         setup_router_interface(iface, false);
173         setup_dhcpv6_interface(iface, false);
174         setup_ndp_interface(iface, false);
175         setup_dhcpv4_interface(iface, false);
176
177         clean_interface(iface);
178         free(iface);
179 }
180
181
182 static int parse_mode(const char *mode)
183 {
184         if (!strcmp(mode, "disabled")) {
185                 return RELAYD_DISABLED;
186         } else if (!strcmp(mode, "server")) {
187                 return RELAYD_SERVER;
188         } else if (!strcmp(mode, "relay")) {
189                 return RELAYD_RELAY;
190         } else if (!strcmp(mode, "hybrid")) {
191                 return RELAYD_HYBRID;
192         } else {
193                 return -1;
194         }
195 }
196
197
198 static void set_config(struct uci_section *s)
199 {
200         struct blob_attr *tb[ODHCPD_ATTR_MAX], *c;
201
202         blob_buf_init(&b, 0);
203         uci_to_blob(&b, s, &odhcpd_attr_list);
204         blobmsg_parse(odhcpd_attrs, ODHCPD_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
205
206         if ((c = tb[ODHCPD_ATTR_MAINDHCP]))
207                 config.legacy = blobmsg_get_bool(c);
208
209         if ((c = tb[ODHCPD_ATTR_LEASEFILE])) {
210                 free(config.dhcp_statefile);
211                 config.dhcp_statefile = strdup(blobmsg_get_string(c));
212         }
213
214         if ((c = tb[ODHCPD_ATTR_LEASETRIGGER])) {
215                 free(config.dhcp_cb);
216                 config.dhcp_cb = strdup(blobmsg_get_string(c));
217         }
218 }
219
220
221 static int set_lease(struct uci_section *s)
222 {
223         struct blob_attr *tb[LEASE_ATTR_MAX], *c;
224
225         blob_buf_init(&b, 0);
226         uci_to_blob(&b, s, &lease_attr_list);
227         blobmsg_parse(lease_attrs, LEASE_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
228
229         size_t hostlen = 1;
230         if ((c = tb[LEASE_ATTR_NAME]))
231                 hostlen = blobmsg_data_len(c);
232
233         struct lease *lease = calloc(1, sizeof(*lease) + hostlen);
234         if (!lease)
235                 goto err;
236
237         if (hostlen > 1)
238                 memcpy(lease->hostname, blobmsg_get_string(c), hostlen);
239
240         if ((c = tb[LEASE_ATTR_IP]))
241                 if (inet_pton(AF_INET, blobmsg_get_string(c), &lease->ipaddr) < 0)
242                         goto err;
243
244         if ((c = tb[LEASE_ATTR_MAC]))
245                 if (!ether_aton_r(blobmsg_get_string(c), &lease->mac))
246                         goto err;
247
248         if ((c = tb[LEASE_ATTR_DUID])) {
249                 size_t duidlen = (blobmsg_data_len(c) - 1) / 2;
250                 lease->duid = malloc(duidlen);
251                 if (!lease->duid)
252                         goto err;
253
254                 ssize_t len = odhcpd_unhexlify(lease->duid,
255                                 duidlen, blobmsg_get_string(c));
256
257                 if (len < 0)
258                         goto err;
259
260                 lease->duid_len = len;
261         }
262
263         if ((c = tb[LEASE_ATTR_HOSTID])) {
264                 errno = 0;
265                 lease->hostid = strtoul(blobmsg_get_string(c), NULL, 16);
266                 if (errno)
267                         goto err;
268         }
269
270         list_add(&lease->head, &leases);
271         return 0;
272
273 err:
274         if (lease) {
275                 free(lease->duid);
276                 free(lease);
277         }
278         return -1;
279 }
280
281
282 int config_parse_interface(void *data, size_t len, const char *name, bool overwrite)
283 {
284         struct blob_attr *tb[IFACE_ATTR_MAX], *c;
285         blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, data, len);
286
287         if (tb[IFACE_ATTR_INTERFACE])
288                 name = blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]);
289
290         if (!name)
291                 return -1;
292
293         struct interface *iface = get_interface(name);
294         if (!iface) {
295                 iface = calloc(1, sizeof(*iface));
296                 if (!iface)
297                         return -1;
298
299                 strncpy(iface->name, name, sizeof(iface->name) - 1);
300                 list_add(&iface->head, &interfaces);
301                 overwrite = true;
302         }
303
304         const char *ifname = NULL;
305 #ifdef WITH_UBUS
306         if (overwrite || !iface->ifname[0])
307                 ifname = ubus_get_ifname(name);
308 #endif
309
310         if (overwrite) {
311                 if ((c = tb[IFACE_ATTR_IFNAME]))
312                         ifname = blobmsg_get_string(c);
313                 else if ((c = tb[IFACE_ATTR_NETWORKID]))
314                         ifname = blobmsg_get_string(c);
315         }
316
317         if (!iface->ifname[0] && !ifname)
318                 return -1;
319
320         if (ifname)
321                 strncpy(iface->ifname, ifname, sizeof(iface->ifname) - 1);
322
323         if ((iface->ifindex = if_nametoindex(iface->ifname)) <= 0)
324                 return -1;
325
326         iface->inuse = true;
327
328         if ((c = tb[IFACE_ATTR_DYNAMICDHCP]))
329                 iface->no_dynamic_dhcp = !blobmsg_get_bool(c);
330
331         if (overwrite && (c = tb[IFACE_ATTR_IGNORE]))
332                 iface->ignore = blobmsg_get_bool(c);
333
334         if ((c = tb[IFACE_ATTR_LEASETIME])) {
335                 char *val = blobmsg_get_string(c), *endptr;
336                 double time = strtod(val, &endptr);
337                 if (time && endptr[0]) {
338                         if (endptr[0] == 's')
339                                 time *= 1;
340                         else if (endptr[0] == 'm')
341                                 time *= 60;
342                         else if (endptr[0] == 'h')
343                                 time *= 3600;
344                         else if (endptr[0] == 'd')
345                                 time *= 24 * 3600;
346                         else if (endptr[0] == 'w')
347                                 time *= 7 * 24 * 3600;
348                         else
349                                 goto err;
350                 }
351
352                 if (time >= 60)
353                         iface->dhcpv4_leasetime = time;
354         }
355
356         if ((c = tb[IFACE_ATTR_START])) {
357                 iface->dhcpv4_start.s_addr = htonl(blobmsg_get_u32(c));
358
359                 if (config.legacy)
360                         iface->dhcpv4 = RELAYD_SERVER;
361         }
362
363         if ((c = tb[IFACE_ATTR_LIMIT]))
364                 iface->dhcpv4_end.s_addr = htonl(
365                                 ntohl(iface->dhcpv4_start.s_addr) + blobmsg_get_u32(c));
366
367         if ((c = tb[IFACE_ATTR_MASTER]))
368                 iface->master = blobmsg_get_bool(c);
369
370         if (overwrite && (c = tb[IFACE_ATTR_UPSTREAM])) {
371                 struct blob_attr *cur;
372                 unsigned rem;
373
374                 blobmsg_for_each_attr(cur, c, rem) {
375                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
376                                 continue;
377
378                         iface->upstream = realloc(iface->upstream,
379                                         iface->upstream_len + blobmsg_data_len(cur));
380                         if (!iface->upstream)
381                                 goto err;
382
383                         memcpy(iface->upstream + iface->upstream_len, blobmsg_get_string(cur), blobmsg_data_len(cur));
384                         iface->upstream_len += blobmsg_data_len(cur);
385                 }
386         }
387
388         int mode;
389         if ((c = tb[IFACE_ATTR_RA])) {
390                 if ((mode = parse_mode(blobmsg_get_string(c))) >= 0)
391                         iface->ra = mode;
392                 else
393                         goto err;
394         }
395
396         if ((c = tb[IFACE_ATTR_DHCPV4])) {
397                 if ((mode = parse_mode(blobmsg_get_string(c))) >= 0)
398                         iface->dhcpv4 = mode;
399                 else
400                         goto err;
401         }
402
403         if ((c = tb[IFACE_ATTR_DHCPV6])) {
404                 if ((mode = parse_mode(blobmsg_get_string(c))) >= 0)
405                         iface->dhcpv6 = mode;
406                 else
407                         goto err;
408         }
409
410         if ((c = tb[IFACE_ATTR_NDP])) {
411                 if ((mode = parse_mode(blobmsg_get_string(c))) >= 0)
412                         iface->ndp = mode;
413                 else
414                         goto err;
415         }
416
417         if ((c = tb[IFACE_ATTR_ROUTER])) {
418                 struct blob_attr *cur;
419                 unsigned rem;
420
421                 blobmsg_for_each_attr(cur, c, rem) {
422                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
423                                 continue;
424
425                         struct in_addr addr4;
426                         if (inet_pton(AF_INET, blobmsg_get_string(cur), &addr4) == 1) {
427                                 iface->dhcpv4_router = realloc(iface->dhcpv4_router,
428                                                 (++iface->dhcpv4_router_cnt) * sizeof(*iface->dhcpv4_router));
429                                 if (!iface->dhcpv4_router)
430                                         goto err;
431
432                                 iface->dhcpv4_router[iface->dhcpv4_router_cnt - 1] = addr4;
433                         } else {
434                                 goto err;
435                         }
436                 }
437         }
438
439         if ((c = tb[IFACE_ATTR_DNS])) {
440                 struct blob_attr *cur;
441                 unsigned rem;
442
443                 iface->always_rewrite_dns = true;
444                 blobmsg_for_each_attr(cur, c, rem) {
445                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
446                                 continue;
447
448                         struct in_addr addr4;
449                         struct in6_addr addr6;
450                         if (inet_pton(AF_INET, blobmsg_get_string(cur), &addr4) == 1) {
451                                 iface->dhcpv4_dns = realloc(iface->dhcpv4_dns,
452                                                 (++iface->dhcpv4_dns_cnt) * sizeof(*iface->dhcpv4_dns));
453                                 if (!iface->dhcpv4_dns)
454                                         goto err;
455
456                                 iface->dhcpv4_dns[iface->dhcpv4_dns_cnt - 1] = addr4;
457                         } else if (inet_pton(AF_INET6, blobmsg_get_string(cur), &addr6) == 1) {
458                                 iface->dns = realloc(iface->dns,
459                                                 (++iface->dns_cnt) * sizeof(*iface->dns));
460                                 if (!iface->dns)
461                                         goto err;
462
463                                 iface->dns[iface->dns_cnt - 1] = addr6;
464                         } else {
465                                 goto err;
466                         }
467                 }
468         }
469
470         if ((c = tb[IFACE_ATTR_DOMAIN])) {
471                 struct blob_attr *cur;
472                 unsigned rem;
473
474                 blobmsg_for_each_attr(cur, c, rem) {
475                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
476                                 continue;
477
478                         uint8_t buf[256];
479                         char *domain = blobmsg_get_string(cur);
480                         size_t domainlen = strlen(domain);
481                         if (domainlen > 0 && domain[domainlen - 1] == '.')
482                                 domain[domainlen - 1] = 0;
483
484                         int len = dn_comp(domain, buf, sizeof(buf), NULL, NULL);
485                         if (len <= 0)
486                                 goto err;
487
488                         iface->search = realloc(iface->search, iface->search_len + len);
489                         if (!iface->search)
490                                 goto err;
491
492                         memcpy(&iface->search[iface->search_len], buf, len);
493                         iface->search_len += len;
494                 }
495         }
496
497         if ((c = tb[IFACE_ATTR_FILTER_CLASS])) {
498                 iface->filter_class = realloc(iface->filter_class, blobmsg_data_len(c) + 1);
499                 memcpy(iface->filter_class, blobmsg_get_string(c), blobmsg_data_len(c) + 1);
500         }
501
502         if ((c = tb[IFACE_ATTR_DHCPV6_RAW])) {
503                 iface->dhcpv6_raw_len = blobmsg_data_len(c) / 2;
504                 iface->dhcpv6_raw = realloc(iface->dhcpv6_raw, iface->dhcpv6_raw_len);
505                 odhcpd_unhexlify(iface->dhcpv6_raw, iface->dhcpv6_raw_len, blobmsg_get_string(c));
506         }
507
508         if ((c = tb[IFACE_ATTR_RA_DEFAULT]))
509                 iface->default_router = blobmsg_get_u32(c);
510
511         if ((c = tb[IFACE_ATTR_RA_MANAGEMENT]))
512                 iface->managed = blobmsg_get_u32(c);
513         else if (overwrite)
514                 iface->managed = 1;
515
516         if ((c = tb[IFACE_ATTR_RA_OFFLINK]))
517                 iface->ra_not_onlink = blobmsg_get_bool(c);
518
519         if ((c = tb[IFACE_ATTR_RA_ADVROUTER]))
520                 iface->ra_advrouter = blobmsg_get_bool(c);
521
522         if ((c = tb[IFACE_ATTR_RA_PREFERENCE])) {
523                 const char *prio = blobmsg_get_string(c);
524
525                 if (!strcmp(prio, "high"))
526                         iface->route_preference = 1;
527                 else if (!strcmp(prio, "low"))
528                         iface->route_preference = -1;
529                 else if (!strcmp(prio, "medium") || !strcmp(prio, "default"))
530                         iface->route_preference = 0;
531                 else
532                         goto err;
533         }
534
535         if ((c = tb[IFACE_ATTR_PD_MANAGER]))
536                 strncpy(iface->dhcpv6_pd_manager, blobmsg_get_string(c),
537                                 sizeof(iface->dhcpv6_pd_manager) - 1);
538
539         if ((c = tb[IFACE_ATTR_PD_CER]) &&
540                         inet_pton(AF_INET6, blobmsg_get_string(c), &iface->dhcpv6_pd_cer) < 1)
541                 goto err;
542
543         if ((c = tb[IFACE_ATTR_NDPROXY_ROUTING]))
544                 iface->learn_routes = blobmsg_get_bool(c);
545         else if (overwrite)
546                 iface->learn_routes = true;
547
548         if ((c = tb[IFACE_ATTR_NDPROXY_SLAVE]))
549                 iface->external = blobmsg_get_bool(c);
550
551         if ((c = tb[IFACE_ATTR_NDPROXY_STATIC])) {
552                 struct blob_attr *cur;
553                 unsigned rem;
554
555                 blobmsg_for_each_attr(cur, c, rem) {
556                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
557                                 continue;
558
559                         int len = blobmsg_data_len(cur);
560                         iface->static_ndp = realloc(iface->static_ndp, iface->static_ndp_len + len);
561                         if (!iface->static_ndp)
562                                 goto err;
563
564                         if (iface->static_ndp_len)
565                                 iface->static_ndp[iface->static_ndp_len - 1] = ' ';
566
567                         memcpy(&iface->static_ndp[iface->static_ndp_len], blobmsg_get_string(cur), len);
568                         iface->static_ndp_len += len;
569                 }
570         }
571
572         return 0;
573
574 err:
575         close_interface(iface);
576         return -1;
577 }
578
579 static int set_interface(struct uci_section *s)
580 {
581         blob_buf_init(&b, 0);
582         uci_to_blob(&b, s, &interface_attr_list);
583         return config_parse_interface(blob_data(b.head), blob_len(b.head), s->e.name, true);
584 }
585
586
587 void odhcpd_reload(void)
588 {
589         struct uci_context *uci = uci_alloc_context();
590         while (!list_empty(&leases)) {
591                 struct lease *l = list_first_entry(&leases, struct lease, head);
592                 list_del(&l->head);
593                 free(l->duid);
594                 free(l);
595         }
596
597         struct interface *master = NULL, *i, *n;
598
599         if (!uci)
600                 return;
601
602         list_for_each_entry(i, &interfaces, head)
603                 clean_interface(i);
604
605         struct uci_package *dhcp = NULL;
606         if (!uci_load(uci, "dhcp", &dhcp)) {
607                 struct uci_element *e;
608                 uci_foreach_element(&dhcp->sections, e) {
609                         struct uci_section *s = uci_to_section(e);
610                         if (!strcmp(s->type, "host"))
611                                 set_lease(s);
612                         else if (!strcmp(s->type, "odhcpd"))
613                                 set_config(s);
614                 }
615
616                 uci_foreach_element(&dhcp->sections, e) {
617                         struct uci_section *s = uci_to_section(e);
618                         if (!strcmp(s->type, "dhcp"))
619                                 set_interface(s);
620                 }
621         }
622
623
624 #ifdef WITH_UBUS
625         ubus_apply_network();
626 #endif
627
628         bool any_dhcpv6_slave = false, any_ra_slave = false, any_ndp_slave = false;
629
630         // Test for
631         list_for_each_entry(i, &interfaces, head) {
632                 if (i->master)
633                         continue;
634
635                 if (i->dhcpv6 == RELAYD_HYBRID || i->dhcpv6 == RELAYD_RELAY)
636                         any_dhcpv6_slave = true;
637
638                 if (i->ra == RELAYD_HYBRID || i->ra == RELAYD_RELAY)
639                         any_ra_slave = true;
640
641                 if (i->ndp == RELAYD_HYBRID || i->ndp == RELAYD_RELAY)
642                         any_ndp_slave = true;
643         }
644
645         // Evaluate hybrid mode for master
646         list_for_each_entry(i, &interfaces, head) {
647                 if (!i->master)
648                         continue;
649
650                 enum odhcpd_mode hybrid_mode = RELAYD_DISABLED;
651 #ifdef WITH_UBUS
652                 if (!ubus_has_prefix(i->name, i->ifname))
653                         hybrid_mode = RELAYD_RELAY;
654 #endif
655
656                 if (i->dhcpv6 == RELAYD_HYBRID)
657                         i->dhcpv6 = hybrid_mode;
658
659                 if (i->dhcpv6 == RELAYD_RELAY && !any_dhcpv6_slave)
660                         i->dhcpv6 = RELAYD_DISABLED;
661
662                 if (i->ra == RELAYD_HYBRID)
663                         i->ra = hybrid_mode;
664
665                 if (i->ra == RELAYD_RELAY && !any_ra_slave)
666                         i->ra = RELAYD_DISABLED;
667
668                 if (i->ndp == RELAYD_HYBRID)
669                         i->ndp = hybrid_mode;
670
671                 if (i->ndp == RELAYD_RELAY && !any_ndp_slave)
672                         i->ndp = RELAYD_DISABLED;
673
674                 if (i->dhcpv6 == RELAYD_RELAY || i->ra == RELAYD_RELAY || i->ndp == RELAYD_RELAY)
675                         master = i;
676         }
677
678
679         list_for_each_entry_safe(i, n, &interfaces, head) {
680                 if (i->inuse) {
681                         // Resolve hybrid mode
682                         if (i->dhcpv6 == RELAYD_HYBRID)
683                                 i->dhcpv6 = (master && master->dhcpv6 == RELAYD_RELAY) ?
684                                                 RELAYD_RELAY : RELAYD_SERVER;
685
686                         if (i->ra == RELAYD_HYBRID)
687                                 i->ra = (master && master->ra == RELAYD_RELAY) ?
688                                                 RELAYD_RELAY : RELAYD_SERVER;
689
690                         if (i->ndp == RELAYD_HYBRID)
691                                 i->ndp = (master && master->ndp == RELAYD_RELAY) ?
692                                                 RELAYD_RELAY : RELAYD_DISABLED;
693
694                         setup_router_interface(i, true);
695                         setup_dhcpv6_interface(i, true);
696                         setup_ndp_interface(i, true);
697                         setup_dhcpv4_interface(i, true);
698                 } else {
699                         close_interface(i);
700                 }
701         }
702
703         uci_unload(uci, dhcp);
704         uci_free_context(uci);
705 }
706
707
708 static void handle_signal(int signal)
709 {
710         char b[1] = {0};
711         if (signal == SIGHUP)
712                 write(reload_pipe[1], b, sizeof(b));
713         else
714                 uloop_end();
715 }
716
717
718
719 static void reload_cb(struct uloop_fd *u, _unused unsigned int events)
720 {
721         char b[512];
722         read(u->fd, b, sizeof(b));
723         odhcpd_reload();
724 }
725
726 static struct uloop_fd reload_fd = { .cb = reload_cb };
727
728 void odhcpd_run(void)
729 {
730         pipe2(reload_pipe, O_NONBLOCK | O_CLOEXEC);
731         reload_fd.fd = reload_pipe[0];
732         uloop_fd_add(&reload_fd, ULOOP_READ);
733
734         signal(SIGTERM, handle_signal);
735         signal(SIGINT, handle_signal);
736         signal(SIGHUP, handle_signal);
737
738 #ifdef WITH_UBUS
739         while (init_ubus())
740                 sleep(1);
741 #endif
742
743         odhcpd_reload();
744         uloop_run();
745 }
746