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