• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/odhcp6c/src/ubus.c

  1 /**
  2  * SPDX-License-Identifier: BSD-2-Clause-Patent
  3  *
  4  * SPDX-FileCopyrightText: Copyright (c) 2024 SoftAtHome
  5  *
  6  * Redistribution and use in source and binary forms, with or
  7  * without modification, are permitted provided that the following
  8  * conditions are met:
  9  *
 10  * 1. Redistributions of source code must retain the above copyright
 11  * notice, this list of conditions and the following disclaimer.
 12  *
 13  * 2. Redistributions in binary form must reproduce the above
 14  * copyright notice, this list of conditions and the following
 15  * disclaimer in the documentation and/or other materials provided
 16  * with the distribution.
 17  *
 18  * Subject to the terms and conditions of this license, each
 19  * copyright holder and contributor hereby grants to those receiving
 20  * rights under this license a perpetual, worldwide, non-exclusive,
 21  * no-charge, royalty-free, irrevocable (except for failure to
 22  * satisfy the conditions of this license) patent license to make,
 23  * have made, use, offer to sell, sell, import, and otherwise
 24  * transfer this software, where such license applies only to those
 25  * patent claims, already acquired or hereafter acquired, licensable
 26  * by such copyright holder or contributor that are necessarily
 27  * infringed by:
 28  *
 29  * (a) their Contribution(s) (the licensed copyrights of copyright
 30  * holders and non-copyrightable additions of contributors, in
 31  * source or binary form) alone; or
 32  *
 33  * (b) combination of their Contribution(s) with the work of
 34  * authorship to which such Contribution(s) was added by such
 35  * copyright holder or contributor, if, at the time the Contribution
 36  * is added, such addition causes such combination to be necessarily
 37  * infringed. The patent license shall not apply to any other
 38  * combinations which include the Contribution.
 39  *
 40  * Except as expressly stated above, no rights or licenses from any
 41  * copyright holder or contributor is granted under this license,
 42  * whether expressly, by implication, estoppel or otherwise.
 43  *
 44  * DISCLAIMER
 45  *
 46  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 47  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 48  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 49  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 50  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 51  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 52  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 53  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 54  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 55  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 56  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 57  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 58  * POSSIBILITY OF SUCH DAMAGE.
 59  *
 60  */
 61 
 62 #include <arpa/inet.h>
 63 #include <inttypes.h>
 64 #include <libubox/blobmsg.h>
 65 #include <resolv.h>
 66 #include <stdio.h>
 67 #include <sys/types.h>
 68 #include <syslog.h>
 69 
 70 #include "config.h"
 71 #include "odhcp6c.h"
 72 #include "ubus.h"
 73 
 74 #define CHECK(stmt) \
 75         do { \
 76                 int ret = (stmt); \
 77                 if (ret != UBUS_STATUS_OK) \
 78                 { \
 79                         syslog(LOG_ERR, "%s failed: %s (%d)", #stmt, ubus_strerror(ret), ret); \
 80                         return ret; \
 81                 } \
 82         } while (0)
 83 
 84 #define CHECK_ALLOC(buf) \
 85         do { \
 86                 if (buf == NULL) \
 87                 { \
 88                         return UBUS_STATUS_NO_MEMORY; \
 89                 } \
 90         } while (0)
 91 
 92 enum entry_type {
 93         ENTRY_ADDRESS,
 94         ENTRY_HOST,
 95         ENTRY_ROUTE,
 96         ENTRY_PREFIX
 97 };
 98 
 99 enum {
100         RECONFIGURE_DHCP_ATTR_DSCP,
101         RECONFIGURE_DHCP_ATTR_RELEASE,
102         RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT,
103         RECONFIGURE_DHCP_ATTR_SK_PRIORITY,
104         RECONFIGURE_DHCP_ATTR_OPT_REQUESTED,
105         RECONFIGURE_DHCP_ATTR_OPT_STRICT,
106         RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE,
107         RECONFIGURE_DHCP_ATTR_OPT_FQDN,
108         RECONFIGURE_DHCP_ATTR_OPT_UNICAST,
109         RECONFIGURE_DHCP_ATTR_OPT_SEND,
110         RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES,
111         RECONFIGURE_DHCP_ATTR_REQ_PREFIXES,
112         RECONFIGURE_DHCP_ATTR_STATEFUL,
113         RECONFIGURE_DHCP_ATTR_MSG_SOLICIT,
114         RECONFIGURE_DHCP_ATTR_MSG_REQUEST,
115         RECONFIGURE_DHCP_ATTR_MSG_RENEW,
116         RECONFIGURE_DHCP_ATTR_MSG_REBIND,
117         RECONFIGURE_DHCP_ATTR_MSG_RELEASE,
118         RECONFIGURE_DHCP_ATTR_MSG_DECLINE,
119         RECONFIGURE_DHCP_ATTR_MSG_INFO_REQ,
120         RECONFIGURE_DHCP_ATTR_IRT_DEFAULT,
121         RECONFIGURE_DHCP_ATTR_IRT_MIN,
122         RECONFIGURE_DHCP_ATTR_RAND_FACTOR,
123         RECONFIGURE_DHCP_ATTR_AUTH_PROTO,
124         RECONFIGURE_DHCP_ATTR_AUTH_TOKEN,
125         RECONFIGURE_DHCP_ATTR_MAX,
126 };
127 
128 struct ubus_context *ubus = NULL;
129 static struct blob_buf b;
130 static char ubus_name[24];
131 
132 static int ubus_handle_get_state(struct ubus_context *ctx, struct ubus_object *obj,
133                 struct ubus_request_data *req, const char *method, struct blob_attr *msg);
134 static int ubus_handle_get_stats(struct ubus_context *ctx, struct ubus_object *obj,
135                 struct ubus_request_data *req, const char *method, struct blob_attr *msg);
136 static int ubus_handle_reset_stats(struct ubus_context *ctx, struct ubus_object *obj,
137                 struct ubus_request_data *req, const char *method, struct blob_attr *msg);
138 static int ubus_handle_reconfigure_dhcp(struct ubus_context *ctx, struct ubus_object *obj,
139                 struct ubus_request_data *req, const char *method, struct blob_attr *msg);
140 static int ubus_handle_renew(struct ubus_context *ctx, struct ubus_object *obj,
141                 struct ubus_request_data *req, const char *method, struct blob_attr *msg);
142 static int ubus_handle_release(struct ubus_context *ctx, struct ubus_object *obj,
143                 struct ubus_request_data *req, const char *method, struct blob_attr *msg);
144 
145 static const struct blobmsg_policy reconfigure_dhcp_policy[RECONFIGURE_DHCP_ATTR_MAX] = {
146         [RECONFIGURE_DHCP_ATTR_DSCP] = { .name = "dscp", .type = BLOBMSG_TYPE_INT32},
147         [RECONFIGURE_DHCP_ATTR_RELEASE] = { .name = "release", .type = BLOBMSG_TYPE_BOOL},
148         [RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT] = { .name = "sol_timeout", .type = BLOBMSG_TYPE_INT32},
149         [RECONFIGURE_DHCP_ATTR_SK_PRIORITY] = { .name = "sk_prio", .type = BLOBMSG_TYPE_INT32},
150         [RECONFIGURE_DHCP_ATTR_OPT_REQUESTED] = { .name = "opt_requested", .type = BLOBMSG_TYPE_ARRAY},
151         [RECONFIGURE_DHCP_ATTR_OPT_STRICT] = { .name = "opt_strict", .type = BLOBMSG_TYPE_BOOL},
152         [RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE] = { .name = "opt_reconfigure", .type = BLOBMSG_TYPE_BOOL},
153         [RECONFIGURE_DHCP_ATTR_OPT_FQDN] = { .name = "opt_fqdn", .type = BLOBMSG_TYPE_BOOL},
154         [RECONFIGURE_DHCP_ATTR_OPT_UNICAST] = { .name = "opt_unicast", .type = BLOBMSG_TYPE_BOOL},
155         [RECONFIGURE_DHCP_ATTR_OPT_SEND] = { .name = "opt_send", .type = BLOBMSG_TYPE_ARRAY},
156         [RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES] = { .name = "req_addresses", .type = BLOBMSG_TYPE_STRING},
157         [RECONFIGURE_DHCP_ATTR_REQ_PREFIXES] = { .name = "req_prefixes", .type = BLOBMSG_TYPE_INT32},
158         [RECONFIGURE_DHCP_ATTR_STATEFUL] = { .name = "stateful_only", .type = BLOBMSG_TYPE_BOOL},
159         [RECONFIGURE_DHCP_ATTR_MSG_SOLICIT] = { .name = "msg_solicit", .type = BLOBMSG_TYPE_TABLE},
160         [RECONFIGURE_DHCP_ATTR_MSG_REQUEST] = { .name = "msg_request", .type = BLOBMSG_TYPE_TABLE},
161         [RECONFIGURE_DHCP_ATTR_MSG_RENEW] = { .name = "msg_renew", .type = BLOBMSG_TYPE_TABLE},
162         [RECONFIGURE_DHCP_ATTR_MSG_REBIND] = { .name = "msg_rebind", .type = BLOBMSG_TYPE_TABLE},
163         [RECONFIGURE_DHCP_ATTR_MSG_RELEASE] = { .name = "msg_release", .type = BLOBMSG_TYPE_TABLE},
164         [RECONFIGURE_DHCP_ATTR_MSG_DECLINE] = { .name = "msg_decline", .type = BLOBMSG_TYPE_TABLE},
165         [RECONFIGURE_DHCP_ATTR_MSG_INFO_REQ] = { .name = "msg_inforeq", .type = BLOBMSG_TYPE_TABLE},
166         [RECONFIGURE_DHCP_ATTR_IRT_DEFAULT] = { .name = "irt_default", .type = BLOBMSG_TYPE_INT32},
167         [RECONFIGURE_DHCP_ATTR_IRT_MIN] = { .name = "irt_min", .type = BLOBMSG_TYPE_INT32},
168         [RECONFIGURE_DHCP_ATTR_RAND_FACTOR] = { .name = "rand_factor", .type = BLOBMSG_TYPE_INT32},
169         [RECONFIGURE_DHCP_ATTR_AUTH_PROTO] = { .name = "auth_protocol", .type = BLOBMSG_TYPE_STRING},
170         [RECONFIGURE_DHCP_ATTR_AUTH_TOKEN] = { .name = "auth_token", .type = BLOBMSG_TYPE_STRING},
171 };
172 
173 static struct ubus_method odhcp6c_object_methods[] = {
174         UBUS_METHOD_NOARG("get_state", ubus_handle_get_state),
175         UBUS_METHOD_NOARG("get_statistics", ubus_handle_get_stats),
176         UBUS_METHOD_NOARG("reset_statistics", ubus_handle_reset_stats),
177         UBUS_METHOD("reconfigure_dhcp", ubus_handle_reconfigure_dhcp, reconfigure_dhcp_policy),
178         UBUS_METHOD_NOARG("renew", ubus_handle_renew),
179         UBUS_METHOD_NOARG("release", ubus_handle_release),
180 };
181 
182 static struct ubus_object_type odhcp6c_object_type =
183         UBUS_OBJECT_TYPE("odhcp6c", odhcp6c_object_methods);
184 
185 static struct ubus_object odhcp6c_object = {
186         .name = NULL,
187         .type = &odhcp6c_object_type,
188         .methods = odhcp6c_object_methods,
189         .n_methods = ARRAY_SIZE(odhcp6c_object_methods),
190 };
191 
192 static void ubus_disconnect_cb(struct ubus_context *ubus)
193 {
194         int ret;
195 
196         ret = ubus_reconnect(ubus, NULL);
197         if (ret) {
198                 syslog(LOG_ERR, "Cannot reconnect to ubus: %s", ubus_strerror(ret));
199                 ubus_destroy(ubus);
200         }
201 }
202 
203 char *ubus_init(const char* interface)
204 {
205         int ret = 0;
206 
207         if (!(ubus = ubus_connect(NULL)))
208                 return NULL;
209 
210         snprintf(ubus_name, 24, "odhcp6c.%s", interface);
211         odhcp6c_object.name = ubus_name;
212 
213         ret = ubus_add_object(ubus, &odhcp6c_object);
214         if (ret) {
215                 ubus_destroy(ubus);
216                 return (char *)ubus_strerror(ret);
217         }
218 
219         ubus->connection_lost = ubus_disconnect_cb;
220         return NULL;
221 }
222 
223 struct ubus_context *ubus_get_ctx(void)
224 {
225         return ubus;
226 }
227 
228 void ubus_destroy(struct ubus_context *ubus)
229 {
230         syslog(LOG_NOTICE, "Disconnecting from ubus");
231 
232         if (ubus != NULL)
233                 ubus_free(ubus);
234         ubus = NULL;
235 
236         /* Forces re-initialization when we're reusing the same definitions later on. */
237         odhcp6c_object.id = 0;
238         odhcp6c_object.id = 0;
239 }
240 
241 static int ipv6_to_blob(const char *name,
242                 const struct in6_addr *addr, size_t cnt)
243 {
244         void *arr = blobmsg_open_array(&b, name);
245 
246         for (size_t i = 0; i < cnt; ++i) {
247                 char *buf = blobmsg_alloc_string_buffer(&b, NULL, INET6_ADDRSTRLEN);
248                 CHECK_ALLOC(buf);
249                 inet_ntop(AF_INET6, &addr[i], buf, INET6_ADDRSTRLEN);
250                 blobmsg_add_string_buffer(&b);
251         }
252 
253         blobmsg_close_array(&b, arr);
254         return UBUS_STATUS_OK;
255 }
256 
257 static int fqdn_to_blob(const char *name, const uint8_t *fqdn, size_t len)
258 {
259         size_t buf_size = len > 255 ? 256 : (len + 1);
260         const uint8_t *fqdn_end = fqdn + len;
261         char *buf = NULL;
262 
263         void *arr = blobmsg_open_array(&b, name);
264 
265         while (fqdn < fqdn_end) {
266                 buf = blobmsg_alloc_string_buffer(&b, name, buf_size);
267                 CHECK_ALLOC(buf);
268                 int l = dn_expand(fqdn, fqdn_end, fqdn, buf, buf_size);
269                 if (l < 1) {
270                         buf[0] = '\0';
271                         blobmsg_add_string_buffer(&b);
272                         break;
273                 }
274                 buf[l] = '\0';
275                 blobmsg_add_string_buffer(&b);
276                 fqdn += l;
277         }
278 
279         blobmsg_close_array(&b, arr);
280         return UBUS_STATUS_OK;
281 }
282 
283 static int bin_to_blob(uint8_t *opts, size_t len)
284 {
285         uint8_t *oend = opts + len, *odata;
286         uint16_t otype, olen;
287 
288         dhcpv6_for_each_option(opts, oend, otype, olen, odata) {
289                 char name[14];
290                 char *buf;
291 
292                 snprintf(name, 14, "OPTION_%hu", otype);
293                 buf = blobmsg_alloc_string_buffer(&b, name, olen * 2);
294                 CHECK_ALLOC(buf);
295                 script_hexlify(buf, odata, olen);
296                 blobmsg_add_string_buffer(&b);
297         }
298         return UBUS_STATUS_OK;
299 }
300 
301 static int entry_to_blob(const char *name, const void *data, size_t len, enum entry_type type)
302 {
303         const struct odhcp6c_entry *e = data;
304 
305         void *arr = blobmsg_open_array(&b, name);
306 
307         for (size_t i = 0; i < len / sizeof(*e); ++i) {
308                 void *entry = blobmsg_open_table(&b, name);
309 
310                 /*
311                  * The only invalid entries allowed to be passed to the script are prefix entries.
312                  * This will allow immediate removal of the old ipv6-prefix-assignment that might
313                  * otherwise be kept for up to 2 hours (see L-13 requirement of RFC 7084).
314                  */
315                 if (!e[i].valid && type != ENTRY_PREFIX)
316                         continue;
317 
318                 char *buf = blobmsg_alloc_string_buffer(&b, "target", INET6_ADDRSTRLEN);
319                 CHECK_ALLOC(buf);
320                 inet_ntop(AF_INET6, &e[i].target, buf, INET6_ADDRSTRLEN);
321                 blobmsg_add_string_buffer(&b);
322 
323                 if (type != ENTRY_HOST) {
324                         blobmsg_add_u8(&b, "length", e[i].length);
325                         if (type == ENTRY_ROUTE) {
326                                 if (!IN6_IS_ADDR_UNSPECIFIED(&e[i].router)) {
327                                         buf = blobmsg_alloc_string_buffer(&b, "router", INET6_ADDRSTRLEN);
328                                         CHECK_ALLOC(buf);
329                                         inet_ntop(AF_INET6, &e[i].router, buf, INET6_ADDRSTRLEN);
330                                         blobmsg_add_string_buffer(&b);
331                                 }
332 
333                                 blobmsg_add_u32(&b, "valid", e[i].valid);
334                                 blobmsg_add_u16(&b, "priority", e[i].priority);
335                         } else {
336                                 blobmsg_add_u32(&b, "valid", e[i].valid);
337                                 blobmsg_add_u32(&b, "preferred", e[i].preferred);
338                                 blobmsg_add_u32(&b, "t1", e[i].t1);
339                                 blobmsg_add_u32(&b, "t2", e[i].t2);
340                         }
341 
342                         if (type == ENTRY_PREFIX && ntohl(e[i].iaid) != 1) {
343                                 blobmsg_add_u32(&b, "iaid", ntohl(e[i].iaid));
344                         }
345 
346                         if (type == ENTRY_PREFIX && e[i].priority) {
347                                 // priority and router are abused for prefix exclusion
348                                 buf = blobmsg_alloc_string_buffer(&b, "excluded", INET6_ADDRSTRLEN);
349                                 CHECK_ALLOC(buf);
350                                 inet_ntop(AF_INET6, &e[i].router, buf, INET6_ADDRSTRLEN);
351                                 blobmsg_add_string_buffer(&b);
352                                 blobmsg_add_u16(&b, "excluded_length", e[i].priority);
353                         }
354                 }
355 
356                 blobmsg_close_table(&b, entry);
357         }
358 
359         blobmsg_close_array(&b, arr);
360         return UBUS_STATUS_OK;
361 }
362 
363 static int search_to_blob(const char *name, const uint8_t *start, size_t len)
364 {
365         void *arr = blobmsg_open_array(&b, name);
366         char *buf = NULL;
367 
368         for (struct odhcp6c_entry *e = (struct odhcp6c_entry*)start;
369                                 (uint8_t*)e < &start[len] &&
370                                 (uint8_t*)odhcp6c_next_entry(e) <= &start[len];
371                                 e = odhcp6c_next_entry(e)) {
372                 if (!e->valid)
373                         continue;
374 
375                 buf = blobmsg_alloc_string_buffer(&b, NULL, e->auxlen+1);
376                 CHECK_ALLOC(buf);
377                 buf = mempcpy(buf, e->auxtarget, e->auxlen);
378                 *buf = '\0';
379                 blobmsg_add_string_buffer(&b);
380         }
381 
382         blobmsg_close_array(&b, arr);
383         return UBUS_STATUS_OK;
384 }
385 
386 static int s46_to_blob_portparams(const uint8_t *data, size_t len)
387 {
388         uint8_t *odata;
389         uint16_t otype, olen;
390 
391         dhcpv6_for_each_option(data, &data[len], otype, olen, odata) {
392                 if (otype == DHCPV6_OPT_S46_PORTPARAMS &&
393                                 olen == sizeof(struct dhcpv6_s46_portparams)) {
394                         struct dhcpv6_s46_portparams *params = (void*)odata;
395                         blobmsg_add_u8(&b, "offset", params->offset);
396                         blobmsg_add_u8(&b, "psidlen", params->psid_len);
397                         blobmsg_add_u16(&b, "psid", ntohs(params->psid));
398                 }
399         }
400         return UBUS_STATUS_OK;
401 }
402 
403 static int s46_to_blob(enum odhcp6c_state state, const uint8_t *data, size_t len)
404 {
405         const char *name = (state == STATE_S46_MAPE) ? "MAPE" :
406                         (state == STATE_S46_MAPT) ? "MAPT" : "LW4O6";
407 
408         if (len == 0)
409                 return UBUS_STATUS_OK;
410 
411         char *buf = NULL;
412         void *arr = blobmsg_open_array(&b, name);
413 
414         const char *type = (state == STATE_S46_MAPE) ? "map-e" :
415                         (state == STATE_S46_MAPT) ? "map-t" : "lw4o6";
416 
417         uint8_t *odata;
418         uint16_t otype, olen;
419 
420         dhcpv6_for_each_option(data, &data[len], otype, olen, odata) {
421                 struct dhcpv6_s46_rule *rule = (struct dhcpv6_s46_rule*)odata;
422                 struct dhcpv6_s46_v4v6bind *bind = (struct dhcpv6_s46_v4v6bind*)odata;
423 
424                 void *option = blobmsg_open_table(&b, NULL);
425 
426                 if (state != STATE_S46_LW && otype == DHCPV6_OPT_S46_RULE &&
427                                 olen >= sizeof(struct dhcpv6_s46_rule)) {
428                         struct in6_addr in6 = IN6ADDR_ANY_INIT;
429 
430                         size_t prefix6len = rule->prefix6_len;
431                         prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1;
432 
433                         if (prefix6len > sizeof(in6) ||
434                                 olen < sizeof(struct dhcpv6_s46_rule) + prefix6len)
435                                 continue;
436 
437                         memcpy(&in6, rule->ipv6_prefix, prefix6len);
438 
439                         buf = blobmsg_alloc_string_buffer(&b, "ipv4prefix", INET_ADDRSTRLEN);
440                         CHECK_ALLOC(buf);
441                         inet_ntop(AF_INET, &rule->ipv4_prefix, buf, INET_ADDRSTRLEN);
442                         blobmsg_add_string_buffer(&b);
443 
444                         buf = blobmsg_alloc_string_buffer(&b, "ipv6prefix", INET6_ADDRSTRLEN);
445                         CHECK_ALLOC(buf);
446                         inet_ntop(AF_INET6, &in6, buf, INET6_ADDRSTRLEN);
447                         blobmsg_add_string_buffer(&b);
448 
449                         blobmsg_add_u8(&b, "fmr", rule->flags);
450                         blobmsg_add_string(&b, "type", type);
451                         blobmsg_add_u8(&b, "ealen", rule->ea_len);
452                         blobmsg_add_u8(&b, "prefix4len", rule->prefix4_len);
453                         blobmsg_add_u8(&b, "prefix6len", rule->prefix6_len);
454 
455                         s46_to_blob_portparams(&rule->ipv6_prefix[prefix6len],
456                                         olen - sizeof(*rule) - prefix6len);
457 
458                         dhcpv6_for_each_option(data, &data[len], otype, olen, odata) {
459                                 if (state != STATE_S46_MAPT && otype == DHCPV6_OPT_S46_BR &&
460                                                 olen == sizeof(struct in6_addr)) {
461                                         buf = blobmsg_alloc_string_buffer(&b, "br", INET6_ADDRSTRLEN);
462                                         CHECK_ALLOC(buf);
463                                         inet_ntop(AF_INET6, odata, buf, INET6_ADDRSTRLEN);
464                                         blobmsg_add_string_buffer(&b);
465                                 } else if (state == STATE_S46_MAPT && otype == DHCPV6_OPT_S46_DMR &&
466                                                 olen >= sizeof(struct dhcpv6_s46_dmr)) {
467                                         struct dhcpv6_s46_dmr *dmr = (struct dhcpv6_s46_dmr*)odata;
468                                         memset(&in6, 0, sizeof(in6));
469                                         size_t prefix6len = dmr->dmr_prefix6_len;
470                                         prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1;
471 
472                                         if (prefix6len > sizeof(in6) ||
473                                                 olen < sizeof(struct dhcpv6_s46_dmr) + prefix6len)
474                                                 continue;
475 
476                                         buf = blobmsg_alloc_string_buffer(&b, "dmr", INET6_ADDRSTRLEN);
477                                         CHECK_ALLOC(buf);
478                                         inet_ntop(AF_INET6, &in6, buf, INET6_ADDRSTRLEN);
479                                         blobmsg_add_string_buffer(&b);
480                                         blobmsg_add_u8(&b, "dmrprefix6len", dmr->dmr_prefix6_len);
481                                 }
482                         }
483                 } else if (state == STATE_S46_LW && otype == DHCPV6_OPT_S46_V4V6BIND &&
484                                 olen >= sizeof(struct dhcpv6_s46_v4v6bind)) {
485                         struct in6_addr in6 = IN6ADDR_ANY_INIT;
486 
487                         size_t prefix6len = bind->bindprefix6_len;
488                         prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1;
489 
490                         if (prefix6len > sizeof(in6) ||
491                                 olen < sizeof(struct dhcpv6_s46_v4v6bind) + prefix6len)
492                                 continue;
493 
494                         memcpy(&in6, bind->bind_ipv6_prefix, prefix6len);
495 
496                         buf = blobmsg_alloc_string_buffer(&b, "ipv4prefix", INET_ADDRSTRLEN);
497                         CHECK_ALLOC(buf);
498                         inet_ntop(AF_INET, &bind->ipv4_address, buf, INET_ADDRSTRLEN);
499                         blobmsg_add_string_buffer(&b);
500 
501                         buf = blobmsg_alloc_string_buffer(&b, "ipv6prefix", INET6_ADDRSTRLEN);
502                         CHECK_ALLOC(buf);
503                         inet_ntop(AF_INET6, &in6, buf, INET6_ADDRSTRLEN);
504                         blobmsg_add_string_buffer(&b);
505 
506                         blobmsg_add_string(&b, "type", type);
507                         blobmsg_add_u8(&b, "prefix4len", 32);
508                         blobmsg_add_u8(&b, "prefix6len", bind->bindprefix6_len);
509 
510                         s46_to_blob_portparams(&bind->bind_ipv6_prefix[prefix6len],
511                                         olen - sizeof(*bind) - prefix6len);
512 
513                         dhcpv6_for_each_option(data, &data[len], otype, olen, odata) {
514                                 if (otype == DHCPV6_OPT_S46_BR && olen == sizeof(struct in6_addr)) {
515                                         buf = blobmsg_alloc_string_buffer(&b, "br", INET6_ADDRSTRLEN);
516                                         CHECK_ALLOC(buf);
517                                         inet_ntop(AF_INET6, odata, buf, INET6_ADDRSTRLEN);
518                                         blobmsg_add_string_buffer(&b);
519                                 }
520                         }
521                 }
522                 blobmsg_close_table(&b, option);
523         }
524 
525         blobmsg_close_array(&b, arr);
526         return UBUS_STATUS_OK;
527 }
528 
529 static int states_to_blob(void)
530 {
531         char *buf = NULL;
532         size_t dns_len, search_len, custom_len, sntp_ip_len, ntp_ip_len, ntp_dns_len;
533         size_t sip_ip_len, sip_fqdn_len, aftr_name_len, cer_len, addr_len;
534         size_t s46_mapt_len, s46_mape_len, s46_lw_len, passthru_len;
535         struct in6_addr *addr = odhcp6c_get_state(STATE_SERVER_ADDR, &addr_len);
536         struct in6_addr *dns = odhcp6c_get_state(STATE_DNS, &dns_len);
537         uint8_t *search = odhcp6c_get_state(STATE_SEARCH, &search_len);
538         uint8_t *custom = odhcp6c_get_state(STATE_CUSTOM_OPTS, &custom_len);
539         struct in6_addr *sntp = odhcp6c_get_state(STATE_SNTP_IP, &sntp_ip_len);
540         struct in6_addr *ntp = odhcp6c_get_state(STATE_NTP_IP, &ntp_ip_len);
541         uint8_t *ntp_dns = odhcp6c_get_state(STATE_NTP_FQDN, &ntp_dns_len);
542         struct in6_addr *sip = odhcp6c_get_state(STATE_SIP_IP, &sip_ip_len);
543         uint8_t *sip_fqdn = odhcp6c_get_state(STATE_SIP_FQDN, &sip_fqdn_len);
544         uint8_t *aftr_name = odhcp6c_get_state(STATE_AFTR_NAME, &aftr_name_len);
545         struct in6_addr *cer = odhcp6c_get_state(STATE_CER, &cer_len);
546         uint8_t *s46_mapt = odhcp6c_get_state(STATE_S46_MAPT, &s46_mapt_len);
547         uint8_t *s46_mape = odhcp6c_get_state(STATE_S46_MAPE, &s46_mape_len);
548         uint8_t *s46_lw = odhcp6c_get_state(STATE_S46_LW, &s46_lw_len);
549         uint8_t *passthru = odhcp6c_get_state(STATE_PASSTHRU, &passthru_len);
550 
551         size_t prefix_len, address_len, ra_pref_len,
552                 ra_route_len, ra_dns_len, ra_search_len;
553         uint8_t *prefix = odhcp6c_get_state(STATE_IA_PD, &prefix_len);
554         uint8_t *address = odhcp6c_get_state(STATE_IA_NA, &address_len);
555         uint8_t *ra_pref = odhcp6c_get_state(STATE_RA_PREFIX, &ra_pref_len);
556         uint8_t *ra_route = odhcp6c_get_state(STATE_RA_ROUTE, &ra_route_len);
557         uint8_t *ra_dns = odhcp6c_get_state(STATE_RA_DNS, &ra_dns_len);
558         uint8_t *ra_search = odhcp6c_get_state(STATE_RA_SEARCH, &ra_search_len);
559 
560         blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
561 
562         blobmsg_add_string(&b, "DHCPV6_STATE", dhcpv6_state_to_str(dhcpv6_get_state()));
563 
564         CHECK(ipv6_to_blob("SERVER", addr, addr_len / sizeof(*addr)));
565         CHECK(ipv6_to_blob("RDNSS", dns, dns_len / sizeof(*dns)));
566         CHECK(ipv6_to_blob("SNTP_IP", sntp, sntp_ip_len / sizeof(*sntp)));
567         CHECK(ipv6_to_blob("NTP_IP", ntp, ntp_ip_len / sizeof(*ntp)));
568         CHECK(fqdn_to_blob("NTP_FQDN", ntp_dns, ntp_dns_len));
569         CHECK(ipv6_to_blob("SIP_IP", sip, sip_ip_len / sizeof(*sip)));
570         CHECK(fqdn_to_blob("DOMAINS", search, search_len));
571         CHECK(fqdn_to_blob("SIP_DOMAIN", sip_fqdn, sip_fqdn_len));
572         CHECK(fqdn_to_blob("AFTR", aftr_name, aftr_name_len));
573         CHECK(ipv6_to_blob("CER", cer, cer_len / sizeof(*cer)));
574         CHECK(s46_to_blob(STATE_S46_MAPE, s46_mape, s46_mape_len));
575         CHECK(s46_to_blob(STATE_S46_MAPT, s46_mapt, s46_mapt_len));
576         CHECK(s46_to_blob(STATE_S46_LW, s46_lw, s46_lw_len));
577         CHECK(bin_to_blob(custom, custom_len));
578 
579         if (odhcp6c_is_bound()) {
580                 CHECK(entry_to_blob("PREFIXES", prefix, prefix_len, ENTRY_PREFIX));
581                 CHECK(entry_to_blob("ADDRESSES", address, address_len, ENTRY_ADDRESS));
582         }
583 
584         CHECK(entry_to_blob("RA_ADDRESSES", ra_pref, ra_pref_len, ENTRY_ADDRESS));
585         CHECK(entry_to_blob("RA_ROUTES", ra_route, ra_route_len, ENTRY_ROUTE));
586         CHECK(entry_to_blob("RA_DNS", ra_dns, ra_dns_len, ENTRY_HOST));
587         CHECK(search_to_blob("RA_DOMAINS", ra_search, ra_search_len));
588 
589         blobmsg_add_u32(&b, "RA_HOPLIMIT", ra_get_hoplimit());
590         blobmsg_add_u32(&b, "RA_MTU", ra_get_mtu());
591         blobmsg_add_u32(&b, "RA_REACHABLE", ra_get_reachable());
592         blobmsg_add_u32(&b, "RA_RETRANSMIT", ra_get_retransmit());
593 
594         buf = blobmsg_alloc_string_buffer(&b, "PASSTHRU", passthru_len * 2);
595         CHECK_ALLOC(buf);
596         script_hexlify(buf, passthru, passthru_len);
597         blobmsg_add_string_buffer(&b);
598 
599         return UBUS_STATUS_OK;
600 }
601 
602 static int ubus_handle_get_stats(struct ubus_context *ctx, _unused struct ubus_object *obj,
603                 struct ubus_request_data *req, _unused const char *method,
604                 _unused struct blob_attr *msg)
605 {
606         struct dhcpv6_stats stats = dhcpv6_get_stats();
607 
608         blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
609         blobmsg_add_u64(&b, "dhcp_solicit", stats.solicit);
610         blobmsg_add_u64(&b, "dhcp_advertise", stats.advertise);
611         blobmsg_add_u64(&b, "dhcp_request", stats.request);
612         blobmsg_add_u64(&b, "dhcp_confirm", stats.confirm);
613         blobmsg_add_u64(&b, "dhcp_renew", stats.renew);
614         blobmsg_add_u64(&b, "dhcp_rebind", stats.rebind);
615         blobmsg_add_u64(&b, "dhcp_reply", stats.reply);
616         blobmsg_add_u64(&b, "dhcp_release", stats.release);
617         blobmsg_add_u64(&b, "dhcp_decline", stats.decline);
618         blobmsg_add_u64(&b, "dhcp_reconfigure", stats.reconfigure);
619         blobmsg_add_u64(&b, "dhcp_information_request", stats.information_request);
620         blobmsg_add_u64(&b, "dhcp_discarded_packets", stats.discarded_packets);
621         blobmsg_add_u64(&b, "dhcp_transmit_failures", stats.transmit_failures);
622 
623         CHECK(ubus_send_reply(ctx, req, b.head));
624         blob_buf_free(&b);
625 
626         return UBUS_STATUS_OK;
627 }
628 
629 static int ubus_handle_reset_stats(_unused struct ubus_context *ctx, _unused struct ubus_object *obj,
630                 _unused struct ubus_request_data *req, _unused const char *method,
631                 _unused struct blob_attr *msg)
632 {
633         dhcpv6_reset_stats();
634 
635         return UBUS_STATUS_OK;
636 }
637 
638 static int ubus_handle_get_state(struct ubus_context *ctx, _unused struct ubus_object *obj,
639                 struct ubus_request_data *req, _unused const char *method,
640                 _unused struct blob_attr *msg)
641 {
642         CHECK(states_to_blob());
643         CHECK(ubus_send_reply(ctx, req, b.head));
644         blob_buf_free(&b);
645 
646         return UBUS_STATUS_OK;
647 }
648 
649 static int ubus_handle_reconfigure_dhcp_rtx(enum config_dhcp_msg msg, struct blob_attr* table)
650 {
651         struct blob_attr *cur = NULL;
652         uint32_t value = 0;
653         size_t rem = 0;
654 
655         if (msg >= CONFIG_DHCP_MAX || blobmsg_data_len(table) == 0)
656                 return UBUS_STATUS_INVALID_ARGUMENT;
657 
658         blobmsg_for_each_attr(cur, table, rem) {
659                 if (!blobmsg_check_attr(cur, true) || blobmsg_type(cur) != BLOBMSG_TYPE_INT32)
660                         return UBUS_STATUS_INVALID_ARGUMENT;
661 
662                 const char* name = blobmsg_name(cur);
663                 if (strcmp("delay_max", name) == 0) {
664                         value = blobmsg_get_u32(cur);
665                         if (!config_set_rtx_delay_max(msg, value))
666                                 return UBUS_STATUS_INVALID_ARGUMENT;
667                 } else if (strcmp("timeout_init", name) == 0 ) {
668                         value = blobmsg_get_u32(cur);
669                         if (!config_set_rtx_timeout_init(msg, value))
670                                 return UBUS_STATUS_INVALID_ARGUMENT;
671                 } else if (strcmp("timeout_max", name) == 0 ) {
672                         value = blobmsg_get_u32(cur);
673                         if (!config_set_rtx_timeout_max(msg, value))
674                                 return UBUS_STATUS_INVALID_ARGUMENT;
675                 } else if (strcmp("rc_max", name) == 0) {
676                         value = blobmsg_get_u32(cur);
677                         if (!config_set_rtx_rc_max(msg, value))
678                                 return UBUS_STATUS_INVALID_ARGUMENT;
679                 } else {
680                         return UBUS_STATUS_INVALID_ARGUMENT;
681                 }
682     }
683 
684         return UBUS_STATUS_OK;
685 }
686 
687 static int ubus_handle_reconfigure_dhcp(_unused struct ubus_context *ctx, _unused struct ubus_object *obj,
688                 _unused struct ubus_request_data *req, _unused const char *method,
689                 struct blob_attr *msg)
690 {
691         const struct blobmsg_policy *policy = reconfigure_dhcp_policy;
692         struct blob_attr *tb[RECONFIGURE_DHCP_ATTR_MAX];
693         struct blob_attr *cur = NULL;
694         struct blob_attr *elem = NULL;
695         char *string = NULL;
696         uint32_t value = 0;
697         uint32_t index = 0;
698         bool enabled = false;
699         bool valid_args = false;
700         bool need_reinit = false;
701 
702         if (blobmsg_parse(policy, RECONFIGURE_DHCP_ATTR_MAX, tb, blob_data(msg), blob_len(msg)))
703                 return UBUS_STATUS_INVALID_ARGUMENT;
704 
705         if ((cur = tb[RECONFIGURE_DHCP_ATTR_DSCP])) {
706                 value = blobmsg_get_u32(cur);
707                 if (!config_set_dscp(value))
708                         return UBUS_STATUS_INVALID_ARGUMENT;
709                 need_reinit = true;
710                 valid_args = true;
711         }
712 
713         if ((cur = tb[RECONFIGURE_DHCP_ATTR_RELEASE])) {
714                 enabled = blobmsg_get_bool(cur);
715                 config_set_release(enabled);
716                 valid_args = true;
717         }
718 
719         if ((cur = tb[RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT])) {
720                 value = blobmsg_get_u32(cur);
721                 if (!config_set_rtx_timeout_max(CONFIG_DHCP_SOLICIT, value))
722                         return UBUS_STATUS_INVALID_ARGUMENT;
723                 need_reinit = true;
724                 valid_args = true;
725         }
726 
727         if ((cur = tb[RECONFIGURE_DHCP_ATTR_SK_PRIORITY])) {
728                 value = blobmsg_get_u32(cur);
729                 if (!config_set_sk_priority(value))
730                         return UBUS_STATUS_INVALID_ARGUMENT;
731                 need_reinit = true;
732                 valid_args = true;
733         }
734 
735         if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_REQUESTED])) {
736                 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false))
737                         return UBUS_STATUS_INVALID_ARGUMENT;
738 
739                 config_clear_requested_options();
740 
741                 blobmsg_for_each_attr(elem, cur, index) {
742                         if (blobmsg_type(elem) != BLOBMSG_TYPE_INT32)
743                                 return UBUS_STATUS_INVALID_ARGUMENT;
744 
745                         value = blobmsg_get_u32(elem);
746                         if (!config_add_requested_options(value))
747                                 return UBUS_STATUS_INVALID_ARGUMENT;
748                 }
749 
750                 need_reinit = true;
751                 valid_args = true;
752         }
753 
754         if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_STRICT])) {
755                 enabled = blobmsg_get_bool(cur);
756                 config_set_client_options(DHCPV6_STRICT_OPTIONS, enabled);
757                 need_reinit = true;
758                 valid_args = true;
759         }
760 
761         if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE])) {
762                 enabled = blobmsg_get_bool(cur);
763                 config_set_client_options(DHCPV6_ACCEPT_RECONFIGURE, enabled);
764                 need_reinit = true;
765                 valid_args = true;
766         }
767 
768         if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_FQDN])) {
769                 enabled = blobmsg_get_bool(cur);
770                 config_set_client_options(DHCPV6_CLIENT_FQDN, enabled);
771                 need_reinit = true;
772                 valid_args = true;
773         }
774 
775         if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_UNICAST])) {
776                 enabled = blobmsg_get_bool(cur);
777                 config_set_client_options(DHCPV6_IGNORE_OPT_UNICAST, enabled);
778                 need_reinit = true;
779                 valid_args = true;
780         }
781 
782         if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_SEND])) {
783                 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false))
784                         return UBUS_STATUS_INVALID_ARGUMENT;
785 
786                 config_clear_send_options();
787 
788                 blobmsg_for_each_attr(elem, cur, index) {
789                         string = blobmsg_get_string(elem);
790                         if (string == NULL || !config_add_send_options(string))
791                                 return UBUS_STATUS_INVALID_ARGUMENT;
792                 }
793 
794                 need_reinit = true;
795                 valid_args = true;
796         }
797 
798         if ((cur = tb[RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES])) {
799                 string = blobmsg_get_string(cur);
800                 if (string == NULL || !config_set_request_addresses(string))
801                         return UBUS_STATUS_INVALID_ARGUMENT;
802 
803                 need_reinit = true;
804                 valid_args = true;
805         }
806 
807         if ((cur = tb[RECONFIGURE_DHCP_ATTR_REQ_PREFIXES])) {
808                 value = blobmsg_get_u32(cur);
809 
810                 if (!config_set_request_prefix(value, 1))
811                         return UBUS_STATUS_INVALID_ARGUMENT;
812 
813                 need_reinit = true;
814                 valid_args = true;
815         }
816 
817         if ((cur = tb[RECONFIGURE_DHCP_ATTR_STATEFUL])) {
818                 enabled = blobmsg_get_bool(cur);
819                 config_set_stateful_only(enabled);
820                 need_reinit = true;
821                 valid_args = true;
822         }
823 
824         if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_SOLICIT])) {
825                 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_SOLICIT, cur))
826                         return UBUS_STATUS_INVALID_ARGUMENT;
827 
828                 need_reinit = true;
829                 valid_args = true;
830         }
831 
832         if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_REQUEST])) {
833                 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_REQUEST, cur))
834                         return UBUS_STATUS_INVALID_ARGUMENT;
835 
836                 need_reinit = true;
837                 valid_args = true;
838         }
839 
840         if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_RENEW])) {
841                 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_RENEW, cur))
842                         return UBUS_STATUS_INVALID_ARGUMENT;
843 
844                 need_reinit = true;
845                 valid_args = true;
846         }
847 
848         if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_REBIND])) {
849                 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_REBIND, cur))
850                         return UBUS_STATUS_INVALID_ARGUMENT;
851 
852                 need_reinit = true;
853                 valid_args = true;
854         }
855 
856         if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_RELEASE])) {
857                 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_RELEASE, cur))
858                         return UBUS_STATUS_INVALID_ARGUMENT;
859 
860                 need_reinit = true;
861                 valid_args = true;
862         }
863 
864         if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_DECLINE])) {
865                 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_DECLINE, cur))
866                         return UBUS_STATUS_INVALID_ARGUMENT;
867 
868                 need_reinit = true;
869                 valid_args = true;
870         }
871 
872         if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_INFO_REQ])) {
873                 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_INFO_REQ, cur))
874                         return UBUS_STATUS_INVALID_ARGUMENT;
875 
876                 need_reinit = true;
877                 valid_args = true;
878         }
879 
880         if ((cur = tb[RECONFIGURE_DHCP_ATTR_IRT_MIN])) {
881                 value = blobmsg_get_u32(cur);
882 
883                 if (!config_set_irt_min(value))
884                         return UBUS_STATUS_INVALID_ARGUMENT;
885 
886                 need_reinit = true;
887                 valid_args = true;
888         }
889 
890         if ((cur = tb[RECONFIGURE_DHCP_ATTR_IRT_DEFAULT])) {
891                 value = blobmsg_get_u32(cur);
892 
893                 if (!config_set_irt_default(value))
894                         return UBUS_STATUS_INVALID_ARGUMENT;
895 
896                 need_reinit = true;
897                 valid_args = true;
898         }
899 
900         if ((cur = tb[RECONFIGURE_DHCP_ATTR_RAND_FACTOR])) {
901                 value = blobmsg_get_u32(cur);
902 
903                 if (!config_set_rand_factor(value))
904                         return UBUS_STATUS_INVALID_ARGUMENT;
905 
906                 need_reinit = true;
907                 valid_args = true;
908         }
909 
910         if ((cur = tb[RECONFIGURE_DHCP_ATTR_AUTH_PROTO])) {
911                 string = blobmsg_get_string(cur);
912 
913                 if (string == NULL || !config_set_auth_protocol(string))
914                         return UBUS_STATUS_INVALID_ARGUMENT;
915 
916                 need_reinit = true;
917                 valid_args = true;
918         }
919 
920         if ((cur = tb[RECONFIGURE_DHCP_ATTR_AUTH_TOKEN])) {
921                 string = blobmsg_get_string(cur);
922 
923                 if (string == NULL || !config_set_auth_token(string))
924                         return UBUS_STATUS_INVALID_ARGUMENT;
925 
926                 need_reinit = true;
927                 valid_args = true;
928         }
929 
930         if (need_reinit)
931                 raise(SIGUSR2);
932 
933         return valid_args ? UBUS_STATUS_OK : UBUS_STATUS_INVALID_ARGUMENT;
934 }
935 
936 static int ubus_handle_renew(_unused struct ubus_context *ctx, _unused struct ubus_object *obj,
937                 _unused struct ubus_request_data *req, _unused const char *method,
938                 _unused struct blob_attr *msg)
939 {
940         raise(SIGUSR1);
941         return UBUS_STATUS_OK;
942 }
943 
944 static int ubus_handle_release(_unused struct ubus_context *ctx, _unused struct ubus_object *obj,
945                 _unused struct ubus_request_data *req, _unused const char *method,
946                 _unused struct blob_attr *msg)
947 {
948         raise(SIGUSR2);
949         return UBUS_STATUS_OK;
950 }
951 
952 int ubus_dhcp_event(const char *status)
953 {
954         if (!ubus || !odhcp6c_object.has_subscribers)
955                 return UBUS_STATUS_UNKNOWN_ERROR;
956 
957         CHECK(states_to_blob());
958         CHECK(ubus_notify(ubus, &odhcp6c_object, status, b.head, -1));
959         blob_buf_free(&b);
960 
961         return UBUS_STATUS_OK;
962 }
963 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt