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
};
22 IFACE_ATTR_DYNAMICDHCP
,
35 IFACE_ATTR_RA_DEFAULT
,
36 IFACE_ATTR_RA_MANAGEMENT
,
37 IFACE_ATTR_RA_OFFLINK
,
38 IFACE_ATTR_RA_PREFERENCE
,
39 IFACE_ATTR_NDPROXY_ROUTING
,
40 IFACE_ATTR_NDPROXY_SLAVE
,
41 IFACE_ATTR_NDPROXY_STATIC
,
45 static const struct blobmsg_policy iface_attrs
[IFACE_ATTR_MAX
] = {
46 [IFACE_ATTR_INTERFACE
] = { .name
= "interface", .type
= BLOBMSG_TYPE_STRING
},
47 [IFACE_ATTR_IFNAME
] = { .name
= "ifname", .type
= BLOBMSG_TYPE_STRING
},
48 [IFACE_ATTR_NETWORKID
] = { .name
= "networkid", .type
= BLOBMSG_TYPE_STRING
},
49 [IFACE_ATTR_DYNAMICDHCP
] = { .name
= "dynamicdhcp", .type
= BLOBMSG_TYPE_BOOL
},
50 [IFACE_ATTR_IGNORE
] = { .name
= "ignore", .type
= BLOBMSG_TYPE_BOOL
},
51 [IFACE_ATTR_LEASETIME
] = { .name
= "leasetime", .type
= BLOBMSG_TYPE_STRING
},
52 [IFACE_ATTR_START
] = { .name
= "start", .type
= BLOBMSG_TYPE_INT32
},
53 [IFACE_ATTR_LIMIT
] = { .name
= "limit", .type
= BLOBMSG_TYPE_INT32
},
54 [IFACE_ATTR_MASTER
] = { .name
= "master", .type
= BLOBMSG_TYPE_BOOL
},
55 [IFACE_ATTR_UPSTREAM
] = { .name
= "upstream", .type
= BLOBMSG_TYPE_ARRAY
},
56 [IFACE_ATTR_RA
] = { .name
= "ra", .type
= BLOBMSG_TYPE_STRING
},
57 [IFACE_ATTR_DHCPV4
] = { .name
= "dhcpv4", .type
= BLOBMSG_TYPE_STRING
},
58 [IFACE_ATTR_DHCPV6
] = { .name
= "dhcpv6", .type
= BLOBMSG_TYPE_STRING
},
59 [IFACE_ATTR_NDP
] = { .name
= "ndp", .type
= BLOBMSG_TYPE_STRING
},
60 [IFACE_ATTR_DNS
] = { .name
= "dns", .type
= BLOBMSG_TYPE_ARRAY
},
61 [IFACE_ATTR_DOMAIN
] = { .name
= "domain", .type
= BLOBMSG_TYPE_ARRAY
},
62 [IFACE_ATTR_RA_DEFAULT
] = { .name
= "ra_default", .type
= BLOBMSG_TYPE_INT32
},
63 [IFACE_ATTR_RA_MANAGEMENT
] = { .name
= "ra_management", .type
= BLOBMSG_TYPE_INT32
},
64 [IFACE_ATTR_RA_OFFLINK
] = { .name
= "ra_offlink", .type
= BLOBMSG_TYPE_BOOL
},
65 [IFACE_ATTR_RA_PREFERENCE
] = { .name
= "ra_preference", .type
= BLOBMSG_TYPE_STRING
},
66 [IFACE_ATTR_NDPROXY_ROUTING
] = { .name
= "ndproxy_routing", .type
= BLOBMSG_TYPE_BOOL
},
67 [IFACE_ATTR_NDPROXY_SLAVE
] = { .name
= "ndproxy_slave", .type
= BLOBMSG_TYPE_BOOL
},
68 [IFACE_ATTR_NDPROXY_STATIC
] = { .name
= "ndproxy_static", .type
= BLOBMSG_TYPE_ARRAY
},
71 static const struct uci_blob_param_info iface_attr_info
[IFACE_ATTR_MAX
] = {
72 [IFACE_ATTR_UPSTREAM
] = { .type
= BLOBMSG_TYPE_STRING
},
73 [IFACE_ATTR_DNS
] = { .type
= BLOBMSG_TYPE_STRING
},
74 [IFACE_ATTR_DOMAIN
] = { .type
= BLOBMSG_TYPE_STRING
},
75 [IFACE_ATTR_NDPROXY_STATIC
] = { .type
= BLOBMSG_TYPE_STRING
},
78 const struct uci_blob_param_list interface_attr_list
= {
79 .n_params
= IFACE_ATTR_MAX
,
80 .params
= iface_attrs
,
81 .info
= iface_attr_info
,
95 static const struct blobmsg_policy lease_attrs
[LEASE_ATTR_MAX
] = {
96 [LEASE_ATTR_IP
] = { .name
= "ip", .type
= BLOBMSG_TYPE_STRING
},
97 [LEASE_ATTR_MAC
] = { .name
= "mac", .type
= BLOBMSG_TYPE_STRING
},
98 [LEASE_ATTR_DUID
] = { .name
= "duid", .type
= BLOBMSG_TYPE_STRING
},
99 [LEASE_ATTR_HOSTID
] = { .name
= "hostid", .type
= BLOBMSG_TYPE_STRING
},
100 [LEASE_ATTR_NAME
] = { .name
= "name", .type
= BLOBMSG_TYPE_STRING
},
104 const struct uci_blob_param_list lease_attr_list
= {
105 .n_params
= LEASE_ATTR_MAX
,
106 .params
= lease_attrs
,
112 ODHCPD_ATTR_LEASEFILE
,
113 ODHCPD_ATTR_LEASETRIGGER
,
118 static const struct blobmsg_policy odhcpd_attrs
[LEASE_ATTR_MAX
] = {
119 [ODHCPD_ATTR_LEGACY
] = { .name
= "legacy", .type
= BLOBMSG_TYPE_BOOL
},
120 [ODHCPD_ATTR_LEASEFILE
] = { .name
= "leasefile", .type
= BLOBMSG_TYPE_STRING
},
121 [ODHCPD_ATTR_LEASETRIGGER
] = { .name
= "leasetrigger", .type
= BLOBMSG_TYPE_STRING
},
125 const struct uci_blob_param_list odhcpd_attr_list
= {
126 .n_params
= ODHCPD_ATTR_MAX
,
127 .params
= odhcpd_attrs
,
131 static struct interface
* get_interface(const char *name
)
134 list_for_each_entry(c
, &interfaces
, head
)
135 if (!strcmp(c
->name
, name
))
141 static void clean_interface(struct interface
*iface
)
145 free(iface
->upstream
);
146 free(iface
->static_ndp
);
147 free(iface
->dhcpv4_dns
);
148 memset(&iface
->ra
, 0, sizeof(*iface
) - offsetof(struct interface
, ra
));
152 static void close_interface(struct interface
*iface
)
154 if (iface
->head
.next
)
155 list_del(&iface
->head
);
157 setup_router_interface(iface
, false);
158 setup_dhcpv6_interface(iface
, false);
159 setup_ndp_interface(iface
, false);
160 setup_dhcpv4_interface(iface
, false);
162 clean_interface(iface
);
167 static int parse_mode(const char *mode
)
169 if (!strcmp(mode
, "disabled")) {
170 return RELAYD_DISABLED
;
171 } else if (!strcmp(mode
, "server")) {
172 return RELAYD_SERVER
;
173 } else if (!strcmp(mode
, "relay")) {
175 } else if (!strcmp(mode
, "hybrid")) {
176 return RELAYD_HYBRID
;
183 static void set_config(struct uci_section
*s
)
185 struct blob_attr
*tb
[ODHCPD_ATTR_MAX
], *c
;
187 blob_buf_init(&b
, 0);
188 uci_to_blob(&b
, s
, &odhcpd_attr_list
);
189 blobmsg_parse(odhcpd_attrs
, ODHCPD_ATTR_MAX
, tb
, blob_data(b
.head
), blob_len(b
.head
));
191 if ((c
= tb
[ODHCPD_ATTR_LEGACY
]))
192 config
.legacy
= blobmsg_get_bool(c
);
194 if ((c
= tb
[ODHCPD_ATTR_LEASEFILE
])) {
195 free(config
.dhcp_statefile
);
196 config
.dhcp_statefile
= strdup(blobmsg_get_string(c
));
199 if ((c
= tb
[ODHCPD_ATTR_LEASETRIGGER
])) {
200 free(config
.dhcp_cb
);
201 config
.dhcp_cb
= strdup(blobmsg_get_string(c
));
206 static int set_lease(struct uci_section
*s
)
208 struct blob_attr
*tb
[LEASE_ATTR_MAX
], *c
;
210 blob_buf_init(&b
, 0);
211 uci_to_blob(&b
, s
, &lease_attr_list
);
212 blobmsg_parse(lease_attrs
, LEASE_ATTR_MAX
, tb
, blob_data(b
.head
), blob_len(b
.head
));
215 if ((c
= tb
[LEASE_ATTR_NAME
]))
216 hostlen
= blobmsg_data_len(c
);
218 struct lease
*lease
= calloc(1, sizeof(*lease
) + hostlen
);
223 memcpy(lease
->hostname
, blobmsg_get_string(c
), hostlen
);
225 if ((c
= tb
[LEASE_ATTR_IP
]))
226 if (inet_pton(AF_INET
, blobmsg_get_string(c
), &lease
->ipaddr
) < 0)
229 if ((c
= tb
[LEASE_ATTR_MAC
]))
230 if (!ether_aton_r(blobmsg_get_string(c
), &lease
->mac
))
233 if ((c
= tb
[LEASE_ATTR_DUID
])) {
234 size_t duidlen
= (blobmsg_data_len(c
) - 1) / 2;
235 lease
->duid
= malloc(duidlen
);
239 ssize_t len
= odhcpd_unhexlify(lease
->duid
,
240 duidlen
, blobmsg_get_string(c
));
245 lease
->duid_len
= len
;
248 if ((c
= tb
[LEASE_ATTR_HOSTID
]))
249 if (odhcpd_unhexlify((uint8_t*)&lease
->hostid
, sizeof(lease
->hostid
),
250 blobmsg_get_string(c
)) < 0)
253 list_add(&lease
->head
, &leases
);
265 int config_parse_interface(void *data
, size_t len
, const char *name
, bool overwrite
)
267 struct blob_attr
*tb
[IFACE_ATTR_MAX
], *c
;
268 blobmsg_parse(iface_attrs
, IFACE_ATTR_MAX
, tb
, data
, len
);
270 if (tb
[IFACE_ATTR_INTERFACE
])
271 name
= blobmsg_get_string(tb
[IFACE_ATTR_INTERFACE
]);
276 struct interface
*iface
= get_interface(name
);
278 iface
= calloc(1, sizeof(*iface
));
282 strncpy(iface
->name
, name
, sizeof(iface
->name
) - 1);
283 list_add(&iface
->head
, &interfaces
);
286 const char *ifname
= NULL
;
288 if (overwrite
|| !iface
->ifname
[0])
289 ifname
= ubus_get_ifname(name
);
293 if ((c
= tb
[IFACE_ATTR_IFNAME
]))
294 ifname
= blobmsg_get_string(c
);
295 else if ((c
= tb
[IFACE_ATTR_NETWORKID
]))
296 ifname
= blobmsg_get_string(c
);
299 if (!iface
->ifname
[0] && !ifname
)
303 strncpy(iface
->ifname
, ifname
, sizeof(iface
->ifname
) - 1);
307 if ((c
= tb
[IFACE_ATTR_DYNAMICDHCP
]))
308 iface
->no_dynamic_dhcp
= !blobmsg_get_bool(c
);
310 if (overwrite
&& (c
= tb
[IFACE_ATTR_IGNORE
]))
311 iface
->ignore
= blobmsg_get_bool(c
);
313 if ((c
= tb
[IFACE_ATTR_LEASETIME
])) {
314 char *val
= blobmsg_get_string(c
), *endptr
;
315 double time
= strtod(val
, &endptr
);
316 if (time
&& endptr
[0]) {
317 if (endptr
[0] == 's')
319 else if (endptr
[0] == 'm')
321 else if (endptr
[0] == 'h')
323 else if (endptr
[0] == 'd')
325 else if (endptr
[0] == 'w')
326 time
*= 7 * 24 * 3600;
332 iface
->dhcpv4_leasetime
= time
;
335 if ((c
= tb
[IFACE_ATTR_START
])) {
336 iface
->dhcpv4_start
.s_addr
= htonl(blobmsg_get_u32(c
));
339 iface
->dhcpv4
= RELAYD_SERVER
;
342 if ((c
= tb
[IFACE_ATTR_LIMIT
]))
343 iface
->dhcpv4_end
.s_addr
= htonl(
344 ntohl(iface
->dhcpv4_start
.s_addr
) + blobmsg_get_u32(c
));
346 if ((c
= tb
[IFACE_ATTR_MASTER
]))
347 iface
->master
= blobmsg_get_bool(c
);
349 if (overwrite
&& (c
= tb
[IFACE_ATTR_UPSTREAM
])) {
350 struct blob_attr
*cur
;
353 blobmsg_for_each_attr(cur
, c
, rem
) {
354 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, NULL
))
357 iface
->upstream
= realloc(iface
->upstream
,
358 iface
->upstream_len
+ blobmsg_data_len(cur
));
359 if (!iface
->upstream
)
362 memcpy(iface
->upstream
+ iface
->upstream_len
, blobmsg_get_string(cur
), blobmsg_data_len(cur
));
363 iface
->upstream_len
+= blobmsg_data_len(cur
);
368 if ((c
= tb
[IFACE_ATTR_RA
])) {
369 if ((mode
= parse_mode(blobmsg_get_string(c
))) >= 0)
375 if ((c
= tb
[IFACE_ATTR_DHCPV4
])) {
376 if ((mode
= parse_mode(blobmsg_get_string(c
))) >= 0)
377 iface
->dhcpv4
= mode
;
382 if ((c
= tb
[IFACE_ATTR_DHCPV6
])) {
383 if ((mode
= parse_mode(blobmsg_get_string(c
))) >= 0)
384 iface
->dhcpv6
= mode
;
389 if ((c
= tb
[IFACE_ATTR_NDP
])) {
390 if ((mode
= parse_mode(blobmsg_get_string(c
))) >= 0)
396 if ((c
= tb
[IFACE_ATTR_DNS
])) {
397 struct blob_attr
*cur
;
400 iface
->always_rewrite_dns
= true;
401 blobmsg_for_each_attr(cur
, c
, rem
) {
402 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, NULL
))
405 struct in_addr addr4
;
406 struct in6_addr addr6
;
407 if (inet_pton(AF_INET
, blobmsg_get_string(cur
), &addr4
) == 1) {
408 iface
->dhcpv4_dns
= realloc(iface
->dhcpv4_dns
,
409 (++iface
->dhcpv4_dns_cnt
) * sizeof(*iface
->dhcpv4_dns
));
410 if (!iface
->dhcpv4_dns
)
413 iface
->dhcpv4_dns
[iface
->dhcpv4_dns_cnt
- 1] = addr4
;
414 } else if (inet_pton(AF_INET6
, blobmsg_get_string(cur
), &addr6
) == 1) {
415 iface
->dns
= realloc(iface
->dns
,
416 (++iface
->dns_cnt
) * sizeof(*iface
->dns
));
420 iface
->dns
[iface
->dns_cnt
- 1] = addr6
;
427 if ((c
= tb
[IFACE_ATTR_DOMAIN
])) {
428 struct blob_attr
*cur
;
431 blobmsg_for_each_attr(cur
, c
, rem
) {
432 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, NULL
))
436 int len
= dn_comp(blobmsg_get_string(cur
), buf
, sizeof(buf
), NULL
, NULL
);
440 iface
->search
= realloc(iface
->search
, iface
->search_len
+ len
);
444 memcpy(&iface
->search
[iface
->search_len
], buf
, len
);
445 iface
->search_len
+= len
;
449 if ((c
= tb
[IFACE_ATTR_RA_DEFAULT
]))
450 iface
->default_router
= blobmsg_get_u32(c
);
452 if ((c
= tb
[IFACE_ATTR_RA_MANAGEMENT
]))
453 iface
->managed
= blobmsg_get_u32(c
);
455 if ((c
= tb
[IFACE_ATTR_RA_OFFLINK
]))
456 iface
->ra_not_onlink
= blobmsg_get_bool(c
);
458 if ((c
= tb
[IFACE_ATTR_RA_PREFERENCE
])) {
459 const char *prio
= blobmsg_get_string(c
);
461 if (!strcmp(prio
, "high"))
462 iface
->route_preference
= 1;
463 else if (!strcmp(prio
, "low"))
464 iface
->route_preference
= -1;
465 else if (!strcmp(prio
, "medium") || !strcmp(prio
, "default"))
466 iface
->route_preference
= 0;
471 if ((c
= tb
[IFACE_ATTR_NDPROXY_ROUTING
]))
472 iface
->learn_routes
= blobmsg_get_bool(c
);
474 iface
->learn_routes
= true;
476 if ((c
= tb
[IFACE_ATTR_NDPROXY_SLAVE
]))
477 iface
->external
= blobmsg_get_bool(c
);
479 if ((c
= tb
[IFACE_ATTR_NDPROXY_STATIC
])) {
480 struct blob_attr
*cur
;
483 blobmsg_for_each_attr(cur
, c
, rem
) {
484 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, NULL
))
487 int len
= blobmsg_data_len(cur
);
488 iface
->static_ndp
= realloc(iface
->static_ndp
, iface
->static_ndp_len
+ len
);
489 if (!iface
->static_ndp
)
492 memcpy(&iface
->static_ndp
[iface
->static_ndp_len
], blobmsg_get_string(cur
), len
);
493 iface
->static_ndp_len
+= len
;
497 iface
->ignore
= (iface
->ifindex
= if_nametoindex(iface
->ifname
)) <= 0;
501 close_interface(iface
);
505 static int set_interface(struct uci_section
*s
)
507 blob_buf_init(&b
, 0);
508 uci_to_blob(&b
, s
, &interface_attr_list
);
509 return config_parse_interface(blob_data(b
.head
), blob_len(b
.head
), s
->e
.name
, true);
513 void odhcpd_reload(void)
515 struct uci_context
*uci
= uci_alloc_context();
517 list_for_each_entry(l
, &leases
, head
) {
523 struct interface
*master
= NULL
, *i
, *n
;
528 list_for_each_entry(i
, &interfaces
, head
)
531 struct uci_package
*dhcp
= NULL
;
532 if (!uci_load(uci
, "dhcp", &dhcp
)) {
533 struct uci_element
*e
;
534 uci_foreach_element(&dhcp
->sections
, e
) {
535 struct uci_section
*s
= uci_to_section(e
);
536 if (!strcmp(s
->type
, "host"))
538 else if (!strcmp(s
->type
, "odhcpd"))
542 uci_foreach_element(&dhcp
->sections
, e
) {
543 struct uci_section
*s
= uci_to_section(e
);
544 if (!strcmp(s
->type
, "dhcp"))
551 ubus_apply_network();
554 // Evaluate hybrid mode for master
555 list_for_each_entry(i
, &interfaces
, head
) {
559 enum odhcpd_mode hybrid_mode
= RELAYD_DISABLED
;
561 if (ubus_has_prefix(i
->name
, i
->ifname
))
562 hybrid_mode
= RELAYD_RELAY
;
565 if (i
->dhcpv6
== RELAYD_HYBRID
)
566 i
->dhcpv6
= hybrid_mode
;
568 if (i
->ra
== RELAYD_HYBRID
)
571 if (i
->ndp
== RELAYD_HYBRID
)
572 i
->ndp
= hybrid_mode
;
574 if (i
->dhcpv6
== RELAYD_RELAY
|| i
->ra
== RELAYD_RELAY
|| i
->ndp
== RELAYD_RELAY
)
579 list_for_each_entry_safe(i
, n
, &interfaces
, head
) {
581 // Resolve hybrid mode
582 if (i
->dhcpv6
== RELAYD_HYBRID
)
583 i
->dhcpv6
= (master
&& master
->dhcpv6
== RELAYD_RELAY
) ?
584 RELAYD_RELAY
: RELAYD_SERVER
;
586 if (i
->ra
== RELAYD_HYBRID
)
587 i
->ra
= (master
&& master
->ra
== RELAYD_RELAY
) ?
588 RELAYD_RELAY
: RELAYD_SERVER
;
590 if (i
->ndp
== RELAYD_HYBRID
)
591 i
->ndp
= (master
&& master
->ndp
== RELAYD_RELAY
) ?
592 RELAYD_RELAY
: RELAYD_SERVER
;
594 setup_router_interface(i
, true);
595 setup_dhcpv6_interface(i
, true);
596 setup_ndp_interface(i
, true);
597 setup_dhcpv4_interface(i
, true);
603 uci_unload(uci
, dhcp
);
604 uci_free_context(uci
);
608 static void handle_signal(int signal
)
611 if (signal
== SIGHUP
)
612 write(reload_pipe
[1], b
, sizeof(b
));
619 static void reload_cb(struct uloop_fd
*u
, _unused
unsigned int events
)
622 read(u
->fd
, b
, sizeof(b
));
626 static struct uloop_fd reload_fd
= { .cb
= reload_cb
};
628 void odhcpd_run(void)
630 pipe2(reload_pipe
, O_NONBLOCK
| O_CLOEXEC
);
631 reload_fd
.fd
= reload_pipe
[0];
632 uloop_fd_add(&reload_fd
, ULOOP_READ
);
634 signal(SIGTERM
, handle_signal
);
635 signal(SIGINT
, handle_signal
);
636 signal(SIGHUP
, handle_signal
);