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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt