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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt