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 ((urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY)) < 0 || 437 init_dhcpv6(ifname, client_options, sk_prio, sol_timeout) || 438 ra_init(ifname, &ifid, ra_options, ra_holdoff_interval) || 439 script_init(script, ifname)) { 440 syslog(LOG_ERR, "failed to initialize: %s", strerror(errno)); 441 return 3; 442 } 443 444 if (daemonize) { 445 openlog("odhcp6c", LOG_PID, LOG_DAEMON); // Disable LOG_PERROR 446 if (daemon(0, 0)) { 447 syslog(LOG_ERR, "Failed to daemonize: %s", 448 strerror(errno)); 449 return 4; 450 } 451 452 if (!pidfile) { 453 snprintf((char*)buf, sizeof(buf), "/var/run/odhcp6c.%s.pid", ifname); 454 pidfile = (char*)buf; 455 } 456 457 FILE *fp = fopen(pidfile, "w"); 458 if (fp) { 459 fprintf(fp, "%i\n", getpid()); 460 fclose(fp); 461 } 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", 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", 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 size_t ia_pd_len, ia_na_len; 577 odhcp6c_get_state(STATE_IA_PD, &ia_pd_len); 578 odhcp6c_get_state(STATE_IA_NA, &ia_na_len); 579 580 if (ia_pd_len == 0 && ia_na_len == 0) 581 break; 582 583 // If we have IAs, try rebind otherwise restart 584 res = dhcpv6_request(DHCPV6_MSG_REBIND); 585 odhcp6c_signal_process(); 586 587 if (res > 0) 588 script_call("rebound", 0, true); 589 else 590 break; 591 } 592 break; 593 594 default: 595 break; 596 } 597 598 odhcp6c_expire(false); 599 600 size_t ia_pd_len, ia_na_len, server_id_len; 601 odhcp6c_get_state(STATE_IA_PD, &ia_pd_len); 602 odhcp6c_get_state(STATE_IA_NA, &ia_na_len); 603 odhcp6c_get_state(STATE_SERVER_ID, &server_id_len); 604 605 // Add all prefixes to lost prefixes 606 bound = false; 607 script_call("unbound", 0, true); 608 609 if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0) && release) 610 dhcpv6_request(DHCPV6_MSG_RELEASE); 611 612 odhcp6c_clear_state(STATE_IA_NA); 613 odhcp6c_clear_state(STATE_IA_PD); 614 } 615 616 script_call("stopped", 0, true); 617 618 return 0; 619 } 620 621 static int usage(void) 622 { 623 const char buf[] = 624 "Usage: odhcp6c [options] <interface>\n" 625 "\nFeature options:\n" 626 " -S <time> Wait at least <time> sec for a DHCP-server (0)\n" 627 " -D Discard advertisements without any address or prefix proposed\n" 628 " -N <mode> Mode for requesting addresses [try|force|none]\n" 629 " -P <[pfx/]len> Request IPv6-Prefix (0 = auto)\n" 630 " -F Force IPv6-Prefix\n" 631 " -V <class> Set vendor-class option (base-16 encoded)\n" 632 " -u <user-class> Set user-class option string\n" 633 " -x <opt>:<val> Add option opt (with value val) in sent packets (cumulative)\n" 634 " Examples of IPv6 address, string and base-16 encoded options:\n" 635 " -x dns:2001:2001::1,2001:2001::2 - option 23\n" 636 " -x 15:office - option 15 (userclass)\n" 637 " -x 0x1f4:ABBA - option 500\n" 638 " -x 202:'\"file\"' - option 202\n" 639 " -c <clientid> Override client-ID (base-16 encoded 16-bit type + value)\n" 640 " -i <iface-id> Use a custom interface identifier for RA handling\n" 641 " -r <options> Options to be requested (comma-separated)\n" 642 " -R Do not request any options except those specified with -r\n" 643 " -s <script> Status update script (/usr/sbin/odhcp6c-update)\n" 644 " -a Don't send Accept Reconfigure option\n" 645 " -f Don't send Client FQDN option\n" 646 " -k Don't send a RELEASE when stopping\n" 647 " -K <sk-prio> Set packet kernel priority (0)\n" 648 " -t <seconds> Maximum timeout for DHCPv6-SOLICIT (120)\n" 649 " -m <seconds> Minimum time between accepting RA updates (3)\n" 650 " -L Ignore default lifetime for RDNSS records\n" 651 " -U Ignore Server Unicast option\n" 652 "\nInvocation options:\n" 653 " -p <pidfile> Set pidfile (/var/run/odhcp6c.pid)\n" 654 " -d Daemonize\n" 655 " -e Write logmessages to stderr\n" 656 " -v Increase logging verbosity\n" 657 " -h Show this help\n\n"; 658 fputs(buf, stderr); 659 660 return 1; 661 } 662 663 // Don't want to pull-in librt and libpthread just for a monotonic clock... 664 uint64_t odhcp6c_get_milli_time(void) 665 { 666 struct timespec t; 667 668 clock_gettime(CLOCK_MONOTONIC, &t); 669 670 return ((uint64_t)t.tv_sec) * 1000 + ((uint64_t)t.tv_nsec) / 1000000; 671 } 672 673 static uint8_t* odhcp6c_resize_state(enum odhcp6c_state state, ssize_t len) 674 { 675 if (len == 0) 676 return state_data[state] + state_len[state]; 677 else if (state_len[state] + len > 1024) 678 return NULL; 679 680 uint8_t *n = realloc(state_data[state], state_len[state] + len); 681 682 if (n || state_len[state] + len == 0) { 683 state_data[state] = n; 684 n += state_len[state]; 685 state_len[state] += len; 686 } 687 688 return n; 689 } 690 691 bool odhcp6c_signal_process(void) 692 { 693 while (signal_io) { 694 signal_io = false; 695 696 bool ra_updated = ra_process(); 697 698 if (ra_link_up()) { 699 signal_usr2 = true; 700 ra = false; 701 } 702 703 if (ra_updated && (bound || allow_slaac_only >= 0)) { 704 script_call("ra-updated", (!ra && !bound) ? 705 script_sync_delay : script_accu_delay, false); 706 ra = true; 707 } 708 } 709 710 return signal_usr1 || signal_usr2 || signal_term; 711 } 712 713 void odhcp6c_clear_state(enum odhcp6c_state state) 714 { 715 state_len[state] = 0; 716 } 717 718 int odhcp6c_add_state(enum odhcp6c_state state, const void *data, size_t len) 719 { 720 uint8_t *n = odhcp6c_resize_state(state, len); 721 722 if (!n) 723 return -1; 724 725 memcpy(n, data, len); 726 727 return 0; 728 } 729 730 int odhcp6c_insert_state(enum odhcp6c_state state, size_t offset, const void *data, size_t len) 731 { 732 ssize_t len_after = state_len[state] - offset; 733 if (len_after < 0) 734 return -1; 735 736 uint8_t *n = odhcp6c_resize_state(state, len); 737 738 if (n) { 739 uint8_t *sdata = state_data[state]; 740 741 memmove(sdata + offset + len, sdata + offset, len_after); 742 memcpy(sdata + offset, data, len); 743 } 744 745 return 0; 746 } 747 748 size_t odhcp6c_remove_state(enum odhcp6c_state state, size_t offset, size_t len) 749 { 750 uint8_t *data = state_data[state]; 751 ssize_t len_after = state_len[state] - (offset + len); 752 753 if (len_after < 0) 754 return state_len[state]; 755 756 memmove(data + offset, data + offset + len, len_after); 757 758 return state_len[state] -= len; 759 } 760 761 void* odhcp6c_move_state(enum odhcp6c_state state, size_t *len) 762 { 763 *len = state_len[state]; 764 void *data = state_data[state]; 765 766 state_len[state] = 0; 767 state_data[state] = NULL; 768 769 return data; 770 } 771 772 void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len) 773 { 774 *len = state_len[state]; 775 776 return state_data[state]; 777 } 778 779 static struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new) 780 { 781 size_t len, cmplen = offsetof(struct odhcp6c_entry, target) + ((new->length + 7) / 8); 782 uint8_t *start = odhcp6c_get_state(state, &len); 783 784 for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start; 785 (uint8_t*)c < &start[len] && 786 (uint8_t*)odhcp6c_next_entry(c) <= &start[len]; 787 c = odhcp6c_next_entry(c)) { 788 if (!memcmp(c, new, cmplen) && !memcmp(c->auxtarget, new->auxtarget, new->auxlen)) 789 return c; 790 } 791 792 return NULL; 793 } 794 795 bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, 796 uint32_t safe, unsigned int holdoff_interval) 797 { 798 struct odhcp6c_entry *x = odhcp6c_find_entry(state, new); 799 800 if (x && x->valid > new->valid && new->valid < safe) 801 new->valid = safe; 802 803 if (x) { 804 if (holdoff_interval && new->valid >= x->valid && 805 new->valid != UINT32_MAX && 806 new->valid - x->valid < holdoff_interval && 807 new->preferred >= x->preferred && 808 new->preferred != UINT32_MAX && 809 new->preferred - x->preferred < holdoff_interval) 810 return false; 811 812 x->valid = new->valid; 813 x->preferred = new->preferred; 814 x->t1 = new->t1; 815 x->t2 = new->t2; 816 x->iaid = new->iaid; 817 } else if (odhcp6c_add_state(state, new, odhcp6c_entry_size(new))) 818 return false; 819 820 return true; 821 } 822 823 static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed, bool remove_expired) 824 { 825 size_t len; 826 uint8_t *start = odhcp6c_get_state(state, &len); 827 828 for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start; 829 (uint8_t*)c < &start[len] && 830 (uint8_t*)odhcp6c_next_entry(c) <= &start[len]; 831 ) { 832 if (c->t1 < elapsed) 833 c->t1 = 0; 834 else if (c->t1 != UINT32_MAX) 835 c->t1 -= elapsed; 836 837 if (c->t2 < elapsed) 838 c->t2 = 0; 839 else if (c->t2 != UINT32_MAX) 840 c->t2 -= elapsed; 841 842 if (c->preferred < elapsed) 843 c->preferred = 0; 844 else if (c->preferred != UINT32_MAX) 845 c->preferred -= elapsed; 846 847 if (c->valid < elapsed) 848 c->valid = 0; 849 else if (c->valid != UINT32_MAX) 850 c->valid -= elapsed; 851 852 if (!c->valid && remove_expired) { 853 odhcp6c_remove_state(state, ((uint8_t*)c) - start, odhcp6c_entry_size(c)); 854 start = odhcp6c_get_state(state, &len); 855 } else 856 c = odhcp6c_next_entry(c); 857 } 858 } 859 860 static uint8_t *odhcp6c_state_find_opt(const uint16_t code) 861 { 862 size_t opts_len; 863 uint8_t *odata, *opts = odhcp6c_get_state(STATE_OPTS, &opts_len); 864 uint16_t otype, olen; 865 866 dhcpv6_for_each_option(opts, &opts[opts_len], otype, olen, odata) { 867 if (otype == code) 868 return &odata[-4]; 869 } 870 871 return NULL; 872 } 873 874 void odhcp6c_expire(bool expire_ia_pd) 875 { 876 time_t now = odhcp6c_get_milli_time() / 1000; 877 uint32_t elapsed = (last_update > 0) ? now - last_update : 0; 878 879 last_update = now; 880 881 odhcp6c_expire_list(STATE_RA_PREFIX, elapsed, true); 882 odhcp6c_expire_list(STATE_RA_ROUTE, elapsed, true); 883 odhcp6c_expire_list(STATE_RA_DNS, elapsed, true); 884 odhcp6c_expire_list(STATE_RA_SEARCH, elapsed, true); 885 odhcp6c_expire_list(STATE_IA_NA, elapsed, true); 886 odhcp6c_expire_list(STATE_IA_PD, elapsed, expire_ia_pd); 887 } 888 889 uint32_t odhcp6c_elapsed(void) 890 { 891 return odhcp6c_get_milli_time() / 1000 - last_update; 892 } 893 894 int odhcp6c_random(void *buf, size_t len) 895 { 896 return read(urandom_fd, buf, len); 897 } 898 899 bool odhcp6c_is_bound(void) 900 { 901 return bound; 902 } 903 904 bool odhcp6c_addr_in_scope(const struct in6_addr *addr) 905 { 906 FILE *fd = fopen("/proc/net/if_inet6", "r"); 907 int len; 908 bool ret = false; 909 char buf[256]; 910 911 if (fd == NULL) 912 return false; 913 914 while (fgets(buf, sizeof(buf), fd)) { 915 struct in6_addr inet6_addr; 916 uint32_t flags, dummy; 917 unsigned int i; 918 char name[IF_NAMESIZE], addr_buf[33]; 919 920 len = strlen(buf); 921 922 if ((len <= 0) || buf[len - 1] != '\n') 923 break; 924 925 buf[--len] = '\0'; 926 927 if (sscanf(buf, "%s %x %x %x %x %s", 928 addr_buf, &dummy, &dummy, &dummy, &flags, name) != 6) 929 break; 930 931 if (strcmp(name, ifname) || 932 (flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE | IFA_F_DEPRECATED))) 933 continue; 934 935 for (i = 0; i < strlen(addr_buf); i++) { 936 if (!isxdigit(addr_buf[i]) || isupper(addr_buf[i])) 937 break; 938 } 939 940 memset(&inet6_addr, 0, sizeof(inet6_addr)); 941 for (i = 0; i < (strlen(addr_buf) / 2); i++) { 942 unsigned char byte; 943 static const char hex[] = "0123456789abcdef"; 944 byte = ((index(hex, addr_buf[i * 2]) - hex) << 4) | 945 (index(hex, addr_buf[i * 2 + 1]) - hex); 946 inet6_addr.s6_addr[i] = byte; 947 } 948 949 if ((IN6_IS_ADDR_LINKLOCAL(&inet6_addr) == IN6_IS_ADDR_LINKLOCAL(addr)) && 950 (IN6_IS_ADDR_UNIQUELOCAL(&inet6_addr) == IN6_IS_ADDR_UNIQUELOCAL(addr))) { 951 ret = true; 952 break; 953 } 954 } 955 956 fclose(fd); 957 return ret; 958 } 959 960 static void sighandler(int signal) 961 { 962 if (signal == SIGUSR1) 963 signal_usr1 = true; 964 else if (signal == SIGUSR2) 965 signal_usr2 = true; 966 else if (signal == SIGIO) 967 signal_io = true; 968 else 969 signal_term = true; 970 } 971 972 static int add_opt(const uint16_t code, const uint8_t *data, const uint16_t len) 973 { 974 struct { 975 uint16_t code; 976 uint16_t len; 977 } opt_hdr = { htons(code), htons(len) }; 978 979 if (odhcp6c_state_find_opt(code)) 980 return -1; 981 982 if (odhcp6c_add_state(STATE_OPTS, &opt_hdr, sizeof(opt_hdr)) || 983 odhcp6c_add_state(STATE_OPTS, data, len)) { 984 syslog(LOG_ERR, "Failed to add option %hu", code); 985 return 1; 986 } 987 988 return 0; 989 } 990 991 struct odhcp6c_opt *odhcp6c_find_opt(const uint16_t code) 992 { 993 struct odhcp6c_opt *opt = opts; 994 995 while (opt->code) { 996 if (opt->code == code) 997 return opt; 998 999 opt++; 1000 } 1001 1002 return NULL; 1003 } 1004 1005 static struct odhcp6c_opt *odhcp6c_find_opt_by_name(const char *name) 1006 { 1007 struct odhcp6c_opt *opt = opts; 1008 1009 if (!name || !strlen(name)) 1010 return NULL; 1011 1012 while (opt->code && (!opt->str || strcmp(opt->str, name))) 1013 opt++; 1014 1015 return (opt->code > 0 ? opt : NULL); 1016 } 1017 1018 static int parse_opt_u8(const char *src, uint8_t **dst) 1019 { 1020 int len = strlen(src); 1021 1022 *dst = realloc(*dst, len/2); 1023 if (!*dst) 1024 return -1; 1025 1026 return script_unhexlify(*dst, len, src); 1027 } 1028 1029 static int parse_opt_string(const char *src, uint8_t **dst, const bool array) 1030 { 1031 int o_len = 0; 1032 char *sep = strpbrk(src, ARRAY_SEP); 1033 1034 if (sep && !array) 1035 return -1; 1036 1037 do { 1038 if (sep) { 1039 *sep = 0; 1040 sep++; 1041 } 1042 1043 int len = strlen(src); 1044 1045 *dst = realloc(*dst, o_len + len); 1046 if (!*dst) 1047 return -1; 1048 1049 memcpy(&((*dst)[o_len]), src, len); 1050 1051 o_len += len; 1052 src = sep; 1053 1054 if (sep) 1055 sep = strpbrk(src, ARRAY_SEP); 1056 } while (src); 1057 1058 return o_len; 1059 } 1060 1061 static int parse_opt_dns_string(const char *src, uint8_t **dst, const bool array) 1062 { 1063 int o_len = 0; 1064 char *sep = strpbrk(src, ARRAY_SEP); 1065 1066 if (sep && !array) 1067 return -1; 1068 1069 do { 1070 uint8_t tmp[256]; 1071 1072 if (sep) { 1073 *sep = 0; 1074 sep++; 1075 } 1076 1077 int len = dn_comp(src, tmp, sizeof(tmp), NULL, NULL); 1078 if (len < 0) 1079 return -1; 1080 1081 *dst = realloc(*dst, o_len + len); 1082 if (!*dst) 1083 return -1; 1084 1085 memcpy(&((*dst)[o_len]), tmp, len); 1086 1087 o_len += len; 1088 src = sep; 1089 1090 if (sep) 1091 sep = strpbrk(src, ARRAY_SEP); 1092 } while (src); 1093 1094 return o_len; 1095 } 1096 1097 static int parse_opt_ip6(const char *src, uint8_t **dst, const bool array) 1098 { 1099 int o_len = 0; 1100 char *sep = strpbrk(src, ARRAY_SEP); 1101 1102 if (sep && !array) 1103 return -1; 1104 1105 do { 1106 int len = sizeof(struct in6_addr); 1107 1108 if (sep) { 1109 *sep = 0; 1110 sep++; 1111 } 1112 1113 *dst = realloc(*dst, o_len + len); 1114 if (!*dst) 1115 return -1; 1116 1117 if (inet_pton(AF_INET6, src, &((*dst)[o_len])) < 1) 1118 return -1; 1119 1120 o_len += len; 1121 src = sep; 1122 1123 if (sep) 1124 sep = strpbrk(src, ARRAY_SEP); 1125 } while (src); 1126 1127 return o_len; 1128 } 1129 1130 static int parse_opt_user_class(const char *src, uint8_t **dst, const bool array) 1131 { 1132 int o_len = 0; 1133 char *sep = strpbrk(src, ARRAY_SEP); 1134 1135 if (sep && !array) 1136 return -1; 1137 1138 do { 1139 if (sep) { 1140 *sep = 0; 1141 sep++; 1142 } 1143 uint16_t str_len = strlen(src); 1144 1145 *dst = realloc(*dst, o_len + str_len + 2); 1146 if (!*dst) 1147 return -1; 1148 1149 struct user_class { 1150 uint16_t len; 1151 uint8_t data[]; 1152 } *e = (struct user_class *)&((*dst)[o_len]); 1153 1154 e->len = ntohs(str_len); 1155 memcpy(e->data, src, str_len); 1156 1157 o_len += str_len + 2; 1158 src = sep; 1159 1160 if (sep) 1161 sep = strpbrk(src, ARRAY_SEP); 1162 } while (src); 1163 1164 return o_len; 1165 } 1166 1167 static int parse_opt_data(const char *data, uint8_t **dst, const unsigned int type, 1168 const bool array) 1169 { 1170 int ret = 0; 1171 1172 switch (type) { 1173 case OPT_U8: 1174 ret = parse_opt_u8(data, dst); 1175 break; 1176 1177 case OPT_STR: 1178 ret = parse_opt_string(data, dst, array); 1179 break; 1180 1181 case OPT_DNS_STR: 1182 ret = parse_opt_dns_string(data, dst, array); 1183 break; 1184 1185 case OPT_IP6: 1186 ret = parse_opt_ip6(data, dst, array); 1187 break; 1188 1189 case OPT_USER_CLASS: 1190 ret = parse_opt_user_class(data, dst, array); 1191 break; 1192 1193 default: 1194 ret = -1; 1195 break; 1196 } 1197 1198 return ret; 1199 } 1200 1201 static int parse_opt(const char *opt) 1202 { 1203 uint32_t optn; 1204 char *data; 1205 uint8_t *payload = NULL; 1206 int payload_len; 1207 unsigned int type = OPT_U8; 1208 bool array = false; 1209 struct odhcp6c_opt *dopt = NULL; 1210 int ret = -1; 1211 1212 data = strpbrk(opt, ":"); 1213 if (!data) 1214 return -1; 1215 1216 *data = '\0'; 1217 data++; 1218 1219 if (strlen(opt) == 0 || strlen(data) == 0) 1220 return -1; 1221 1222 dopt = odhcp6c_find_opt_by_name(opt); 1223 if (!dopt) { 1224 char *e; 1225 optn = strtoul(opt, &e, 0); 1226 if (*e || e == opt || optn > USHRT_MAX) 1227 return -1; 1228 1229 dopt = odhcp6c_find_opt(optn); 1230 } else 1231 optn = dopt->code; 1232 1233 /* Check if the type for the content is well-known */ 1234 if (dopt) { 1235 /* Refuse internal options */ 1236 if (dopt->flags & OPT_INTERNAL) 1237 return -1; 1238 1239 type = dopt->flags & OPT_MASK_SIZE; 1240 array = ((dopt->flags & OPT_ARRAY) == OPT_ARRAY) ? true : false; 1241 } else if (data[0] == '"' || data[0] == '\'') { 1242 char *end = strrchr(data + 1, data[0]); 1243 1244 if (end && (end == (data + strlen(data) - 1))) { 1245 /* Raw option is specified as a string */ 1246 type = OPT_STR; 1247 data++; 1248 *end = '\0'; 1249 } 1250 1251 } 1252 1253 payload_len = parse_opt_data(data, &payload, type, array); 1254 if (payload_len > 0) 1255 ret = add_opt(optn, payload, payload_len); 1256 1257 free(payload); 1258 1259 return ret; 1260 } 1261
This page was automatically generated by LXR 0.3.1. • OpenWrt