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