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