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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt