16 static struct blob_buf b
;
17 static int reload_pipe
[2];
18 struct list_head leases
= LIST_HEAD_INIT(leases
);
19 struct list_head interfaces
= LIST_HEAD_INIT(interfaces
);
20 struct config config
= {.legacy
= false, .main_dhcpv4
= false,
21 .dhcp_cb
= NULL
, .dhcp_statefile
= NULL
,
22 .log_level
= LOG_INFO
};
28 IFACE_ATTR_DYNAMICDHCP
,
42 IFACE_ATTR_FILTER_CLASS
,
43 IFACE_ATTR_DHCPV6_RAW
,
44 IFACE_ATTR_RA_DEFAULT
,
45 IFACE_ATTR_RA_MANAGEMENT
,
46 IFACE_ATTR_RA_OFFLINK
,
47 IFACE_ATTR_RA_PREFERENCE
,
48 IFACE_ATTR_RA_ADVROUTER
,
49 IFACE_ATTR_RA_MININTERVAL
,
50 IFACE_ATTR_RA_MAXINTERVAL
,
51 IFACE_ATTR_RA_LIFETIME
,
52 IFACE_ATTR_RA_USELEASETIME
,
54 IFACE_ATTR_PD_MANAGER
,
56 IFACE_ATTR_NDPROXY_ROUTING
,
57 IFACE_ATTR_NDPROXY_SLAVE
,
61 static const struct blobmsg_policy iface_attrs
[IFACE_ATTR_MAX
] = {
62 [IFACE_ATTR_INTERFACE
] = { .name
= "interface", .type
= BLOBMSG_TYPE_STRING
},
63 [IFACE_ATTR_IFNAME
] = { .name
= "ifname", .type
= BLOBMSG_TYPE_STRING
},
64 [IFACE_ATTR_NETWORKID
] = { .name
= "networkid", .type
= BLOBMSG_TYPE_STRING
},
65 [IFACE_ATTR_DYNAMICDHCP
] = { .name
= "dynamicdhcp", .type
= BLOBMSG_TYPE_BOOL
},
66 [IFACE_ATTR_IGNORE
] = { .name
= "ignore", .type
= BLOBMSG_TYPE_BOOL
},
67 [IFACE_ATTR_LEASETIME
] = { .name
= "leasetime", .type
= BLOBMSG_TYPE_STRING
},
68 [IFACE_ATTR_START
] = { .name
= "start", .type
= BLOBMSG_TYPE_INT32
},
69 [IFACE_ATTR_LIMIT
] = { .name
= "limit", .type
= BLOBMSG_TYPE_INT32
},
70 [IFACE_ATTR_MASTER
] = { .name
= "master", .type
= BLOBMSG_TYPE_BOOL
},
71 [IFACE_ATTR_UPSTREAM
] = { .name
= "upstream", .type
= BLOBMSG_TYPE_ARRAY
},
72 [IFACE_ATTR_RA
] = { .name
= "ra", .type
= BLOBMSG_TYPE_STRING
},
73 [IFACE_ATTR_DHCPV4
] = { .name
= "dhcpv4", .type
= BLOBMSG_TYPE_STRING
},
74 [IFACE_ATTR_DHCPV6
] = { .name
= "dhcpv6", .type
= BLOBMSG_TYPE_STRING
},
75 [IFACE_ATTR_NDP
] = { .name
= "ndp", .type
= BLOBMSG_TYPE_STRING
},
76 [IFACE_ATTR_ROUTER
] = { .name
= "router", .type
= BLOBMSG_TYPE_ARRAY
},
77 [IFACE_ATTR_DNS
] = { .name
= "dns", .type
= BLOBMSG_TYPE_ARRAY
},
78 [IFACE_ATTR_DOMAIN
] = { .name
= "domain", .type
= BLOBMSG_TYPE_ARRAY
},
79 [IFACE_ATTR_FILTER_CLASS
] = { .name
= "filter_class", .type
= BLOBMSG_TYPE_STRING
},
80 [IFACE_ATTR_DHCPV6_RAW
] = { .name
= "dhcpv6_raw", .type
= BLOBMSG_TYPE_STRING
},
81 [IFACE_ATTR_PD_MANAGER
] = { .name
= "pd_manager", .type
= BLOBMSG_TYPE_STRING
},
82 [IFACE_ATTR_PD_CER
] = { .name
= "pd_cer", .type
= BLOBMSG_TYPE_STRING
},
83 [IFACE_ATTR_RA_DEFAULT
] = { .name
= "ra_default", .type
= BLOBMSG_TYPE_INT32
},
84 [IFACE_ATTR_RA_MANAGEMENT
] = { .name
= "ra_management", .type
= BLOBMSG_TYPE_INT32
},
85 [IFACE_ATTR_RA_OFFLINK
] = { .name
= "ra_offlink", .type
= BLOBMSG_TYPE_BOOL
},
86 [IFACE_ATTR_RA_PREFERENCE
] = { .name
= "ra_preference", .type
= BLOBMSG_TYPE_STRING
},
87 [IFACE_ATTR_RA_ADVROUTER
] = { .name
= "ra_advrouter", .type
= BLOBMSG_TYPE_BOOL
},
88 [IFACE_ATTR_RA_MININTERVAL
] = { .name
= "ra_mininterval", .type
= BLOBMSG_TYPE_INT32
},
89 [IFACE_ATTR_RA_MAXINTERVAL
] = { .name
= "ra_maxinterval", .type
= BLOBMSG_TYPE_INT32
},
90 [IFACE_ATTR_RA_LIFETIME
] = { .name
= "ra_lifetime", .type
= BLOBMSG_TYPE_INT32
},
91 [IFACE_ATTR_RA_USELEASETIME
] = { .name
= "ra_useleasetime", .type
= BLOBMSG_TYPE_BOOL
},
92 [IFACE_ATTR_RA_MTU
] = { .name
= "ra_mtu", .type
= BLOBMSG_TYPE_INT32
},
93 [IFACE_ATTR_NDPROXY_ROUTING
] = { .name
= "ndproxy_routing", .type
= BLOBMSG_TYPE_BOOL
},
94 [IFACE_ATTR_NDPROXY_SLAVE
] = { .name
= "ndproxy_slave", .type
= BLOBMSG_TYPE_BOOL
},
97 static const struct uci_blob_param_info iface_attr_info
[IFACE_ATTR_MAX
] = {
98 [IFACE_ATTR_UPSTREAM
] = { .type
= BLOBMSG_TYPE_STRING
},
99 [IFACE_ATTR_DNS
] = { .type
= BLOBMSG_TYPE_STRING
},
100 [IFACE_ATTR_DOMAIN
] = { .type
= BLOBMSG_TYPE_STRING
},
103 const struct uci_blob_param_list interface_attr_list
= {
104 .n_params
= IFACE_ATTR_MAX
,
105 .params
= iface_attrs
,
106 .info
= iface_attr_info
,
114 LEASE_ATTR_LEASETIME
,
119 static const struct blobmsg_policy lease_attrs
[LEASE_ATTR_MAX
] = {
120 [LEASE_ATTR_IP
] = { .name
= "ip", .type
= BLOBMSG_TYPE_STRING
},
121 [LEASE_ATTR_MAC
] = { .name
= "mac", .type
= BLOBMSG_TYPE_STRING
},
122 [LEASE_ATTR_DUID
] = { .name
= "duid", .type
= BLOBMSG_TYPE_STRING
},
123 [LEASE_ATTR_HOSTID
] = { .name
= "hostid", .type
= BLOBMSG_TYPE_STRING
},
124 [LEASE_ATTR_LEASETIME
] = { .name
= "leasetime", .type
= BLOBMSG_TYPE_STRING
},
125 [LEASE_ATTR_NAME
] = { .name
= "name", .type
= BLOBMSG_TYPE_STRING
},
128 const struct uci_blob_param_list lease_attr_list
= {
129 .n_params
= LEASE_ATTR_MAX
,
130 .params
= lease_attrs
,
135 ODHCPD_ATTR_MAINDHCP
,
136 ODHCPD_ATTR_LEASEFILE
,
137 ODHCPD_ATTR_LEASETRIGGER
,
138 ODHCPD_ATTR_LOGLEVEL
,
142 static const struct blobmsg_policy odhcpd_attrs
[LEASE_ATTR_MAX
] = {
143 [ODHCPD_ATTR_LEGACY
] = { .name
= "legacy", .type
= BLOBMSG_TYPE_BOOL
},
144 [ODHCPD_ATTR_MAINDHCP
] = { .name
= "maindhcp", .type
= BLOBMSG_TYPE_BOOL
},
145 [ODHCPD_ATTR_LEASEFILE
] = { .name
= "leasefile", .type
= BLOBMSG_TYPE_STRING
},
146 [ODHCPD_ATTR_LEASETRIGGER
] = { .name
= "leasetrigger", .type
= BLOBMSG_TYPE_STRING
},
147 [ODHCPD_ATTR_LOGLEVEL
] = { .name
= "loglevel", .type
= BLOBMSG_TYPE_INT32
},
150 const struct uci_blob_param_list odhcpd_attr_list
= {
151 .n_params
= ODHCPD_ATTR_MAX
,
152 .params
= odhcpd_attrs
,
155 static int mkdir_p(char *dir
, mode_t mask
)
157 char *l
= strrchr(dir
, '/');
165 if (mkdir_p(dir
, mask
))
170 ret
= mkdir(dir
, mask
);
171 if (ret
&& errno
== EEXIST
)
175 syslog(LOG_ERR
, "mkdir(%s, %d) failed: %s\n", dir
, mask
, strerror(errno
));
180 static void free_lease(struct lease
*l
)
189 static struct interface
* get_interface(const char *name
)
192 list_for_each_entry(c
, &interfaces
, head
)
193 if (!strcmp(c
->name
, name
))
198 static void set_interface_defaults(struct interface
*iface
)
201 iface
->learn_routes
= 1;
202 iface
->dhcpv4_leasetime
= 43200;
203 iface
->ra_maxinterval
= 600;
204 iface
->ra_mininterval
= iface
->ra_maxinterval
/3;
205 iface
->ra_lifetime
= -1;
208 static void clean_interface(struct interface
*iface
)
212 free(iface
->upstream
);
213 free(iface
->dhcpv4_router
);
214 free(iface
->dhcpv4_dns
);
215 free(iface
->dhcpv6_raw
);
216 free(iface
->filter_class
);
217 memset(&iface
->ra
, 0, sizeof(*iface
) - offsetof(struct interface
, ra
));
218 set_interface_defaults(iface
);
221 static void close_interface(struct interface
*iface
)
223 if (iface
->head
.next
)
224 list_del(&iface
->head
);
226 setup_router_interface(iface
, false);
227 setup_dhcpv6_interface(iface
, false);
228 setup_ndp_interface(iface
, false);
229 setup_dhcpv4_interface(iface
, false);
231 clean_interface(iface
);
235 static int parse_mode(const char *mode
)
237 if (!strcmp(mode
, "disabled"))
238 return RELAYD_DISABLED
;
239 else if (!strcmp(mode
, "server"))
240 return RELAYD_SERVER
;
241 else if (!strcmp(mode
, "relay"))
243 else if (!strcmp(mode
, "hybrid"))
244 return RELAYD_HYBRID
;
249 static void set_config(struct uci_section
*s
)
251 struct blob_attr
*tb
[ODHCPD_ATTR_MAX
], *c
;
253 blob_buf_init(&b
, 0);
254 uci_to_blob(&b
, s
, &odhcpd_attr_list
);
255 blobmsg_parse(odhcpd_attrs
, ODHCPD_ATTR_MAX
, tb
, blob_data(b
.head
), blob_len(b
.head
));
257 if ((c
= tb
[ODHCPD_ATTR_LEGACY
]))
258 config
.legacy
= blobmsg_get_bool(c
);
260 if ((c
= tb
[ODHCPD_ATTR_MAINDHCP
]))
261 config
.main_dhcpv4
= blobmsg_get_bool(c
);
263 if ((c
= tb
[ODHCPD_ATTR_LEASEFILE
])) {
264 free(config
.dhcp_statefile
);
265 config
.dhcp_statefile
= strdup(blobmsg_get_string(c
));
268 if ((c
= tb
[ODHCPD_ATTR_LEASETRIGGER
])) {
269 free(config
.dhcp_cb
);
270 config
.dhcp_cb
= strdup(blobmsg_get_string(c
));
273 if ((c
= tb
[ODHCPD_ATTR_LOGLEVEL
])) {
274 int log_level
= (blobmsg_get_u32(c
) & LOG_PRIMASK
);
276 if (config
.log_level
!= log_level
) {
277 config
.log_level
= log_level
;
278 setlogmask(LOG_UPTO(config
.log_level
));
283 static double parse_leasetime(struct blob_attr
*c
) {
284 char *val
= blobmsg_get_string(c
), *endptr
= NULL
;
285 double time
= strcmp(val
, "infinite") ? strtod(val
, &endptr
) : UINT32_MAX
;
287 if (time
&& endptr
&& endptr
[0]) {
288 if (endptr
[0] == 's')
290 else if (endptr
[0] == 'm')
292 else if (endptr
[0] == 'h')
294 else if (endptr
[0] == 'd')
296 else if (endptr
[0] == 'w')
297 time
*= 7 * 24 * 3600;
311 static int set_lease(struct uci_section
*s
)
313 struct blob_attr
*tb
[LEASE_ATTR_MAX
], *c
;
315 blob_buf_init(&b
, 0);
316 uci_to_blob(&b
, s
, &lease_attr_list
);
317 blobmsg_parse(lease_attrs
, LEASE_ATTR_MAX
, tb
, blob_data(b
.head
), blob_len(b
.head
));
320 if ((c
= tb
[LEASE_ATTR_NAME
]))
321 hostlen
= blobmsg_data_len(c
);
323 struct lease
*lease
= calloc(1, sizeof(*lease
) + hostlen
);
328 memcpy(lease
->hostname
, blobmsg_get_string(c
), hostlen
);
330 if ((c
= tb
[LEASE_ATTR_IP
]))
331 if (inet_pton(AF_INET
, blobmsg_get_string(c
), &lease
->ipaddr
) < 0)
334 if ((c
= tb
[LEASE_ATTR_MAC
]))
335 if (!ether_aton_r(blobmsg_get_string(c
), &lease
->mac
))
338 if ((c
= tb
[LEASE_ATTR_DUID
])) {
339 size_t duidlen
= (blobmsg_data_len(c
) - 1) / 2;
340 lease
->duid
= malloc(duidlen
);
344 ssize_t len
= odhcpd_unhexlify(lease
->duid
,
345 duidlen
, blobmsg_get_string(c
));
350 lease
->duid_len
= len
;
353 if ((c
= tb
[LEASE_ATTR_HOSTID
])) {
355 lease
->hostid
= strtoul(blobmsg_get_string(c
), NULL
, 16);
360 if ((c
= tb
[LEASE_ATTR_LEASETIME
])) {
361 double time
= parse_leasetime(c
);
365 lease
->dhcpv4_leasetime
= time
;
368 list_add(&lease
->head
, &leases
);
378 int config_parse_interface(void *data
, size_t len
, const char *name
, bool overwrite
)
380 struct blob_attr
*tb
[IFACE_ATTR_MAX
], *c
;
381 blobmsg_parse(iface_attrs
, IFACE_ATTR_MAX
, tb
, data
, len
);
383 if (tb
[IFACE_ATTR_INTERFACE
])
384 name
= blobmsg_get_string(tb
[IFACE_ATTR_INTERFACE
]);
389 struct interface
*iface
= get_interface(name
);
391 iface
= calloc(1, sizeof(*iface
));
395 strncpy(iface
->name
, name
, sizeof(iface
->name
) - 1);
397 set_interface_defaults(iface
);
399 list_add(&iface
->head
, &interfaces
);
403 const char *ifname
= NULL
;
405 if ((c
= tb
[IFACE_ATTR_IFNAME
]))
406 ifname
= blobmsg_get_string(c
);
407 else if ((c
= tb
[IFACE_ATTR_NETWORKID
]))
408 ifname
= blobmsg_get_string(c
);
412 if (overwrite
|| !iface
->ifname
[0])
413 ifname
= ubus_get_ifname(name
);
416 if (!iface
->ifname
[0] && !ifname
)
420 strncpy(iface
->ifname
, ifname
, sizeof(iface
->ifname
) - 1);
422 if ((iface
->ifindex
= if_nametoindex(iface
->ifname
)) <= 0)
427 if ((c
= tb
[IFACE_ATTR_DYNAMICDHCP
]))
428 iface
->no_dynamic_dhcp
= !blobmsg_get_bool(c
);
430 if (overwrite
&& (c
= tb
[IFACE_ATTR_IGNORE
]))
431 iface
->ignore
= blobmsg_get_bool(c
);
433 if ((c
= tb
[IFACE_ATTR_LEASETIME
])) {
434 double time
= parse_leasetime(c
);
438 iface
->dhcpv4_leasetime
= time
;
441 if ((c
= tb
[IFACE_ATTR_START
])) {
442 iface
->dhcpv4_start
.s_addr
= htonl(blobmsg_get_u32(c
));
444 if (config
.main_dhcpv4
&& config
.legacy
)
445 iface
->dhcpv4
= RELAYD_SERVER
;
448 if ((c
= tb
[IFACE_ATTR_LIMIT
]))
449 iface
->dhcpv4_end
.s_addr
= htonl(
450 ntohl(iface
->dhcpv4_start
.s_addr
) + blobmsg_get_u32(c
));
452 if ((c
= tb
[IFACE_ATTR_MASTER
]))
453 iface
->master
= blobmsg_get_bool(c
);
455 if (overwrite
&& (c
= tb
[IFACE_ATTR_UPSTREAM
])) {
456 struct blob_attr
*cur
;
459 blobmsg_for_each_attr(cur
, c
, rem
) {
460 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, false))
463 iface
->upstream
= realloc(iface
->upstream
,
464 iface
->upstream_len
+ blobmsg_data_len(cur
));
465 if (!iface
->upstream
)
468 memcpy(iface
->upstream
+ iface
->upstream_len
, blobmsg_get_string(cur
), blobmsg_data_len(cur
));
469 iface
->upstream_len
+= blobmsg_data_len(cur
);
474 if ((c
= tb
[IFACE_ATTR_RA
])) {
475 if ((mode
= parse_mode(blobmsg_get_string(c
))) >= 0)
481 if ((c
= tb
[IFACE_ATTR_DHCPV4
])) {
482 if ((mode
= parse_mode(blobmsg_get_string(c
))) >= 0) {
483 if (config
.main_dhcpv4
)
484 iface
->dhcpv4
= mode
;
490 if ((c
= tb
[IFACE_ATTR_DHCPV6
])) {
491 if ((mode
= parse_mode(blobmsg_get_string(c
))) >= 0)
492 iface
->dhcpv6
= mode
;
497 if ((c
= tb
[IFACE_ATTR_NDP
])) {
498 if ((mode
= parse_mode(blobmsg_get_string(c
))) >= 0)
504 if ((c
= tb
[IFACE_ATTR_ROUTER
])) {
505 struct blob_attr
*cur
;
508 blobmsg_for_each_attr(cur
, c
, rem
) {
509 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, false))
512 struct in_addr addr4
;
513 if (inet_pton(AF_INET
, blobmsg_get_string(cur
), &addr4
) == 1) {
514 iface
->dhcpv4_router
= realloc(iface
->dhcpv4_router
,
515 (++iface
->dhcpv4_router_cnt
) * sizeof(*iface
->dhcpv4_router
));
516 if (!iface
->dhcpv4_router
)
519 iface
->dhcpv4_router
[iface
->dhcpv4_router_cnt
- 1] = addr4
;
525 if ((c
= tb
[IFACE_ATTR_DNS
])) {
526 struct blob_attr
*cur
;
529 iface
->always_rewrite_dns
= true;
530 blobmsg_for_each_attr(cur
, c
, rem
) {
531 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, false))
534 struct in_addr addr4
;
535 struct in6_addr addr6
;
536 if (inet_pton(AF_INET
, blobmsg_get_string(cur
), &addr4
) == 1) {
537 iface
->dhcpv4_dns
= realloc(iface
->dhcpv4_dns
,
538 (++iface
->dhcpv4_dns_cnt
) * sizeof(*iface
->dhcpv4_dns
));
539 if (!iface
->dhcpv4_dns
)
542 iface
->dhcpv4_dns
[iface
->dhcpv4_dns_cnt
- 1] = addr4
;
543 } else if (inet_pton(AF_INET6
, blobmsg_get_string(cur
), &addr6
) == 1) {
544 iface
->dns
= realloc(iface
->dns
,
545 (++iface
->dns_cnt
) * sizeof(*iface
->dns
));
549 iface
->dns
[iface
->dns_cnt
- 1] = addr6
;
555 if ((c
= tb
[IFACE_ATTR_DOMAIN
])) {
556 struct blob_attr
*cur
;
559 blobmsg_for_each_attr(cur
, c
, rem
) {
560 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, false))
564 char *domain
= blobmsg_get_string(cur
);
565 size_t domainlen
= strlen(domain
);
566 if (domainlen
> 0 && domain
[domainlen
- 1] == '.')
567 domain
[domainlen
- 1] = 0;
569 int len
= dn_comp(domain
, buf
, sizeof(buf
), NULL
, NULL
);
573 iface
->search
= realloc(iface
->search
, iface
->search_len
+ len
);
577 memcpy(&iface
->search
[iface
->search_len
], buf
, len
);
578 iface
->search_len
+= len
;
582 if ((c
= tb
[IFACE_ATTR_FILTER_CLASS
])) {
583 iface
->filter_class
= realloc(iface
->filter_class
, blobmsg_data_len(c
) + 1);
584 memcpy(iface
->filter_class
, blobmsg_get_string(c
), blobmsg_data_len(c
) + 1);
587 if ((c
= tb
[IFACE_ATTR_DHCPV6_RAW
])) {
588 iface
->dhcpv6_raw_len
= blobmsg_data_len(c
) / 2;
589 iface
->dhcpv6_raw
= realloc(iface
->dhcpv6_raw
, iface
->dhcpv6_raw_len
);
590 odhcpd_unhexlify(iface
->dhcpv6_raw
, iface
->dhcpv6_raw_len
, blobmsg_get_string(c
));
593 if ((c
= tb
[IFACE_ATTR_RA_DEFAULT
]))
594 iface
->default_router
= blobmsg_get_u32(c
);
596 if ((c
= tb
[IFACE_ATTR_RA_MANAGEMENT
]))
597 iface
->managed
= blobmsg_get_u32(c
);
599 if ((c
= tb
[IFACE_ATTR_RA_MTU
])) {
600 iface
->ra_mtu
= blobmsg_get_u32(c
);
601 if (iface
->ra_mtu
< 1280)
605 if ((c
= tb
[IFACE_ATTR_RA_OFFLINK
]))
606 iface
->ra_not_onlink
= blobmsg_get_bool(c
);
608 if ((c
= tb
[IFACE_ATTR_RA_ADVROUTER
]))
609 iface
->ra_advrouter
= blobmsg_get_bool(c
);
611 if ((c
= tb
[IFACE_ATTR_RA_MININTERVAL
]))
612 iface
->ra_mininterval
= blobmsg_get_u32(c
);
614 if ((c
= tb
[IFACE_ATTR_RA_MAXINTERVAL
]))
615 iface
->ra_maxinterval
= blobmsg_get_u32(c
);
617 if ((c
= tb
[IFACE_ATTR_RA_LIFETIME
]))
618 iface
->ra_lifetime
= blobmsg_get_u32(c
);
620 if ((c
= tb
[IFACE_ATTR_RA_USELEASETIME
]))
621 iface
->ra_useleasetime
= blobmsg_get_bool(c
);
623 if ((c
= tb
[IFACE_ATTR_RA_PREFERENCE
])) {
624 const char *prio
= blobmsg_get_string(c
);
626 if (!strcmp(prio
, "high"))
627 iface
->route_preference
= 1;
628 else if (!strcmp(prio
, "low"))
629 iface
->route_preference
= -1;
630 else if (!strcmp(prio
, "medium") || !strcmp(prio
, "default"))
631 iface
->route_preference
= 0;
636 if ((c
= tb
[IFACE_ATTR_PD_MANAGER
]))
637 strncpy(iface
->dhcpv6_pd_manager
, blobmsg_get_string(c
),
638 sizeof(iface
->dhcpv6_pd_manager
) - 1);
640 if ((c
= tb
[IFACE_ATTR_PD_CER
]) &&
641 inet_pton(AF_INET6
, blobmsg_get_string(c
), &iface
->dhcpv6_pd_cer
) < 1)
644 if ((c
= tb
[IFACE_ATTR_NDPROXY_ROUTING
]))
645 iface
->learn_routes
= blobmsg_get_bool(c
);
647 if ((c
= tb
[IFACE_ATTR_NDPROXY_SLAVE
]))
648 iface
->external
= blobmsg_get_bool(c
);
653 close_interface(iface
);
657 static int set_interface(struct uci_section
*s
)
659 blob_buf_init(&b
, 0);
660 uci_to_blob(&b
, s
, &interface_attr_list
);
662 return config_parse_interface(blob_data(b
.head
), blob_len(b
.head
), s
->e
.name
, true);
665 void odhcpd_reload(void)
667 struct uci_context
*uci
= uci_alloc_context();
669 while (!list_empty(&leases
))
670 free_lease(list_first_entry(&leases
, struct lease
, head
));
672 struct interface
*master
= NULL
, *i
, *n
;
677 list_for_each_entry(i
, &interfaces
, head
)
680 struct uci_package
*dhcp
= NULL
;
681 if (!uci_load(uci
, "dhcp", &dhcp
)) {
682 struct uci_element
*e
;
683 uci_foreach_element(&dhcp
->sections
, e
) {
684 struct uci_section
*s
= uci_to_section(e
);
685 if (!strcmp(s
->type
, "host"))
687 else if (!strcmp(s
->type
, "odhcpd"))
691 uci_foreach_element(&dhcp
->sections
, e
) {
692 struct uci_section
*s
= uci_to_section(e
);
693 if (!strcmp(s
->type
, "dhcp"))
698 if (config
.dhcp_statefile
) {
699 char *path
= strdup(config
.dhcp_statefile
);
701 mkdir_p(dirname(path
), 0755);
706 ubus_apply_network();
709 bool any_dhcpv6_slave
= false, any_ra_slave
= false, any_ndp_slave
= false;
712 list_for_each_entry(i
, &interfaces
, head
) {
716 if (i
->dhcpv6
== RELAYD_HYBRID
|| i
->dhcpv6
== RELAYD_RELAY
)
717 any_dhcpv6_slave
= true;
719 if (i
->ra
== RELAYD_HYBRID
|| i
->ra
== RELAYD_RELAY
)
722 if (i
->ndp
== RELAYD_HYBRID
|| i
->ndp
== RELAYD_RELAY
)
723 any_ndp_slave
= true;
726 /* Evaluate hybrid mode for master */
727 list_for_each_entry(i
, &interfaces
, head
) {
731 enum odhcpd_mode hybrid_mode
= RELAYD_DISABLED
;
733 if (!ubus_has_prefix(i
->name
, i
->ifname
))
734 hybrid_mode
= RELAYD_RELAY
;
737 if (i
->dhcpv6
== RELAYD_HYBRID
)
738 i
->dhcpv6
= hybrid_mode
;
740 if (i
->dhcpv6
== RELAYD_RELAY
&& !any_dhcpv6_slave
)
741 i
->dhcpv6
= RELAYD_DISABLED
;
743 if (i
->ra
== RELAYD_HYBRID
)
746 if (i
->ra
== RELAYD_RELAY
&& !any_ra_slave
)
747 i
->ra
= RELAYD_DISABLED
;
749 if (i
->ndp
== RELAYD_HYBRID
)
750 i
->ndp
= hybrid_mode
;
752 if (i
->ndp
== RELAYD_RELAY
&& !any_ndp_slave
)
753 i
->ndp
= RELAYD_DISABLED
;
755 if (i
->dhcpv6
== RELAYD_RELAY
|| i
->ra
== RELAYD_RELAY
|| i
->ndp
== RELAYD_RELAY
)
760 list_for_each_entry_safe(i
, n
, &interfaces
, head
) {
762 /* Resolve hybrid mode */
763 if (i
->dhcpv6
== RELAYD_HYBRID
)
764 i
->dhcpv6
= (master
&& master
->dhcpv6
== RELAYD_RELAY
) ?
765 RELAYD_RELAY
: RELAYD_SERVER
;
767 if (i
->ra
== RELAYD_HYBRID
)
768 i
->ra
= (master
&& master
->ra
== RELAYD_RELAY
) ?
769 RELAYD_RELAY
: RELAYD_SERVER
;
771 if (i
->ndp
== RELAYD_HYBRID
)
772 i
->ndp
= (master
&& master
->ndp
== RELAYD_RELAY
) ?
773 RELAYD_RELAY
: RELAYD_DISABLED
;
775 setup_router_interface(i
, !i
->ignore
|| i
->ra
!= RELAYD_DISABLED
);
776 setup_dhcpv6_interface(i
, !i
->ignore
|| i
->dhcpv6
!= RELAYD_DISABLED
);
777 setup_ndp_interface(i
, !i
->ignore
|| i
->ndp
!= RELAYD_DISABLED
);
778 setup_dhcpv4_interface(i
, !i
->ignore
|| i
->dhcpv4
!= RELAYD_DISABLED
);
783 ndp_handle_addr6_dump();
784 uci_unload(uci
, dhcp
);
785 uci_free_context(uci
);
788 static void handle_signal(int signal
)
792 if (signal
== SIGHUP
) {
793 if (write(reload_pipe
[1], b
, sizeof(b
)) < 0) {}
798 static void reload_cb(struct uloop_fd
*u
, _unused
unsigned int events
)
801 if (read(u
->fd
, b
, sizeof(b
)) < 0) {}
806 static struct uloop_fd reload_fd
= { .cb
= reload_cb
};
808 void odhcpd_run(void)
810 if (pipe2(reload_pipe
, O_NONBLOCK
| O_CLOEXEC
) < 0) {}
812 reload_fd
.fd
= reload_pipe
[0];
813 uloop_fd_add(&reload_fd
, ULOOP_READ
);
815 signal(SIGTERM
, handle_signal
);
816 signal(SIGINT
, handle_signal
);
817 signal(SIGHUP
, handle_signal
);
827 while (!list_empty(&interfaces
))
828 close_interface(list_first_entry(&interfaces
, struct interface
, head
));