10 static struct blob_buf b
;
11 struct list_head leases
= LIST_HEAD_INIT(leases
);
12 struct list_head interfaces
= LIST_HEAD_INIT(interfaces
);
13 struct config config
= {false, NULL
, NULL
};
18 IFACE_ATTR_DYNAMICDHCP
,
31 IFACE_ATTR_ULA_COMPAT
,
32 IFACE_ATTR_RA_DEFAULT
,
33 IFACE_ATTR_RA_MANAGEMENT
,
34 IFACE_ATTR_RA_OFFLINK
,
35 IFACE_ATTR_RA_PREFERENCE
,
36 IFACE_ATTR_NDPROXY_ROUTING
,
37 IFACE_ATTR_NDPROXY_SLAVE
,
38 IFACE_ATTR_NDPROXY_STATIC
,
42 static const struct blobmsg_policy iface_attrs
[IFACE_ATTR_MAX
] = {
43 [IFACE_ATTR_INTERFACE
] = { .name
= "interface", .type
= BLOBMSG_TYPE_STRING
},
44 [IFACE_ATTR_IFNAME
] = { .name
= "ifname", .type
= BLOBMSG_TYPE_STRING
},
45 [IFACE_ATTR_DYNAMICDHCP
] = { .name
= "dynamicdhcp", .type
= BLOBMSG_TYPE_BOOL
},
46 [IFACE_ATTR_IGNORE
] = { .name
= "ignore", .type
= BLOBMSG_TYPE_BOOL
},
47 [IFACE_ATTR_LEASETIME
] = { .name
= "leasetime", .type
= BLOBMSG_TYPE_STRING
},
48 [IFACE_ATTR_START
] = { .name
= "start", .type
= BLOBMSG_TYPE_INT32
},
49 [IFACE_ATTR_LIMIT
] = { .name
= "limit", .type
= BLOBMSG_TYPE_INT32
},
50 [IFACE_ATTR_MASTER
] = { .name
= "master", .type
= BLOBMSG_TYPE_BOOL
},
51 [IFACE_ATTR_UPSTREAM
] = { .name
= "upstream", .type
= BLOBMSG_TYPE_ARRAY
},
52 [IFACE_ATTR_RA
] = { .name
= "ra", .type
= BLOBMSG_TYPE_STRING
},
53 [IFACE_ATTR_DHCPV4
] = { .name
= "dhcpv4", .type
= BLOBMSG_TYPE_STRING
},
54 [IFACE_ATTR_DHCPV6
] = { .name
= "dhcpv6", .type
= BLOBMSG_TYPE_STRING
},
55 [IFACE_ATTR_NDPROXY
] = { .name
= "ndproxy", .type
= BLOBMSG_TYPE_BOOL
},
56 [IFACE_ATTR_DNS
] = { .name
= "dns", .type
= BLOBMSG_TYPE_ARRAY
},
57 [IFACE_ATTR_DOMAIN
] = { .name
= "domain", .type
= BLOBMSG_TYPE_ARRAY
},
58 [IFACE_ATTR_ULA_COMPAT
] = { .name
= "ula_compat", .type
= BLOBMSG_TYPE_BOOL
},
59 [IFACE_ATTR_RA_DEFAULT
] = { .name
= "ra_default", .type
= BLOBMSG_TYPE_INT32
},
60 [IFACE_ATTR_RA_MANAGEMENT
] = { .name
= "ra_management", .type
= BLOBMSG_TYPE_INT32
},
61 [IFACE_ATTR_RA_OFFLINK
] = { .name
= "ra_offlink", .type
= BLOBMSG_TYPE_BOOL
},
62 [IFACE_ATTR_RA_PREFERENCE
] = { .name
= "ra_preference", .type
= BLOBMSG_TYPE_STRING
},
63 [IFACE_ATTR_NDPROXY_ROUTING
] = { .name
= "ndproxy_routing", .type
= BLOBMSG_TYPE_BOOL
},
64 [IFACE_ATTR_NDPROXY_SLAVE
] = { .name
= "ndproxy_slave", .type
= BLOBMSG_TYPE_BOOL
},
65 [IFACE_ATTR_NDPROXY_STATIC
] = { .name
= "ndproxy_static", .type
= BLOBMSG_TYPE_ARRAY
},
68 static const struct uci_blob_param_info iface_attr_info
[IFACE_ATTR_MAX
] = {
69 [IFACE_ATTR_UPSTREAM
] = { .type
= BLOBMSG_TYPE_STRING
},
70 [IFACE_ATTR_DNS
] = { .type
= BLOBMSG_TYPE_STRING
},
71 [IFACE_ATTR_DOMAIN
] = { .type
= BLOBMSG_TYPE_STRING
},
72 [IFACE_ATTR_NDPROXY_STATIC
] = { .type
= BLOBMSG_TYPE_STRING
},
75 const struct uci_blob_param_list interface_attr_list
= {
76 .n_params
= IFACE_ATTR_MAX
,
77 .params
= iface_attrs
,
78 .info
= iface_attr_info
,
92 static const struct blobmsg_policy lease_attrs
[LEASE_ATTR_MAX
] = {
93 [LEASE_ATTR_IP
] = { .name
= "ip", .type
= BLOBMSG_TYPE_STRING
},
94 [LEASE_ATTR_MAC
] = { .name
= "mac", .type
= BLOBMSG_TYPE_STRING
},
95 [LEASE_ATTR_DUID
] = { .name
= "duid", .type
= BLOBMSG_TYPE_STRING
},
96 [LEASE_ATTR_HOSTID
] = { .name
= "hostid", .type
= BLOBMSG_TYPE_STRING
},
97 [LEASE_ATTR_HOSTNAME
] = { .name
= "hostname", .type
= BLOBMSG_TYPE_STRING
},
101 const struct uci_blob_param_list lease_attr_list
= {
102 .n_params
= LEASE_ATTR_MAX
,
103 .params
= lease_attrs
,
109 ODHCPD_ATTR_LEASEFILE
,
110 ODHCPD_ATTR_LEASETRIGGER
,
115 static const struct blobmsg_policy odhcpd_attrs
[LEASE_ATTR_MAX
] = {
116 [ODHCPD_ATTR_LEGACY
] = { .name
= "legacy", .type
= BLOBMSG_TYPE_BOOL
},
117 [ODHCPD_ATTR_LEASEFILE
] = { .name
= "leasefile", .type
= BLOBMSG_TYPE_STRING
},
118 [ODHCPD_ATTR_LEASETRIGGER
] = { .name
= "leasetrigger", .type
= BLOBMSG_TYPE_STRING
},
122 const struct uci_blob_param_list odhcpd_attr_list
= {
123 .n_params
= ODHCPD_ATTR_MAX
,
124 .params
= odhcpd_attrs
,
128 static struct interface
* get_interface(const char *name
)
131 list_for_each_entry(c
, &interfaces
, head
)
132 if (!strcmp(c
->name
, name
))
138 static void clean_interface(struct interface
*iface
)
142 free(iface
->upstream
);
143 free(iface
->static_ndp
);
144 free(iface
->dhcpv4_dns
);
145 memset(&iface
->ra
, 0, sizeof(*iface
) - offsetof(struct interface
, ra
));
149 static void close_interface(struct interface
*iface
)
151 if (iface
->head
.next
)
152 list_del(&iface
->head
);
154 setup_router_interface(iface
, false);
155 setup_dhcpv6_interface(iface
, false);
156 setup_ndp_interface(iface
, false);
157 setup_dhcpv4_interface(iface
, false);
159 clean_interface(iface
);
164 static int parse_mode(const char *mode
)
166 if (!strcmp(mode
, "disabled")) {
167 return RELAYD_DISABLED
;
168 } else if (!strcmp(mode
, "server")) {
169 return RELAYD_SERVER
;
170 } else if (!strcmp(mode
, "relay")) {
172 } else if (!strcmp(mode
, "hybrid")) {
173 return RELAYD_HYBRID
;
180 static void set_config(struct uci_section
*s
)
182 struct blob_attr
*tb
[ODHCPD_ATTR_MAX
], *c
;
184 blob_buf_init(&b
, 0);
185 uci_to_blob(&b
, s
, &lease_attr_list
);
186 blobmsg_parse(lease_attrs
, ODHCPD_ATTR_MAX
, tb
, blob_data(b
.head
), blob_len(b
.head
));
188 if ((c
= tb
[ODHCPD_ATTR_LEGACY
]))
189 config
.legacy
= blobmsg_get_bool(c
);
191 if ((c
= tb
[ODHCPD_ATTR_LEASEFILE
])) {
192 free(config
.dhcp_statefile
);
193 config
.dhcp_statefile
= strdup(blobmsg_get_string(c
));
196 if ((c
= tb
[ODHCPD_ATTR_LEASETRIGGER
])) {
197 free(config
.dhcp_cb
);
198 config
.dhcp_cb
= strdup(blobmsg_get_string(c
));
203 static int set_lease(struct uci_section
*s
)
205 struct blob_attr
*tb
[LEASE_ATTR_MAX
], *c
;
207 blob_buf_init(&b
, 0);
208 uci_to_blob(&b
, s
, &lease_attr_list
);
209 blobmsg_parse(lease_attrs
, LEASE_ATTR_MAX
, tb
, blob_data(b
.head
), blob_len(b
.head
));
212 if ((c
= tb
[LEASE_ATTR_HOSTNAME
]))
213 hostlen
= blobmsg_data_len(c
);
215 struct lease
*lease
= calloc(1, sizeof(*lease
) + hostlen
);
218 memcpy(lease
->hostname
, blobmsg_get_string(c
), hostlen
);
220 if ((c
= tb
[LEASE_ATTR_IP
]))
221 if (inet_pton(AF_INET
, blobmsg_get_string(c
), &lease
->ipaddr
) < 0)
224 if ((c
= tb
[LEASE_ATTR_MAC
]))
225 if (!ether_aton_r(blobmsg_get_string(c
), &lease
->mac
))
228 if ((c
= tb
[LEASE_ATTR_DUID
])) {
229 size_t duidlen
= (blobmsg_data_len(c
) - 1) / 2;
230 lease
->duid
= malloc(duidlen
);
231 ssize_t len
= odhcpd_unhexlify(lease
->duid
,
232 duidlen
, blobmsg_get_string(c
));
237 lease
->duid_len
= len
;
240 if ((c
= tb
[LEASE_ATTR_HOSTID
]))
241 if (odhcpd_unhexlify((uint8_t*)&lease
->hostid
, sizeof(lease
->hostid
),
242 blobmsg_get_string(c
)) < 0)
245 list_add(&lease
->head
, &leases
);
255 int config_parse_interface(struct blob_attr
*b
, const char *name
)
257 bool overwrite
= !!name
;
258 struct blob_attr
*tb
[IFACE_ATTR_MAX
], *c
;
259 blobmsg_parse(iface_attrs
, IFACE_ATTR_MAX
, tb
, blob_data(b
), blob_len(b
));
261 if (tb
[IFACE_ATTR_INTERFACE
])
262 name
= blobmsg_data(tb
[IFACE_ATTR_INTERFACE
]);
264 struct interface
*iface
= get_interface(name
);
266 iface
= calloc(1, sizeof(*iface
));
267 strncpy(iface
->name
, name
, sizeof(iface
->name
) - 1);
268 list_add(&iface
->head
, &interfaces
);
270 clean_interface(iface
);
273 const char *ifname
= NULL
;
276 ifname
= ubus_get_ifname(name
);
278 if ((c
= tb
[IFACE_ATTR_IFNAME
]))
279 ifname
= blobmsg_get_string(c
);
281 strncpy(iface
->ifname
, ifname
, sizeof(iface
->ifname
) - 1);
285 clean_interface(iface
);
287 if ((c
= tb
[IFACE_ATTR_DYNAMICDHCP
]))
288 iface
->no_dynamic_dhcp
= !blobmsg_get_bool(c
);
290 if ((c
= tb
[IFACE_ATTR_IGNORE
]))
291 iface
->ignore
= blobmsg_get_bool(c
);
293 if ((c
= tb
[IFACE_ATTR_LEASETIME
])) {
294 char *val
= blobmsg_get_string(c
), *endptr
;
295 double time
= strtod(val
, &endptr
);
296 if (time
&& endptr
[0]) {
297 if (endptr
[0] == 's')
299 else if (endptr
[0] == 'm')
301 else if (endptr
[0] == 'h')
303 else if (endptr
[0] == 'd')
305 else if (endptr
[0] == 'w')
306 time
*= 7 * 24 * 3600;
312 iface
->dhcpv4_leasetime
= time
;
315 if ((c
= tb
[IFACE_ATTR_START
])) {
316 iface
->dhcpv4_start
.s_addr
= htonl(blobmsg_get_u32(c
));
319 iface
->dhcpv4
= RELAYD_SERVER
;
322 if ((c
= tb
[IFACE_ATTR_LIMIT
]))
323 iface
->dhcpv4_end
.s_addr
= htonl(
324 ntohl(iface
->dhcpv4_start
.s_addr
) + blobmsg_get_u32(c
));
326 if ((c
= tb
[IFACE_ATTR_MASTER
]))
327 iface
->master
= blobmsg_get_bool(c
);
329 if ((c
= tb
[IFACE_ATTR_UPSTREAM
])) {
330 struct blob_attr
*cur
;
333 blobmsg_for_each_attr(cur
, c
, rem
) {
334 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, NULL
))
337 iface
->upstream
= realloc(iface
->upstream
,
338 iface
->upstream_len
+ blobmsg_data_len(cur
));
339 memcpy(iface
->upstream
+ iface
->upstream_len
, blobmsg_get_string(cur
), blobmsg_data_len(cur
));
340 iface
->upstream_len
+= blobmsg_data_len(cur
);
344 if ((c
= tb
[IFACE_ATTR_RA
]))
345 if ((iface
->ra
= parse_mode(blobmsg_get_string(c
))) < 0)
348 if ((c
= tb
[IFACE_ATTR_DHCPV4
]))
349 if ((iface
->dhcpv4
= parse_mode(blobmsg_get_string(c
))) < 0)
352 if ((c
= tb
[IFACE_ATTR_DHCPV6
]))
353 if ((iface
->dhcpv6
= parse_mode(blobmsg_get_string(c
))) < 0)
356 if ((c
= tb
[IFACE_ATTR_NDPROXY
]))
357 iface
->ndp
= blobmsg_get_bool(c
) ? RELAYD_RELAY
: RELAYD_DISABLED
;
359 if ((c
= tb
[IFACE_ATTR_DNS
])) {
360 struct blob_attr
*cur
;
363 blobmsg_for_each_attr(cur
, c
, rem
) {
364 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, NULL
))
367 struct in_addr addr4
;
368 struct in6_addr addr6
;
369 if (inet_pton(AF_INET
, blobmsg_get_string(cur
), &addr4
) == 1) {
370 iface
->dhcpv4_dns
= realloc(iface
->dhcpv4_dns
,
371 (++iface
->dhcpv4_dns_cnt
) * sizeof(*iface
->dhcpv4_dns
));
372 iface
->dhcpv4_dns
[iface
->dhcpv4_dns_cnt
- 1] = addr4
;
373 } else if (inet_pton(AF_INET6
, blobmsg_get_string(cur
), &addr6
) == 1) {
374 iface
->dns
= realloc(iface
->dns
,
375 (++iface
->dns_cnt
) * sizeof(*iface
->dns
));
376 iface
->dns
[iface
->dns_cnt
- 1] = addr6
;
383 if ((c
= tb
[IFACE_ATTR_DOMAIN
])) {
384 struct blob_attr
*cur
;
387 blobmsg_for_each_attr(cur
, c
, rem
) {
388 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, NULL
))
392 int len
= dn_comp(blobmsg_get_string(cur
), buf
, sizeof(buf
), NULL
, NULL
);
396 iface
->search
= realloc(iface
->search
, iface
->search_len
+ len
);
397 memcpy(&iface
->search
[iface
->search_len
], buf
, len
);
398 iface
->search_len
+= len
;
402 if ((c
= tb
[IFACE_ATTR_ULA_COMPAT
]))
403 iface
->deprecate_ula_if_public_avail
= blobmsg_get_bool(c
);
405 if ((c
= tb
[IFACE_ATTR_RA_DEFAULT
]))
406 iface
->default_router
= blobmsg_get_u32(c
);
408 if ((c
= tb
[IFACE_ATTR_RA_MANAGEMENT
]))
409 iface
->managed
= blobmsg_get_u32(c
);
411 if ((c
= tb
[IFACE_ATTR_RA_OFFLINK
]))
412 iface
->ra_not_onlink
= blobmsg_get_bool(c
);
414 if ((c
= tb
[IFACE_ATTR_RA_PREFERENCE
])) {
415 const char *prio
= blobmsg_get_string(c
);
417 if (!strcmp(prio
, "high"))
418 iface
->route_preference
= 1;
419 else if (!strcmp(prio
, "low"))
420 iface
->route_preference
= -1;
421 else if (!strcmp(prio
, "medium") || !strcmp(prio
, "default"))
422 iface
->route_preference
= 0;
427 if ((c
= tb
[IFACE_ATTR_NDPROXY_ROUTING
]))
428 iface
->learn_routes
= blobmsg_get_bool(c
);
430 if ((c
= tb
[IFACE_ATTR_NDPROXY_SLAVE
]))
431 iface
->external
= blobmsg_get_bool(c
);
433 if ((c
= tb
[IFACE_ATTR_NDPROXY_STATIC
])) {
434 struct blob_attr
*cur
;
437 blobmsg_for_each_attr(cur
, c
, rem
) {
438 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
|| !blobmsg_check_attr(cur
, NULL
))
441 int len
= blobmsg_data_len(cur
);
442 iface
->static_ndp
= realloc(iface
->static_ndp
, iface
->static_ndp_len
+ len
);
443 memcpy(&iface
->static_ndp
[iface
->static_ndp_len
], blobmsg_get_string(cur
), len
);
444 iface
->static_ndp_len
+= len
;
448 iface
->ignore
= (iface
->ifindex
= if_nametoindex(iface
->ifname
)) < 0;
452 close_interface(iface
);
456 static int set_interface(struct uci_section
*s
)
458 blob_buf_init(&b
, 0);
459 uci_to_blob(&b
, s
, &interface_attr_list
);
460 return config_parse_interface(b
.head
, s
->e
.name
);
464 static volatile bool do_reload
= false;
465 static void set_stop(int signal
)
468 do_reload
= (signal
== SIGHUP
);
471 void odhcpd_run(void)
473 struct uci_context
*uci
= uci_alloc_context();
474 signal(SIGTERM
, set_stop
);
475 signal(SIGHUP
, set_stop
);
476 signal(SIGINT
, set_stop
);
482 list_for_each_entry(l
, &leases
, head
) {
488 struct uci_package
*dhcp
= NULL
;
489 if (!uci_load(uci
, "dhcp", &dhcp
)) {
490 struct uci_element
*e
;
491 uci_foreach_element(&dhcp
->sections
, e
) {
492 struct uci_section
*s
= uci_to_section(e
);
493 if (!strcmp(s
->type
, "lease"))
495 else if (!strcmp(s
->type
, "odhcpd"))
499 uci_foreach_element(&dhcp
->sections
, e
) {
500 struct uci_section
*s
= uci_to_section(e
);
501 if (!strcmp(s
->type
, "dhcp"))
507 ubus_apply_network();
510 // Evaluate hybrid mode for master
511 struct interface
*master
= NULL
, *i
;
512 list_for_each_entry(i
, &interfaces
, head
) {
516 enum odhcpd_mode hybrid_mode
= RELAYD_DISABLED
;
517 if (i
->dhcpv6
== RELAYD_HYBRID
)
518 i
->dhcpv6
= hybrid_mode
;
520 if (i
->ra
== RELAYD_HYBRID
)
523 if (i
->ndp
== RELAYD_HYBRID
)
524 i
->ndp
= hybrid_mode
;
526 if (i
->dhcpv6
== RELAYD_RELAY
|| i
->ra
== RELAYD_RELAY
|| i
->ndp
== RELAYD_RELAY
)
531 list_for_each_entry(i
, &interfaces
, head
) {
532 if (i
->inuse
&& !i
->ignore
) {
533 // Resolve hybrid mode
534 if (i
->dhcpv6
== RELAYD_HYBRID
)
535 i
->dhcpv6
= (master
&& master
->dhcpv6
== RELAYD_RELAY
) ?
536 RELAYD_RELAY
: RELAYD_SERVER
;
538 if (i
->ra
== RELAYD_HYBRID
)
539 i
->ra
= (master
&& master
->ra
== RELAYD_RELAY
) ?
540 RELAYD_RELAY
: RELAYD_SERVER
;
542 if (i
->ndp
== RELAYD_HYBRID
)
543 i
->ndp
= (master
&& master
->ndp
== RELAYD_RELAY
) ?
544 RELAYD_RELAY
: RELAYD_SERVER
;
546 setup_router_interface(i
, true);
547 setup_dhcpv6_interface(i
, true);
548 setup_ndp_interface(i
, true);
549 setup_dhcpv4_interface(i
, true);
558 uci_unload(uci
, dhcp
);