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 <arpa/inet.h> 17 #include <inttypes.h> 18 #include <netdb.h> 19 #include <netinet/in.h> 20 #include <resolv.h> 21 #include <signal.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <sys/wait.h> 26 #include <unistd.h> 27 28 #include "odhcp6c.h" 29 30 static const char hexdigits[] = "0123456789abcdef"; 31 static const int8_t hexvals[] = { 32 -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1, 33 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 34 -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 35 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 36 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 37 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 38 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 39 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 40 }; 41 42 static char action[16] = ""; 43 static char *argv[4] = {NULL, NULL, action, NULL}; 44 static volatile pid_t running = 0; 45 static time_t started; 46 47 static void script_sighandle(int signal) 48 { 49 if (signal == SIGCHLD) { 50 pid_t child; 51 52 while ((child = waitpid(-1, NULL, WNOHANG)) > 0) 53 if (running == child) 54 running = 0; 55 } 56 } 57 58 int script_init(const char *path, const char *ifname) 59 { 60 argv[0] = (char*)path; 61 argv[1] = (char*)ifname; 62 signal(SIGCHLD, script_sighandle); 63 64 return 0; 65 } 66 67 ssize_t script_unhexlify(uint8_t *dst, size_t len, const char *src) 68 { 69 size_t c; 70 71 for (c = 0; c < len && src[0] && src[1]; ++c) { 72 int8_t x = (int8_t)*src++; 73 int8_t y = (int8_t)*src++; 74 if (x < 0 || (x = hexvals[x]) < 0 75 || y < 0 || (y = hexvals[y]) < 0) 76 return -1; 77 dst[c] = x << 4 | y; 78 while (((int8_t)*src) < 0 || 79 (*src && hexvals[(uint8_t)*src] < 0)) 80 src++; 81 } 82 83 return c; 84 } 85 86 void script_hexlify(char *dst, const uint8_t *src, size_t len) 87 { 88 for (size_t i = 0; i < len; ++i) { 89 *dst++ = hexdigits[src[i] >> 4]; 90 *dst++ = hexdigits[src[i] & 0x0f]; 91 } 92 93 *dst = 0; 94 } 95 96 static void ipv6_to_env(const char *name, 97 const struct in6_addr *addr, size_t cnt) 98 { 99 size_t buf_len = strlen(name); 100 char *buf = realloc(NULL, cnt * INET6_ADDRSTRLEN + buf_len + 2); 101 102 memcpy(buf, name, buf_len); 103 buf[buf_len++] = '='; 104 105 for (size_t i = 0; i < cnt; ++i) { 106 inet_ntop(AF_INET6, &addr[i], &buf[buf_len], INET6_ADDRSTRLEN); 107 buf_len += strlen(&buf[buf_len]); 108 buf[buf_len++] = ' '; 109 } 110 111 if (buf[buf_len - 1] == ' ') 112 buf_len--; 113 114 buf[buf_len] = '\0'; 115 putenv(buf); 116 } 117 118 static void fqdn_to_env(const char *name, const uint8_t *fqdn, size_t len) 119 { 120 size_t buf_len = strlen(name); 121 size_t buf_size = len + buf_len + 2; 122 const uint8_t *fqdn_end = fqdn + len; 123 char *buf = realloc(NULL, len + buf_len + 2); 124 125 memcpy(buf, name, buf_len); 126 buf[buf_len++] = '='; 127 128 while (fqdn < fqdn_end) { 129 int l = dn_expand(fqdn, fqdn_end, fqdn, &buf[buf_len], buf_size - buf_len); 130 if (l <= 0) 131 break; 132 fqdn += l; 133 buf_len += strlen(&buf[buf_len]); 134 buf[buf_len++] = ' '; 135 } 136 137 if (buf[buf_len - 1] == ' ') 138 buf_len--; 139 140 buf[buf_len] = '\0'; 141 putenv(buf); 142 } 143 144 static void string_to_env(const char *name, const uint8_t *string, size_t len) 145 { 146 size_t buf_len = strlen(name); 147 const uint8_t *string_end = string + len; 148 char *buf = realloc(NULL, len + buf_len + 2); 149 150 memcpy(buf, name, buf_len); 151 buf[buf_len++] = '='; 152 153 while (string < string_end) { 154 int l = strlen((const char *)string); 155 if (l <= 0) 156 break; 157 string += l; 158 buf_len += strlen(&buf[buf_len]); 159 buf[buf_len++] = ' '; 160 } 161 162 if (buf[buf_len - 1] == ' ') 163 buf_len--; 164 165 buf[buf_len] = '\0'; 166 putenv(buf); 167 } 168 169 static void bin_to_env(uint8_t *opts, size_t len) 170 { 171 uint8_t *oend = opts + len, *odata; 172 uint16_t otype, olen; 173 174 dhcpv6_for_each_option(opts, oend, otype, olen, odata) { 175 char *buf = realloc(NULL, 14 + (olen * 2)); 176 size_t buf_len = 0; 177 178 snprintf(buf, 14, "OPTION_%hu=", otype); 179 buf_len += strlen(buf); 180 181 script_hexlify(&buf[buf_len], odata, olen); 182 putenv(buf); 183 } 184 } 185 186 enum entry_type { 187 ENTRY_ADDRESS, 188 ENTRY_HOST, 189 ENTRY_ROUTE, 190 ENTRY_PREFIX 191 }; 192 193 static void entry_to_env(const char *name, const void *data, size_t len, enum entry_type type) 194 { 195 size_t buf_len = strlen(name); 196 const struct odhcp6c_entry *e = data; 197 // Worst case: ENTRY_PREFIX with iaid != 1 and exclusion 198 const size_t max_entry_len = (INET6_ADDRSTRLEN-1 + 5 + 44 + 15 + 10 + 199 INET6_ADDRSTRLEN-1 + 11 + 1); 200 char *buf = realloc(NULL, buf_len + 2 + (len / sizeof(*e)) * max_entry_len); 201 202 memcpy(buf, name, buf_len); 203 buf[buf_len++] = '='; 204 205 for (size_t i = 0; i < len / sizeof(*e); ++i) { 206 /* 207 * The only invalid entries allowed to be passed to the script are prefix and RA 208 * entries. This will allow immediate removal of the old ipv6-prefix-assignment 209 * that might otherwise be kept for up to 2 hours (see L-13 requirement of RFC 7084). 210 * Similarly, a RA with router lifetime set to 0 indicates that the advertising 211 * router "is not a default router and SHOULD NOT appear on the default router list" 212 * (see RFC 4861, section 4.2). 213 */ 214 if (!e[i].valid && type != ENTRY_PREFIX && type != ENTRY_ROUTE) 215 continue; 216 217 inet_ntop(AF_INET6, &e[i].target, &buf[buf_len], INET6_ADDRSTRLEN); 218 buf_len += strlen(&buf[buf_len]); 219 220 if (type != ENTRY_HOST) { 221 snprintf(&buf[buf_len], 6, "/%"PRIu16, e[i].length); 222 buf_len += strlen(&buf[buf_len]); 223 224 if (type == ENTRY_ROUTE) { 225 buf[buf_len++] = ','; 226 227 if (!IN6_IS_ADDR_UNSPECIFIED(&e[i].router)) { 228 inet_ntop(AF_INET6, &e[i].router, &buf[buf_len], INET6_ADDRSTRLEN); 229 buf_len += strlen(&buf[buf_len]); 230 } 231 232 snprintf(&buf[buf_len], 23, ",%u,%u", e[i].valid, e[i].priority); 233 buf_len += strlen(&buf[buf_len]); 234 } else { 235 snprintf(&buf[buf_len], 45, ",%u,%u,%u,%u", e[i].preferred, e[i].valid, e[i].t1, e[i].t2); 236 buf_len += strlen(&buf[buf_len]); 237 } 238 239 if (type == ENTRY_PREFIX && ntohl(e[i].iaid) != 1) { 240 snprintf(&buf[buf_len], 16, ",class=%08x", ntohl(e[i].iaid)); 241 buf_len += strlen(&buf[buf_len]); 242 } 243 244 if (type == ENTRY_PREFIX && e[i].exclusion_length) { 245 snprintf(&buf[buf_len], 11, ",excluded="); 246 buf_len += strlen(&buf[buf_len]); 247 // '.router' is dual-used: for prefixes it contains the prefix 248 inet_ntop(AF_INET6, &e[i].router, &buf[buf_len], INET6_ADDRSTRLEN); 249 buf_len += strlen(&buf[buf_len]); 250 snprintf(&buf[buf_len], 12, "/%u", e[i].exclusion_length); 251 buf_len += strlen(&buf[buf_len]); 252 } 253 } 254 255 buf[buf_len++] = ' '; 256 } 257 258 if (buf[buf_len - 1] == ' ') 259 buf_len--; 260 261 buf[buf_len] = '\0'; 262 putenv(buf); 263 } 264 265 static void search_to_env(const char *name, const uint8_t *start, size_t len) 266 { 267 size_t buf_len = strlen(name); 268 char *buf = realloc(NULL, buf_len + 2 + len); 269 char *c = mempcpy(buf, name, buf_len); 270 *c++ = '='; 271 272 for (struct odhcp6c_entry *e = (struct odhcp6c_entry*)start; 273 (uint8_t*)e < &start[len] && 274 (uint8_t*)odhcp6c_next_entry(e) <= &start[len]; 275 e = odhcp6c_next_entry(e)) { 276 if (!e->valid) 277 continue; 278 c = mempcpy(c, e->auxtarget, e->auxlen); 279 *c++ = ' '; 280 } 281 282 if (c[-1] == ' ') 283 c--; 284 285 *c = '\0'; 286 putenv(buf); 287 } 288 289 static void int_to_env(const char *name, int value) 290 { 291 size_t len = 13 + strlen(name); 292 char *buf = realloc(NULL, len); 293 294 snprintf(buf, len, "%s=%d", name, value); 295 putenv(buf); 296 } 297 298 static void s46_to_env_portparams(const uint8_t *data, size_t len, FILE *fp) 299 { 300 uint8_t *odata; 301 uint16_t otype, olen; 302 303 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 304 if (otype == DHCPV6_OPT_S46_PORTPARAMS && 305 olen == sizeof(struct dhcpv6_s46_portparams)) { 306 struct dhcpv6_s46_portparams *params = (void*)odata; 307 fprintf(fp, "offset=%d,psidlen=%d,psid=%d,", 308 params->offset, params->psid_len, ntohs(params->psid)); 309 } 310 } 311 } 312 313 static void s46_to_env(enum odhcp6c_state state, const uint8_t *data, size_t len) 314 { 315 const char *name = (state == STATE_S46_MAPE) ? "MAPE" : 316 (state == STATE_S46_MAPT) ? "MAPT" : "LW4O6"; 317 318 if (len == 0) 319 return; 320 321 char *str; 322 size_t strsize; 323 324 FILE *fp = open_memstream(&str, &strsize); 325 fputs(name, fp); 326 fputc('=', fp); 327 328 const char *type = (state == STATE_S46_MAPE) ? "map-e" : 329 (state == STATE_S46_MAPT) ? "map-t" : "lw4o6"; 330 331 uint8_t *odata; 332 uint16_t otype, olen; 333 334 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 335 struct dhcpv6_s46_rule *rule = (struct dhcpv6_s46_rule*)odata; 336 struct dhcpv6_s46_v4v6bind *bind = (struct dhcpv6_s46_v4v6bind*)odata; 337 338 if (state != STATE_S46_LW && otype == DHCPV6_OPT_S46_RULE && 339 olen >= sizeof(struct dhcpv6_s46_rule)) { 340 char buf4[INET_ADDRSTRLEN]; 341 char buf6[INET6_ADDRSTRLEN]; 342 struct in6_addr in6 = IN6ADDR_ANY_INIT; 343 344 size_t prefix6len = rule->prefix6_len; 345 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 346 347 if (prefix6len > sizeof(in6) || 348 olen < sizeof(struct dhcpv6_s46_rule) + prefix6len) 349 continue; 350 351 memcpy(&in6, rule->ipv6_prefix, prefix6len); 352 353 inet_ntop(AF_INET, &rule->ipv4_prefix, buf4, sizeof(buf4)); 354 inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6)); 355 356 if (rule->flags & 1) 357 fputs("fmr,", fp); 358 359 fprintf(fp, "type=%s,ealen=%d,prefix4len=%d,prefix6len=%d,ipv4prefix=%s,ipv6prefix=%s,", 360 type, rule->ea_len, rule->prefix4_len, rule->prefix6_len, buf4, buf6); 361 362 s46_to_env_portparams(&rule->ipv6_prefix[prefix6len], 363 olen - sizeof(*rule) - prefix6len, fp); 364 365 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 366 if (state != STATE_S46_MAPT && otype == DHCPV6_OPT_S46_BR && 367 olen == sizeof(struct in6_addr)) { 368 inet_ntop(AF_INET6, odata, buf6, sizeof(buf6)); 369 fprintf(fp, "br=%s,", buf6); 370 } else if (state == STATE_S46_MAPT && otype == DHCPV6_OPT_S46_DMR && 371 olen >= sizeof(struct dhcpv6_s46_dmr)) { 372 struct dhcpv6_s46_dmr *dmr = (struct dhcpv6_s46_dmr*)odata; 373 memset(&in6, 0, sizeof(in6)); 374 size_t prefix6len = dmr->dmr_prefix6_len; 375 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 376 377 if (prefix6len > sizeof(in6) || 378 olen < sizeof(struct dhcpv6_s46_dmr) + prefix6len) 379 continue; 380 381 memcpy(&in6, dmr->dmr_ipv6_prefix, prefix6len); 382 inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6)); 383 fprintf(fp, "dmr=%s/%d,", buf6, dmr->dmr_prefix6_len); 384 } 385 } 386 387 fputc(' ', fp); 388 } else if (state == STATE_S46_LW && otype == DHCPV6_OPT_S46_V4V6BIND && 389 olen >= sizeof(struct dhcpv6_s46_v4v6bind)) { 390 char buf4[INET_ADDRSTRLEN]; 391 char buf6[INET6_ADDRSTRLEN]; 392 struct in6_addr in6 = IN6ADDR_ANY_INIT; 393 394 size_t prefix6len = bind->bindprefix6_len; 395 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 396 397 if (prefix6len > sizeof(in6) || 398 olen < sizeof(struct dhcpv6_s46_v4v6bind) + prefix6len) 399 continue; 400 401 memcpy(&in6, bind->bind_ipv6_prefix, prefix6len); 402 403 inet_ntop(AF_INET, &bind->ipv4_address, buf4, sizeof(buf4)); 404 inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6)); 405 406 fprintf(fp, "type=%s,prefix4len=32,prefix6len=%d,ipv4prefix=%s,ipv6prefix=%s,", 407 type, bind->bindprefix6_len, buf4, buf6); 408 409 s46_to_env_portparams(&bind->bind_ipv6_prefix[prefix6len], 410 olen - sizeof(*bind) - prefix6len, fp); 411 412 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 413 if (otype == DHCPV6_OPT_S46_BR && olen == sizeof(struct in6_addr)) { 414 inet_ntop(AF_INET6, odata, buf6, sizeof(buf6)); 415 fprintf(fp, "br=%s,", buf6); 416 } 417 } 418 419 fputc(' ', fp); 420 } 421 } 422 423 fclose(fp); 424 putenv(str); 425 } 426 427 void script_call(const char *status, int delay, bool resume) 428 { 429 time_t now = odhcp6c_get_milli_time() / 1000; 430 bool running_script = false; 431 432 if (!argv[0]) 433 return; 434 435 if (running) { 436 time_t diff = now - started; 437 438 kill(running, SIGTERM); 439 440 if (diff > delay) 441 delay -= diff; 442 else 443 delay = 0; 444 445 running_script = true; 446 } 447 448 if (resume || !running_script || !action[0]) 449 strncpy(action, status, sizeof(action) - 1); 450 451 pid_t pid = fork(); 452 453 if (pid > 0) { 454 running = pid; 455 started = now; 456 457 if (!resume) 458 action[0] = 0; 459 460 } else if (pid == 0) { 461 size_t dns_len, search_len, custom_len, sntp_ip_len, ntp_ip_len, ntp_dns_len; 462 size_t sip_ip_len, sip_fqdn_len, aftr_name_len, addr_len; 463 size_t s46_mapt_len, s46_mape_len, s46_lw_len, passthru_len; 464 size_t capt_port_ra_len, capt_port_dhcpv6_len; 465 466 signal(SIGTERM, SIG_DFL); 467 if (delay > 0) { 468 sleep(delay); 469 odhcp6c_expire(false); 470 } 471 472 struct in6_addr *addr = odhcp6c_get_state(STATE_SERVER_ADDR, &addr_len); 473 struct in6_addr *dns = odhcp6c_get_state(STATE_DNS, &dns_len); 474 uint8_t *search = odhcp6c_get_state(STATE_SEARCH, &search_len); 475 uint8_t *custom = odhcp6c_get_state(STATE_CUSTOM_OPTS, &custom_len); 476 struct in6_addr *sntp = odhcp6c_get_state(STATE_SNTP_IP, &sntp_ip_len); 477 struct in6_addr *ntp = odhcp6c_get_state(STATE_NTP_IP, &ntp_ip_len); 478 uint8_t *ntp_dns = odhcp6c_get_state(STATE_NTP_FQDN, &ntp_dns_len); 479 struct in6_addr *sip = odhcp6c_get_state(STATE_SIP_IP, &sip_ip_len); 480 uint8_t *sip_fqdn = odhcp6c_get_state(STATE_SIP_FQDN, &sip_fqdn_len); 481 uint8_t *aftr_name = odhcp6c_get_state(STATE_AFTR_NAME, &aftr_name_len); 482 uint8_t *s46_mapt = odhcp6c_get_state(STATE_S46_MAPT, &s46_mapt_len); 483 uint8_t *s46_mape = odhcp6c_get_state(STATE_S46_MAPE, &s46_mape_len); 484 uint8_t *s46_lw = odhcp6c_get_state(STATE_S46_LW, &s46_lw_len); 485 uint8_t *capt_port_ra = odhcp6c_get_state(STATE_CAPT_PORT_RA, &capt_port_ra_len); 486 uint8_t *capt_port_dhcpv6 = odhcp6c_get_state(STATE_CAPT_PORT_DHCPV6, &capt_port_dhcpv6_len); 487 uint8_t *passthru = odhcp6c_get_state(STATE_PASSTHRU, &passthru_len); 488 489 size_t prefix_len, address_len, ra_pref_len, 490 ra_route_len, ra_dns_len, ra_search_len; 491 uint8_t *prefix = odhcp6c_get_state(STATE_IA_PD, &prefix_len); 492 uint8_t *address = odhcp6c_get_state(STATE_IA_NA, &address_len); 493 uint8_t *ra_pref = odhcp6c_get_state(STATE_RA_PREFIX, &ra_pref_len); 494 uint8_t *ra_route = odhcp6c_get_state(STATE_RA_ROUTE, &ra_route_len); 495 uint8_t *ra_dns = odhcp6c_get_state(STATE_RA_DNS, &ra_dns_len); 496 uint8_t *ra_search = odhcp6c_get_state(STATE_RA_SEARCH, &ra_search_len); 497 498 /* RFC8910 ยง3 */ 499 if (capt_port_ra_len > 0 && capt_port_dhcpv6_len > 0) { 500 if (capt_port_ra_len != capt_port_dhcpv6_len || 501 !memcmp(capt_port_dhcpv6, capt_port_ra, capt_port_dhcpv6_len)) 502 error( 503 "%s received via different vectors differ: preferring URI from DHCPv6", 504 CAPT_PORT_URI_STR); 505 } 506 507 ipv6_to_env("SERVER", addr, addr_len / sizeof(*addr)); 508 ipv6_to_env("RDNSS", dns, dns_len / sizeof(*dns)); 509 ipv6_to_env("SNTP_IP", sntp, sntp_ip_len / sizeof(*sntp)); 510 ipv6_to_env("NTP_IP", ntp, ntp_ip_len / sizeof(*ntp)); 511 fqdn_to_env("NTP_FQDN", ntp_dns, ntp_dns_len); 512 ipv6_to_env("SIP_IP", sip, sip_ip_len / sizeof(*sip)); 513 fqdn_to_env("DOMAINS", search, search_len); 514 fqdn_to_env("SIP_DOMAIN", sip_fqdn, sip_fqdn_len); 515 fqdn_to_env("AFTR", aftr_name, aftr_name_len); 516 s46_to_env(STATE_S46_MAPE, s46_mape, s46_mape_len); 517 s46_to_env(STATE_S46_MAPT, s46_mapt, s46_mapt_len); 518 s46_to_env(STATE_S46_LW, s46_lw, s46_lw_len); 519 if (capt_port_dhcpv6_len > 0) 520 string_to_env(CAPT_PORT_URI_STR, capt_port_dhcpv6, capt_port_dhcpv6_len); 521 else if (capt_port_ra_len > 0) 522 string_to_env(CAPT_PORT_URI_STR, capt_port_ra, capt_port_ra_len); 523 bin_to_env(custom, custom_len); 524 525 if (odhcp6c_is_bound()) { 526 entry_to_env("PREFIXES", prefix, prefix_len, ENTRY_PREFIX); 527 entry_to_env("ADDRESSES", address, address_len, ENTRY_ADDRESS); 528 } 529 530 entry_to_env("RA_ADDRESSES", ra_pref, ra_pref_len, ENTRY_ADDRESS); 531 entry_to_env("RA_ROUTES", ra_route, ra_route_len, ENTRY_ROUTE); 532 entry_to_env("RA_DNS", ra_dns, ra_dns_len, ENTRY_HOST); 533 search_to_env("RA_DOMAINS", ra_search, ra_search_len); 534 535 int_to_env("RA_HOPLIMIT", ra_get_hoplimit()); 536 int_to_env("RA_MTU", ra_get_mtu()); 537 int_to_env("RA_REACHABLE", ra_get_reachable()); 538 int_to_env("RA_RETRANSMIT", ra_get_retransmit()); 539 540 char *buf = malloc(10 + passthru_len * 2); 541 strncpy(buf, "PASSTHRU=", 10); 542 script_hexlify(&buf[9], passthru, passthru_len); 543 putenv(buf); 544 545 execv(argv[0], argv); 546 _exit(128); 547 } 548 } 549
This page was automatically generated by LXR 0.3.1. • OpenWrt