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

Sources/odhcp6c/src/odhcp6c.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt