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