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

Sources/odhcp6c/src/odhcp6c.c

  1 /**
  2  * Copyright (C) 2012-2014 Steven Barth <steven@midlink.org>
  3  * Copyright (C) 2017-2018 Hans Dedecker <dedeckeh@gmail.com>
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU General Public License v2 as published by
  7  * the Free Software Foundation.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  * GNU General Public License for more details.
 13  *
 14  */
 15 
 16 #include <arpa/inet.h>
 17 #include <ctype.h>
 18 #include <errno.h>
 19 #include <fcntl.h>
 20 #include <limits.h>
 21 #include <linux/if_addr.h>
 22 #include <net/if.h>
 23 #include <netinet/icmp6.h>
 24 #include <poll.h>
 25 #include <resolv.h>
 26 #include <signal.h>
 27 #include <stdbool.h>
 28 #include <stddef.h>
 29 #include <stdlib.h>
 30 #include <stdio.h>
 31 #include <string.h>
 32 #include <strings.h>
 33 #include <syslog.h>
 34 #include <sys/syscall.h>
 35 #include <time.h>
 36 #include <unistd.h>
 37 
 38 #include "config.h"
 39 #include "odhcp6c.h"
 40 #include "ra.h"
 41 #include "ubus.h"
 42 
 43 #define DHCPV6_FD_INDEX 0
 44 #define UBUS_FD_INDEX 1
 45 
 46 #ifndef IN6_IS_ADDR_UNIQUELOCAL
 47 #define IN6_IS_ADDR_UNIQUELOCAL(a) \
 48         ((((__const uint32_t *) (a))[0] & htonl (0xfe000000)) \
 49          == htonl (0xfc000000))
 50 #endif
 51 
 52 static void sighandler(int signal);
 53 static int usage(void);
 54 
 55 static uint8_t *state_data[_STATE_MAX] = {NULL};
 56 static size_t state_len[_STATE_MAX] = {0};
 57 
 58 static volatile bool signal_io = false;
 59 static volatile bool signal_usr1 = false;
 60 static volatile bool signal_usr2 = false;
 61 static volatile bool signal_term = false;
 62 
 63 static int urandom_fd = -1;
 64 static bool bound = false, ra = false;
 65 static time_t last_update = 0;
 66 static char *ifname = NULL;
 67 struct config_dhcp *config_dhcp = NULL;
 68 
 69 static unsigned int script_sync_delay = 10;
 70 static unsigned int script_accu_delay = 1;
 71 
 72 static struct odhcp6c_opt opts[] = {
 73         { .code = DHCPV6_OPT_CLIENTID, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 74         { .code = DHCPV6_OPT_SERVERID, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 75         { .code = DHCPV6_OPT_IA_NA, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str= NULL },
 76         { .code = DHCPV6_OPT_IA_TA, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 77         { .code = DHCPV6_OPT_IA_ADDR, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 78         { .code = DHCPV6_OPT_ORO, .flags = OPT_INTERNAL, .str = NULL },
 79         { .code = DHCPV6_OPT_PREF, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 80         { .code = DHCPV6_OPT_ELAPSED, .flags = OPT_INTERNAL, .str = NULL },
 81         { .code = DHCPV6_OPT_RELAY_MSG, .flags = OPT_INTERNAL, .str = NULL },
 82         { .code = DHCPV6_OPT_AUTH, .flags = OPT_U8 | OPT_NO_PASSTHRU, .str = "authentication" },
 83         { .code = DHCPV6_OPT_UNICAST, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 84         { .code = DHCPV6_OPT_STATUS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 85         { .code = DHCPV6_OPT_RAPID_COMMIT, .flags = OPT_INTERNAL, .str = NULL },
 86         { .code = DHCPV6_OPT_USER_CLASS, .flags = OPT_USER_CLASS | OPT_ARRAY, .str = "userclass" },
 87         { .code = DHCPV6_OPT_VENDOR_CLASS, .flags = OPT_U8, .str = "vendorclass" },
 88         { .code = DHCPV6_OPT_INTERFACE_ID, .flags = OPT_INTERNAL, .str = NULL },
 89         { .code = DHCPV6_OPT_RECONF_MESSAGE, .flags = OPT_INTERNAL, .str = NULL },
 90         { .code = DHCPV6_OPT_RECONF_ACCEPT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 91         { .code = DHCPV6_OPT_SIP_SERVER_D, .flags = OPT_DNS_STR | OPT_ORO, .str = "sipserver_d" },
 92         { .code = DHCPV6_OPT_SIP_SERVER_A, .flags = OPT_IP6 | OPT_ARRAY | OPT_ORO, .str = "sipserver_a" },
 93         { .code = DHCPV6_OPT_DNS_SERVERS, .flags = OPT_IP6 | OPT_ARRAY | OPT_ORO, .str = "dns" },
 94         { .code = DHCPV6_OPT_DNS_DOMAIN, .flags = OPT_DNS_STR | OPT_ORO, .str = "search" },
 95         { .code = DHCPV6_OPT_IA_PD, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 96         { .code = DHCPV6_OPT_IA_PREFIX, .flags = OPT_INTERNAL, .str = NULL },
 97         { .code = DHCPV6_OPT_SNTP_SERVERS, .flags = OPT_IP6 | OPT_ARRAY | OPT_ORO, .str = "sntpservers" },
 98         { .code = DHCPV6_OPT_INFO_REFRESH, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO | OPT_ORO_STATELESS, .str = NULL },
 99         { .code = DHCPV6_OPT_REMOTE_ID, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
100         { .code = DHCPV6_OPT_SUBSCRIBER_ID, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
101         { .code = DHCPV6_OPT_FQDN, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
102         { .code = DHCPV6_OPT_ERO, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
103         { .code = DHCPV6_OPT_LQ_QUERY, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
104         { .code = DHCPV6_OPT_CLIENT_DATA, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
105         { .code = DHCPV6_OPT_CLT_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
106         { .code = DHCPV6_OPT_LQ_RELAY_DATA, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
107         { .code = DHCPV6_OPT_LQ_CLIENT_LINK, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
108         { .code = DHCPV6_OPT_RELAY_ID, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
109         { .code = DHCPV6_OPT_NTP_SERVER, .flags = OPT_U8 | OPT_ORO, .str = "ntpserver" },
110         { .code = DHCPV6_OPT_CLIENT_ARCH_TYPE, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
111         { .code = DHCPV6_OPT_AFTR_NAME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
112         { .code = DHCPV6_OPT_RSOO, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
113         { .code = DHCPV6_OPT_PD_EXCLUDE, .flags = OPT_INTERNAL | OPT_ORO | OPT_ORO_STATEFUL, .str = NULL },
114         { .code = DHCPV6_OPT_VSS, .flags = OPT_U8, .str = "vss" },
115         { .code = DHCPV6_OPT_LINK_LAYER_ADDRESS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
116         { .code = DHCPV6_OPT_LINK_ADDRESS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
117         { .code = DHCPV6_OPT_RADIUS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
118         { .code = DHCPV6_OPT_SOL_MAX_RT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO | OPT_ORO_SOLICIT, .str = NULL },
119         { .code = DHCPV6_OPT_INF_MAX_RT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO | OPT_ORO_STATELESS, .str = NULL },
120         { .code = DHCPV6_OPT_DHCPV4_MSG, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
121         { .code = DHCPV6_OPT_S46_RULE, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
122         { .code = DHCPV6_OPT_S46_BR, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
123         { .code = DHCPV6_OPT_S46_DMR, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
124         { .code = DHCPV6_OPT_S46_V4V6BIND, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
125         { .code = DHCPV6_OPT_S46_PORTPARAMS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
126         { .code = DHCPV6_OPT_S46_CONT_MAPE, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
127         { .code = DHCPV6_OPT_S46_CONT_MAPT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
128         { .code = DHCPV6_OPT_S46_CONT_LW, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
129         { .code = DHCPV6_OPT_LQ_BASE_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
130         { .code = DHCPV6_OPT_LQ_START_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
131         { .code = DHCPV6_OPT_LQ_END_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
132         { .code = DHCPV6_OPT_ANI_ATT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
133         { .code = DHCPV6_OPT_ANI_NETWORK_NAME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
134         { .code = DHCPV6_OPT_ANI_AP_NAME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
135         { .code = DHCPV6_OPT_ANI_AP_BSSID, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
136         { .code = DHCPV6_OPT_ANI_OPERATOR_ID, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
137         { .code = DHCPV6_OPT_ANI_OPERATOR_REALM, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
138         { .code = DHCPV6_OPT_MUD_URL_V6, .flags = OPT_STR | OPT_NO_PASSTHRU, .str = "mud_url_v6" },
139         { .code = DHCPV6_OPT_F_BINDING_STATUS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
140         { .code = DHCPV6_OPT_F_CONNECT_FLAGS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
141         { .code = DHCPV6_OPT_F_DNS_REMOVAL_INFO, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
142         { .code = DHCPV6_OPT_F_DNS_HOST_NAME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
143         { .code = DHCPV6_OPT_F_DNS_ZONE_NAME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
144         { .code = DHCPV6_OPT_F_DNS_FLAGS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
145         { .code = DHCPV6_OPT_F_EXPIRATION_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
146         { .code = DHCPV6_OPT_F_MAX_UNACKED_BNDUPD, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
147         { .code = DHCPV6_OPT_F_MCLT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
148         { .code = DHCPV6_OPT_F_PARTNER_LIFETIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
149         { .code = DHCPV6_OPT_F_PARTNER_LIFETIME_SENT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
150         { .code = DHCPV6_OPT_F_PARTNER_DOWN_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
151         { .code = DHCPV6_OPT_F_PARTNER_RAW_CLT_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
152         { .code = DHCPV6_OPT_F_PROTOCOL_VERSION, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
153         { .code = DHCPV6_OPT_F_KEEPALIVE_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
154         { .code = DHCPV6_OPT_F_RECONFIGURE_DATA, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
155         { .code = DHCPV6_OPT_F_RELATIONSHIP_NAME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
156         { .code = DHCPV6_OPT_F_SERVER_FLAGS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
157         { .code = DHCPV6_OPT_F_SERVER_STATE, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
158         { .code = DHCPV6_OPT_F_START_TIME_OF_STATE, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
159         { .code = DHCPV6_OPT_F_STATE_EXPIRATION_TIME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
160         { .code = DHCPV6_OPT_RELAY_PORT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
161         { .code = 0, .flags = 0, .str = NULL },
162 };
163 
164 int main(_o_unused int argc, char* const argv[])
165 {
166         static struct in6_addr ifid = IN6ADDR_ANY_INIT;
167         // Allocate resources
168         const char *pidfile = NULL;
169         const char *script = "/lib/netifd/dhcpv6.script";
170         ssize_t l;
171         uint8_t buf[134], *o_data;
172         char *optpos;
173         uint16_t opttype;
174         struct odhcp6c_opt *opt;
175         int ia_pd_iaid_index = 0;
176         int verbosity = 0;
177         bool help = false, daemonize = false;
178         int logopt = LOG_PID;
179         int c;
180         int res = -1;
181         unsigned int ra_options = RA_RDNSS_DEFAULT_LIFETIME;
182         unsigned int ra_holdoff_interval = RA_MIN_ADV_INTERVAL;
183         bool terminate = false;
184         config_dhcp = config_dhcp_get();
185         config_dhcp_reset();
186 
187         while ((c = getopt(argc, argv, "S::DN:V:P:FB:c:i:r:Ru:Ux:s:EkK:t:C:m:Lhedp:fav")) != -1) {
188                 switch (c) {
189                 case 'S':
190                         config_set_allow_slaac_only((optarg) ? atoi(optarg) : -1);
191                         break;
192 
193                 case 'D':
194                         config_set_stateful_only(true);
195                         break;
196 
197                 case 'N':
198                         if (!config_set_request_addresses(optarg))
199                                 help = true;
200                         break;
201 
202                 case 'V':
203                         opt = odhcp6c_find_opt(DHCPV6_OPT_VENDOR_CLASS);
204                         if (!opt) {
205                                 syslog(LOG_ERR, "Failed to set vendor-class option");
206                                 return 1;
207                         }
208 
209                         o_data = NULL;
210                         res = config_parse_opt_data(optarg, &o_data, opt->flags & OPT_MASK_SIZE,
211                                                 (opt->flags & OPT_ARRAY) == OPT_ARRAY);
212                         if (res > 0) {
213                                 res = config_add_opt(opt->code, o_data, res);
214                                 if (res) {
215                                         if (res > 0)
216                                                 return 1;
217 
218                                         help = true;
219                                 }
220                         } else {
221                                 help = true;
222                         }
223 
224                         free(o_data);
225                         break;
226 
227                 case 'P':
228                         if (config_dhcp->ia_pd_mode == IA_MODE_NONE)
229                                 config_dhcp->ia_pd_mode = IA_MODE_TRY;
230 
231                         if (config_dhcp->allow_slaac_only >= 0 && config_dhcp->allow_slaac_only < 10)
232                                 config_dhcp->allow_slaac_only = 10;
233 
234                         struct odhcp6c_request_prefix prefix = { 0 };
235 
236                         optpos = strchr(optarg, '/');
237                         if (optpos) {
238                                 strncpy((char *)buf, optarg, optpos - optarg);
239                                 buf[optpos - optarg] = '\0';
240                                 if (inet_pton(AF_INET6, (char *)buf, &prefix.addr) <= 0) {
241                                         syslog(LOG_ERR, "invalid argument: '%s'", optarg);
242                                         return 1;
243                                 }
244                                 optpos++;
245                         } else {
246                                 optpos = optarg;
247                         }
248 
249                         char *iaid_begin;
250                         int iaid_len = 0;
251                         prefix.length = strtoul(optpos, &iaid_begin, 10);
252 
253                         if (*iaid_begin != '\0' && *iaid_begin != ',' && *iaid_begin != ':') {
254                                 syslog(LOG_ERR, "invalid argument: '%s'", optarg);
255                                 return 1;
256                         }
257 
258                         if (*iaid_begin == ',' && (iaid_len = strlen(iaid_begin)) > 1)
259                                 memcpy(&prefix.iaid, iaid_begin + 1, iaid_len > 4 ? 4 : iaid_len);
260                         else if (*iaid_begin == ':')
261                                 prefix.iaid = htonl((uint32_t)strtoul(&iaid_begin[1], NULL, 16));
262                         else
263                                 prefix.iaid = htonl(++ia_pd_iaid_index);
264 
265                         if (odhcp6c_add_state(STATE_IA_PD_INIT, &prefix, sizeof(prefix))) {
266                                 syslog(LOG_ERR, "Failed to set request IPv6-Prefix");
267                                 return 1;
268                         }
269                         break;
270 
271                 case 'F':
272                         config_set_force_prefix(true);
273                         break;
274 
275                 case 'c':
276                         l = script_unhexlify(&buf[4], sizeof(buf) - DHCPV6_OPT_HDR_SIZE, optarg);
277                         if (l > 0) {
278                                 buf[0] = 0;
279                                 buf[1] = DHCPV6_OPT_CLIENTID;
280                                 buf[2] = 0;
281                                 buf[3] = l;
282                                 if (odhcp6c_add_state(STATE_CLIENT_ID, buf, l + 4)) {
283                                         syslog(LOG_ERR, "Failed to override client-ID");
284                                         return 1;
285                                 }
286                         } else {
287                                 help = true;
288                         }
289                         break;
290 
291                 case 'i':
292                         if (inet_pton(AF_INET6, optarg, &ifid) != 1)
293                                 help = true;
294                         break;
295 
296                 case 'r':
297                         optpos = optarg;
298                         while (optpos[0]) {
299                                 opttype = htons(strtoul(optarg, &optpos, 10));
300                                 if (optpos == optarg)
301                                         break;
302                                 else if (optpos[0])
303                                         optarg = &optpos[1];
304 
305                                 if (odhcp6c_add_state(STATE_ORO, &opttype, 2)) {
306                                         syslog(LOG_ERR, "Failed to add requested option");
307                                         return 1;
308                                 }
309                         }
310                         break;
311 
312                 case 'R':
313                         config_set_client_options(DHCPV6_STRICT_OPTIONS, true);
314                         break;
315 
316                 case 'u':
317                         opt = odhcp6c_find_opt(DHCPV6_OPT_USER_CLASS);
318                         if (!opt) {
319                                 syslog(LOG_ERR, "Failed to set user-class option");
320                                 return 1;
321                         }
322 
323                         o_data = NULL;
324                         res = config_parse_opt_data(optarg, &o_data, opt->flags & OPT_MASK_SIZE,
325                                                 (opt->flags & OPT_ARRAY) == OPT_ARRAY);
326                         if (res > 0) {
327                                 res = config_add_opt(opt->code, o_data, res);
328                                 if (res) {
329                                         if (res > 0)
330                                                 return 1;
331 
332                                         help = true;
333                                 }
334                         } else {
335                                 help = true;
336                         }
337 
338                         free(o_data);
339                         break;
340 
341                 case 'U':
342                         config_set_client_options(DHCPV6_IGNORE_OPT_UNICAST, true);
343                         break;
344 
345                 case 's':
346                         if (script)
347                                 script = optarg;
348                         break;
349 
350                 case 'E':
351 #ifndef WITH_UBUS
352                         syslog(LOG_ERR, "Failed to use ubus event: ENABLE_UBUS compilation flag missing");
353                         return 1;
354 #endif /* WITH_UBUS */
355                         script = NULL;
356                         break;
357 
358                 case 'k':
359                         config_set_release(false);
360                         break;
361 
362                 case 'K':
363                         config_set_sk_priority(atoi(optarg));
364                         break;
365 
366                 case 't':
367                         config_set_rtx_timeout_max(CONFIG_DHCP_SOLICIT, atoi(optarg));
368                         break;
369 
370                 case 'C':
371                         config_set_dscp(atoi(optarg));
372                         break;
373 
374                 case 'm':
375                         ra_holdoff_interval = atoi(optarg);
376                         break;
377 
378                 case 'L':
379                         ra_options &= ~RA_RDNSS_DEFAULT_LIFETIME;
380                         break;
381 
382                 case 'e':
383                         logopt |= LOG_PERROR;
384                         break;
385 
386                 case 'd':
387                         daemonize = true;
388                         break;
389 
390                 case 'p':
391                         pidfile = optarg;
392                         break;
393 
394                 case 'f':
395                         config_set_client_options(DHCPV6_CLIENT_FQDN, false);
396                         break;
397 
398                 case 'a':
399                         config_set_client_options(DHCPV6_ACCEPT_RECONFIGURE, false);
400                         break;
401 
402                 case 'v':
403                         ++verbosity;
404                         break;
405 
406                 case 'x':
407                         res = config_parse_opt(optarg);
408                         if (res) {
409                                 if (res > 0)
410                                         return res;
411 
412                                 help = true;
413                         }
414                         break;
415 
416                 default:
417                         help = true;
418                         break;
419                 }
420         }
421 
422         if (config_dhcp->allow_slaac_only > 0)
423                 script_sync_delay = config_dhcp->allow_slaac_only;
424 
425         openlog("odhcp6c", logopt, LOG_DAEMON);
426         if (!verbosity)
427                 setlogmask(LOG_UPTO(LOG_WARNING));
428 
429         ifname = argv[optind];
430 
431         if (help || !ifname)
432                 return usage();
433 
434         signal(SIGIO, sighandler);
435         signal(SIGHUP, sighandler);
436         signal(SIGINT, sighandler);
437         signal(SIGTERM, sighandler);
438         signal(SIGUSR1, sighandler);
439         signal(SIGUSR2, sighandler);
440 
441         if (daemonize) {
442                 openlog("odhcp6c", LOG_PID, LOG_DAEMON); // Disable LOG_PERROR
443                 if (daemon(0, 0)) {
444                         syslog(LOG_ERR, "Failed to daemonize: %s",
445                                         strerror(errno));
446                         return 3;
447                 }
448 
449                 if (!pidfile) {
450                         snprintf((char*)buf, sizeof(buf), "/var/run/odhcp6c.%s.pid", ifname);
451                         pidfile = (char*)buf;
452                 }
453 
454                 FILE *fp = fopen(pidfile, "w");
455                 if (fp) {
456                         fprintf(fp, "%i\n", getpid());
457                         fclose(fp);
458                 }
459         }
460 
461         if ((urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY)) < 0 ||
462                         ra_init(ifname, &ifid, ra_options, ra_holdoff_interval) ||
463                         script_init(script, ifname)) {
464                 syslog(LOG_ERR, "failed to initialize: %s", strerror(errno));
465                 return 4;
466         }
467 
468         struct pollfd fds[2] = {0};
469         int nfds = 0;
470 
471         int mode = DHCPV6_UNKNOWN;
472         enum dhcpv6_msg req_msg_type = DHCPV6_MSG_UNKNOWN;
473 
474         fds[DHCPV6_FD_INDEX].fd = -1;
475         fds[DHCPV6_FD_INDEX].events = POLLIN;
476         nfds++;
477 
478 #ifdef WITH_UBUS
479         char *err = ubus_init(ifname);
480         if (err) {
481                 syslog(LOG_ERR, "ubus error: %s", err);
482                 return 1;
483         }
484 
485         struct ubus_context *ubus = ubus_get_ctx();
486         int ubus_socket = ubus->sock.fd;
487         if (ubus_socket < 0) {
488                 syslog(LOG_ERR, "Invalid ubus file descriptor");
489                 return 1;
490         }
491         fds[UBUS_FD_INDEX].fd = ubus_socket;
492         fds[UBUS_FD_INDEX].events = POLLIN;
493         nfds++;
494 #endif /* WITH_UBUS */
495 
496         notify_state_change("started", 0, false);
497 
498         while (!terminate) { // Main logic
499                 int poll_res;
500                 bool signalled = odhcp6c_signal_process();
501 
502                 switch (dhcpv6_get_state()) {
503                 case DHCPV6_INIT:
504                         odhcp6c_clear_state(STATE_SERVER_ID);
505                         odhcp6c_clear_state(STATE_SERVER_ADDR);
506                         odhcp6c_clear_state(STATE_IA_NA);
507                         odhcp6c_clear_state(STATE_IA_PD);
508                         odhcp6c_clear_state(STATE_SNTP_IP);
509                         odhcp6c_clear_state(STATE_NTP_IP);
510                         odhcp6c_clear_state(STATE_NTP_FQDN);
511                         odhcp6c_clear_state(STATE_SIP_IP);
512                         odhcp6c_clear_state(STATE_SIP_FQDN);
513                         odhcp6c_clear_state(STATE_CAPT_PORT_DHCPV6);
514                         bound = false;
515 
516                         size_t oro_len = 0;
517                         odhcp6c_get_state(STATE_ORO, &oro_len);
518                         config_dhcp->oro_user_cnt = oro_len / sizeof(uint16_t);
519 
520                         if (init_dhcpv6(ifname)) {
521                                 syslog(LOG_ERR, "failed to initialize: %s", strerror(errno));
522                                 return 1;
523                         }
524 
525                         fds[DHCPV6_FD_INDEX].fd = dhcpv6_get_socket();
526 
527                         syslog(LOG_NOTICE, "(re)starting transaction on %s", ifname);
528 
529                         signal_usr1 = signal_usr2 = false;
530 
531                         dhcpv6_set_state(DHCPV6_SOLICIT);
532                         break;
533 
534                 case DHCPV6_SOLICIT:
535                         mode = dhcpv6_get_ia_mode();
536                         if (mode == DHCPV6_STATELESS) {
537                                 dhcpv6_set_state(DHCPV6_REQUEST);
538                                 break;
539                         }
540 
541                         req_msg_type = DHCPV6_MSG_SOLICIT;
542                         dhcpv6_send_request(req_msg_type);
543                         break;
544 
545                 case DHCPV6_ADVERT:
546                         if (res > 0) {
547                                 mode = DHCPV6_STATEFUL;
548                                 dhcpv6_set_state(DHCPV6_REQUEST);
549                         } else {
550                                 mode = DHCPV6_UNKNOWN;
551                                 dhcpv6_set_state(DHCPV6_RESET);
552                         }
553                         break;
554 
555                 case DHCPV6_REQUEST:
556                         req_msg_type = (mode == DHCPV6_STATELESS) ? DHCPV6_MSG_INFO_REQ : DHCPV6_MSG_REQUEST;
557                         dhcpv6_send_request(req_msg_type);
558                         break;
559 
560                 case DHCPV6_REPLY:
561                         if ((res > 0) && mode != DHCPV6_UNKNOWN) {
562                                 dhcpv6_set_state(DHCPV6_BOUND);
563                                 break;
564                         }
565 
566                         if ((res < 0) && signalled) {
567                                 mode = DHCPV6_UNKNOWN;
568                                 dhcpv6_set_state(DHCPV6_RESET);
569                                 break;
570                         }
571 
572                         mode = dhcpv6_promote_server_cand();
573                         dhcpv6_set_state(mode > DHCPV6_UNKNOWN ? DHCPV6_REQUEST : DHCPV6_RESET);
574                         break;
575 
576                 case DHCPV6_BOUND:
577                         if (!bound) {
578                                 bound = true;
579                                 if (mode == DHCPV6_STATELESS) {
580                                         syslog(LOG_NOTICE, "entering stateless-mode on %s", ifname);
581                                         signal_usr1 = false;
582                                         notify_state_change("informed", script_sync_delay, true);
583                                 } else {
584                                         notify_state_change("bound", script_sync_delay, true);
585                                         syslog(LOG_NOTICE, "entering stateful-mode on %s", ifname);
586                                 }
587                         }
588 
589                         req_msg_type = DHCPV6_MSG_UNKNOWN;
590                         dhcpv6_send_request(req_msg_type);
591                         break;
592 
593                 case DHCPV6_BOUND_REPLY:
594                         if (res == DHCPV6_MSG_RENEW || res == DHCPV6_MSG_REBIND ||
595                                 res == DHCPV6_MSG_INFO_REQ) {
596                                 req_msg_type = res;
597                                 dhcpv6_set_state(DHCPV6_RECONF);
598                         } else {
599                                 dhcpv6_set_state(DHCPV6_RECONF_REPLY);
600                         }
601                         break;
602 
603                 case DHCPV6_RECONF:
604                         dhcpv6_send_request(req_msg_type);
605                         break;
606 
607                 case DHCPV6_RECONF_REPLY:
608                         if (res > 0) {
609                                 dhcpv6_set_state(DHCPV6_BOUND);
610                                 if (mode == DHCPV6_STATEFUL)
611                                         notify_state_change("updated", 0, false);
612                         } else {
613                                 dhcpv6_set_state(mode == DHCPV6_STATELESS ? DHCPV6_INFO : DHCPV6_RENEW);
614                         }
615                         break;
616 
617                 case DHCPV6_RENEW:
618                         req_msg_type = DHCPV6_MSG_RENEW;
619                         dhcpv6_send_request(req_msg_type);
620                         break;
621 
622                 case DHCPV6_RENEW_REPLY:
623                         if (res > 0 ) {
624                                 notify_state_change("updated", 0, false);
625                                 dhcpv6_set_state(DHCPV6_BOUND);
626                         } else {
627                                 dhcpv6_set_state(DHCPV6_REBIND);
628                         }
629                         break;
630 
631                 case DHCPV6_REBIND:
632                         odhcp6c_clear_state(STATE_SERVER_ID); // Remove binding
633                         odhcp6c_clear_state(STATE_SERVER_ADDR);
634 
635                         size_t ia_pd_len_r, ia_na_len_r;
636                         odhcp6c_get_state(STATE_IA_PD, &ia_pd_len_r);
637                         odhcp6c_get_state(STATE_IA_NA, &ia_na_len_r);
638 
639                         // If we have IAs, try rebind otherwise restart
640                         if (ia_pd_len_r == 0 && ia_na_len_r == 0) {
641                                 dhcpv6_set_state(DHCPV6_RESET);
642                                 break;
643                         }
644 
645                         req_msg_type = DHCPV6_MSG_REBIND;
646                         dhcpv6_send_request(req_msg_type);
647                         break;
648 
649                 case DHCPV6_REBIND_REPLY:
650                         if (res < 0) {
651                                 dhcpv6_set_state(DHCPV6_RESET);
652                         } else {
653                                 notify_state_change("rebound", 0, true);
654                                 dhcpv6_set_state(DHCPV6_BOUND);
655                         }
656                         break;
657 
658                 case DHCPV6_INFO:
659                         req_msg_type = DHCPV6_MSG_INFO_REQ;
660                         dhcpv6_send_request(req_msg_type);
661                         break;
662 
663                 case DHCPV6_INFO_REPLY:
664                         dhcpv6_set_state(res < 0 ? DHCPV6_RESET : DHCPV6_BOUND);
665                         break;
666 
667                 case DHCPV6_SOLICIT_PROCESSING:
668                 case DHCPV6_REQUEST_PROCESSING:
669                         res = dhcpv6_state_processing(req_msg_type);
670                         break;
671 
672                 case DHCPV6_BOUND_PROCESSING:
673                 case DHCPV6_RECONF_PROCESSING:
674                 case DHCPV6_REBIND_PROCESSING:
675                         res = dhcpv6_state_processing(req_msg_type);
676 
677                         if (signal_usr1)
678                                 dhcpv6_set_state(mode == DHCPV6_STATELESS ? DHCPV6_INFO : DHCPV6_RENEW);
679                         break;
680 
681                 case DHCPV6_RENEW_PROCESSING:
682                 case DHCPV6_INFO_PROCESSING:
683                         res = dhcpv6_state_processing(req_msg_type);
684 
685                         if (signal_usr1)
686                                 signal_usr1 = false;    // Acknowledged
687                         break;
688 
689                 case DHCPV6_EXIT:
690                         odhcp6c_expire(false);
691 
692                         size_t ia_pd_len, ia_na_len, server_id_len;
693                         odhcp6c_get_state(STATE_IA_PD, &ia_pd_len);
694                         odhcp6c_get_state(STATE_IA_NA, &ia_na_len);
695                         odhcp6c_get_state(STATE_SERVER_ID, &server_id_len);
696 
697                         // Add all prefixes to lost prefixes
698                         if (bound) {
699                                 bound = false;
700                                 notify_state_change("unbound", 0, true);
701                         }
702 
703                         if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0) && (!signal_term || config_dhcp->release))
704                                 dhcpv6_send_request(DHCPV6_MSG_RELEASE);
705 
706                         odhcp6c_clear_state(STATE_IA_NA);
707                         odhcp6c_clear_state(STATE_IA_PD);
708 
709                         if (signal_term) {
710                                 terminate = true;
711                         } else {
712                                 signal_usr2 = false;
713                                 dhcpv6_set_state(DHCPV6_RESET);
714                         }
715                         break;
716 
717                 case DHCPV6_RESET:
718                         odhcp6c_clear_state(STATE_CLIENT_ID);
719 
720                         if (bound) {
721                                 bound = false;
722                                 notify_state_change("unbound", 0, true);
723                         }
724 
725                         size_t oro_user_len, oro_total_len;
726                         odhcp6c_get_state(STATE_ORO, &oro_total_len);
727                         oro_user_len = config_dhcp->oro_user_cnt * sizeof(uint16_t);
728                         odhcp6c_remove_state(STATE_ORO, oro_user_len, oro_total_len - oro_user_len);
729 
730                         close(dhcpv6_get_socket());
731                         fds[DHCPV6_FD_INDEX].fd = -1;
732 
733                         dhcpv6_set_state(DHCPV6_INIT);
734                         break;
735 
736                 default:
737                         break;
738                 }
739 
740                 if (signal_usr2 || signal_term)
741                         dhcpv6_set_state(DHCPV6_EXIT);
742 
743                 poll_res = poll(fds, nfds, dhcpv6_get_state_timeout());
744                 dhcpv6_reset_state_timeout();
745                 if (poll_res == -1 && (errno == EINTR || errno == EAGAIN)) {
746                         continue;
747                 }
748 
749                 if (fds[0].revents & POLLIN)
750                         dhcpv6_receive_response(req_msg_type);
751 
752 #ifdef WITH_UBUS
753                 if (fds[1].revents & POLLIN)
754                         ubus_handle_event(ubus);
755 #endif /* WITH_UBUS */
756         }
757 
758         notify_state_change("stopped", 0, true);
759 
760 #ifdef WITH_UBUS
761         ubus_destroy(ubus);
762 #endif /* WITH_UBUS */
763 
764         return 0;
765 }
766 
767 static int usage(void)
768 {
769         const char buf[] =
770         "Usage: odhcp6c [options] <interface>\n"
771         "\nFeature options:\n"
772         "       -S <time>       Wait at least <time> sec for a DHCP-server (0)\n"
773         "       -D              Discard advertisements without any address or prefix proposed\n"
774         "       -N <mode>       Mode for requesting addresses [try|force|none]\n"
775         "       -P <[pfx/]len>  Request IPv6-Prefix (0 = auto)\n"
776         "       -F              Force IPv6-Prefix\n"
777         "       -V <class>      Set vendor-class option (base-16 encoded)\n"
778         "       -u <user-class> Set user-class option string\n"
779         "       -x <opt>:<val>  Add option opt (with value val) in sent packets (cumulative)\n"
780         "                       Examples of IPv6 address, string and base-16 encoded options:\n"
781         "                       -x dns:2001:2001::1,2001:2001::2 - option 23\n"
782         "                       -x 15:office - option 15 (userclass)\n"
783         "                       -x 0x1f4:ABBA - option 500\n"
784         "                       -x 202:'\"file\"' - option 202\n"
785         "       -c <clientid>   Override client-ID (base-16 encoded 16-bit type + value)\n"
786         "       -i <iface-id>   Use a custom interface identifier for RA handling\n"
787         "       -r <options>    Options to be requested (comma-separated)\n"
788         "       -R              Do not request any options except those specified with -r\n"
789         "       -s <script>     Status update script (/lib/netifd/dhcpv6.script)\n"
790         "       -E              Only use UBUS event and disable status update script\n"
791         "       -a              Don't send Accept Reconfigure option\n"
792         "       -f              Don't send Client FQDN option\n"
793         "       -k              Don't send a RELEASE when stopping\n"
794         "       -K <sk-prio>    Set packet kernel priority (0)\n"
795         "       -C <dscp>       Set packet DSCP value (0)\n"
796         "       -t <seconds>    Maximum timeout for DHCPv6-SOLICIT (120)\n"
797         "       -m <seconds>    Minimum time between accepting RA updates (3)\n"
798         "       -L              Ignore default lifetime for RDNSS records\n"
799         "       -U              Ignore Server Unicast option\n"
800         "\nInvocation options:\n"
801         "       -p <pidfile>    Set pidfile (/var/run/odhcp6c.pid)\n"
802         "       -d              Daemonize\n"
803         "       -e              Write logmessages to stderr\n"
804         "       -v              Increase logging verbosity\n"
805         "       -h              Show this help\n\n";
806         fputs(buf, stderr);
807 
808         return 1;
809 }
810 
811 // Don't want to pull-in librt and libpthread just for a monotonic clock...
812 uint64_t odhcp6c_get_milli_time(void)
813 {
814         struct timespec t;
815 
816         clock_gettime(CLOCK_MONOTONIC, &t);
817 
818         return ((uint64_t)t.tv_sec) * 1000 + ((uint64_t)t.tv_nsec) / 1000000;
819 }
820 
821 static uint8_t* odhcp6c_resize_state(enum odhcp6c_state state, ssize_t len)
822 {
823         if (len == 0)
824                 return state_data[state] + state_len[state];
825         else if (state_len[state] + len > 1024)
826                 return NULL;
827 
828         uint8_t *n = realloc(state_data[state], state_len[state] + len);
829 
830         if (n || state_len[state] + len == 0) {
831                 state_data[state] = n;
832                 n += state_len[state];
833                 state_len[state] += len;
834         }
835 
836         return n;
837 }
838 
839 static bool odhcp6c_server_advertised()
840 {
841         size_t len;
842         uint8_t *start = odhcp6c_get_state(STATE_RA_ROUTE, &len);
843 
844         for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start;
845                         (uint8_t*)c < &start[len] &&
846                         (uint8_t*)odhcp6c_next_entry(c) <= &start[len];
847                         c = odhcp6c_next_entry(c)) {
848                 // Only default route entries have flags
849                 if (c->length != 0 || IN6_IS_ADDR_UNSPECIFIED(&c->router))
850                         continue;
851 
852                 if (c->ra_flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))
853                         return true;
854         }
855 
856         return false;
857 }
858 
859 bool odhcp6c_signal_process(void)
860 {
861         while (signal_io) {
862                 signal_io = false;
863 
864                 size_t old_ra_prefix_size = state_len[STATE_RA_PREFIX];
865                 bool ra_updated = ra_process();
866 
867                 if (ra_link_up()) {
868                         signal_usr2 = true;
869                         ra = false;
870                 } else if (old_ra_prefix_size != state_len[STATE_RA_PREFIX] &&
871                                 odhcp6c_server_advertised()) {
872                         // Restart DHCPv6 transaction when router advertisement flags
873                         // show presence of a DHCPv6 server and new prefixes were
874                         // added to STATE_RA_PREFIX state
875                         signal_usr2 = true;
876                 }
877 
878                 if (ra_updated && (bound || config_dhcp->allow_slaac_only >= 0)) {
879                         notify_state_change("ra-updated", (!ra && !bound) ?
880                                         script_sync_delay : script_accu_delay, false);
881                         ra = true;
882                 }
883         }
884 
885         return signal_usr1 || signal_usr2 || signal_term;
886 }
887 
888 void odhcp6c_clear_state(enum odhcp6c_state state)
889 {
890         state_len[state] = 0;
891 }
892 
893 int odhcp6c_add_state(enum odhcp6c_state state, const void *data, size_t len)
894 {
895         uint8_t *n = odhcp6c_resize_state(state, len);
896 
897         if (!n)
898                 return -1;
899 
900         memcpy(n, data, len);
901 
902         return 0;
903 }
904 
905 int odhcp6c_insert_state(enum odhcp6c_state state, size_t offset, const void *data, size_t len)
906 {
907         ssize_t len_after = state_len[state] - offset;
908         if (len_after < 0)
909                 return -1;
910 
911         uint8_t *n = odhcp6c_resize_state(state, len);
912 
913         if (n) {
914                 uint8_t *sdata = state_data[state];
915 
916                 memmove(sdata + offset + len, sdata + offset, len_after);
917                 memcpy(sdata + offset, data, len);
918         }
919 
920         return 0;
921 }
922 
923 size_t odhcp6c_remove_state(enum odhcp6c_state state, size_t offset, size_t len)
924 {
925         uint8_t *data = state_data[state];
926         ssize_t len_after = state_len[state] - (offset + len);
927 
928         if (len_after < 0)
929                 return state_len[state];
930 
931         memmove(data + offset, data + offset + len, len_after);
932 
933         return state_len[state] -= len;
934 }
935 
936 void* odhcp6c_move_state(enum odhcp6c_state state, size_t *len)
937 {
938         *len = state_len[state];
939         void *data = state_data[state];
940 
941         state_len[state] = 0;
942         state_data[state] = NULL;
943 
944         return data;
945 }
946 
947 void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len)
948 {
949         *len = state_len[state];
950 
951         return state_data[state];
952 }
953 
954 static struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new)
955 {
956         size_t len, cmplen = offsetof(struct odhcp6c_entry, target) + ((new->length + 7) / 8);
957         uint8_t *start = odhcp6c_get_state(state, &len);
958 
959         for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start;
960                         (uint8_t*)c < &start[len] &&
961                         (uint8_t*)odhcp6c_next_entry(c) <= &start[len];
962                         c = odhcp6c_next_entry(c)) {
963                 if (!memcmp(c, new, cmplen) && !memcmp(c->auxtarget, new->auxtarget, new->auxlen))
964                         return c;
965         }
966 
967         return NULL;
968 }
969 
970 bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new,
971                 unsigned int holdoff_interval)
972 {
973         struct odhcp6c_entry *x = odhcp6c_find_entry(state, new);
974 
975         if (x) {
976                 if (holdoff_interval && new->valid >= x->valid &&
977                                 new->valid != UINT32_MAX &&
978                                 new->valid - x->valid < holdoff_interval &&
979                                 new->preferred >= x->preferred &&
980                                 new->preferred != UINT32_MAX &&
981                                 new->preferred - x->preferred < holdoff_interval)
982                         return false;
983 
984                 x->valid = new->valid;
985                 x->ra_flags = new->ra_flags;
986                 x->priority = new->priority;
987                 x->preferred = new->preferred;
988                 x->t1 = new->t1;
989                 x->t2 = new->t2;
990                 x->iaid = new->iaid;
991         } else if (odhcp6c_add_state(state, new, odhcp6c_entry_size(new))) {
992                 return false;
993         }
994 
995         return true;
996 }
997 
998 static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed, bool remove_expired)
999 {
1000         size_t len;
1001         uint8_t *start = odhcp6c_get_state(state, &len);
1002 
1003         for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start;
1004                         (uint8_t*)c < &start[len] &&
1005                         (uint8_t*)odhcp6c_next_entry(c) <= &start[len];
1006                         ) {
1007                 if (c->t1 < elapsed)
1008                         c->t1 = 0;
1009                 else if (c->t1 != UINT32_MAX)
1010                         c->t1 -= elapsed;
1011 
1012                 if (c->t2 < elapsed)
1013                         c->t2 = 0;
1014                 else if (c->t2 != UINT32_MAX)
1015                         c->t2 -= elapsed;
1016 
1017                 if (c->preferred < elapsed)
1018                         c->preferred = 0;
1019                 else if (c->preferred != UINT32_MAX)
1020                         c->preferred -= elapsed;
1021 
1022                 if (c->valid < elapsed)
1023                         c->valid = 0;
1024                 else if (c->valid != UINT32_MAX)
1025                         c->valid -= elapsed;
1026 
1027                 if (!c->valid && remove_expired) {
1028                         odhcp6c_remove_state(state, ((uint8_t*)c) - start, odhcp6c_entry_size(c));
1029                         start = odhcp6c_get_state(state, &len);
1030                 } else {
1031                         c = odhcp6c_next_entry(c);
1032                 }
1033         }
1034 }
1035 
1036 void odhcp6c_expire(bool expire_ia_pd)
1037 {
1038         time_t now = odhcp6c_get_milli_time() / 1000;
1039         uint32_t elapsed = (last_update > 0) ? now - last_update : 0;
1040 
1041         last_update = now;
1042 
1043         odhcp6c_expire_list(STATE_RA_PREFIX, elapsed, true);
1044         odhcp6c_expire_list(STATE_RA_ROUTE, elapsed, true);
1045         odhcp6c_expire_list(STATE_RA_DNS, elapsed, true);
1046         odhcp6c_expire_list(STATE_RA_SEARCH, elapsed, true);
1047         odhcp6c_expire_list(STATE_IA_NA, elapsed, true);
1048         odhcp6c_expire_list(STATE_IA_PD, elapsed, expire_ia_pd);
1049 }
1050 
1051 uint32_t odhcp6c_elapsed(void)
1052 {
1053         return odhcp6c_get_milli_time() / 1000 - last_update;
1054 }
1055 
1056 int odhcp6c_random(void *buf, size_t len)
1057 {
1058         return read(urandom_fd, buf, len);
1059 }
1060 
1061 bool odhcp6c_is_bound(void)
1062 {
1063         return bound;
1064 }
1065 
1066 bool odhcp6c_addr_in_scope(const struct in6_addr *addr)
1067 {
1068         FILE *fd = fopen("/proc/net/if_inet6", "r");
1069         int len;
1070         bool ret = false;
1071         char buf[256];
1072 
1073         if (fd == NULL)
1074                 return false;
1075 
1076         while (fgets(buf, sizeof(buf), fd)) {
1077                 struct in6_addr inet6_addr;
1078                 uint32_t flags, dummy;
1079                 unsigned int i;
1080                 char name[IF_NAMESIZE], addr_buf[33];
1081 
1082                 len = strlen(buf);
1083 
1084                 if ((len <= 0) || buf[len - 1] != '\n')
1085                         break;
1086 
1087                 buf[--len] = '\0';
1088 
1089                 if (sscanf(buf, "%s %x %x %x %x %s",
1090                                 addr_buf, &dummy, &dummy, &dummy, &flags, name) != 6)
1091                         break;
1092 
1093                 if (strcmp(name, ifname) ||
1094                         (flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE | IFA_F_DEPRECATED)))
1095                         continue;
1096 
1097                 for (i = 0; i < strlen(addr_buf); i++) {
1098                         if (!isxdigit(addr_buf[i]) || isupper(addr_buf[i]))
1099                                 break;
1100                 }
1101 
1102                 memset(&inet6_addr, 0, sizeof(inet6_addr));
1103                 for (i = 0; i < (strlen(addr_buf) / 2); i++) {
1104                         unsigned char byte;
1105                         static const char hex[] = "0123456789abcdef";
1106                         byte = ((index(hex, addr_buf[i * 2]) - hex) << 4) |
1107                                 (index(hex, addr_buf[i * 2 + 1]) - hex);
1108                         inet6_addr.s6_addr[i] = byte;
1109                 }
1110 
1111                 if ((IN6_IS_ADDR_LINKLOCAL(&inet6_addr) == IN6_IS_ADDR_LINKLOCAL(addr)) &&
1112                         (IN6_IS_ADDR_UNIQUELOCAL(&inet6_addr) == IN6_IS_ADDR_UNIQUELOCAL(addr))) {
1113                         ret = true;
1114                         break;
1115                 }
1116         }
1117 
1118         fclose(fd);
1119         return ret;
1120 }
1121 
1122 static void sighandler(int signal)
1123 {
1124         if (signal == SIGUSR1)
1125                 signal_usr1 = true;
1126         else if (signal == SIGUSR2)
1127                 signal_usr2 = true;
1128         else if (signal == SIGIO)
1129                 signal_io = true;
1130         else
1131                 signal_term = true;
1132 }
1133 
1134 void notify_state_change(const char *status, int delay, bool resume)
1135 {
1136         script_call(status, delay, resume);
1137 
1138 #ifdef WITH_UBUS
1139         ubus_dhcp_event(status);
1140 #endif /* WITH_UBUS */
1141 }
1142 
1143 struct odhcp6c_opt *odhcp6c_find_opt(const uint16_t code)
1144 {
1145         struct odhcp6c_opt *opt = opts;
1146 
1147         while (opt->code) {
1148                 if (opt->code == code)
1149                         return opt;
1150 
1151                 opt++;
1152         }
1153 
1154         return NULL;
1155 }
1156 
1157 struct odhcp6c_opt *odhcp6c_find_opt_by_name(const char *name)
1158 {
1159         struct odhcp6c_opt *opt = opts;
1160 
1161         if (!name || !strlen(name))
1162                 return NULL;
1163 
1164         while (opt->code && (!opt->str || strcmp(opt->str, name)))
1165                 opt++;
1166 
1167         return (opt->code > 0 ? opt : NULL);
1168 }
1169 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt