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 <stdio.h> 17 #include <netdb.h> 18 #include <resolv.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <syslog.h> 22 #include <signal.h> 23 #include <unistd.h> 24 #include <inttypes.h> 25 #include <arpa/inet.h> 26 #include <sys/wait.h> 27 #include <netinet/in.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 static 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 + 22 + 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 entries. 184 * This will allow immediate removal of the old ipv6-prefix-assignment that might 185 * otherwise be kept for up to 2 hours (see L-13 requirement of RFC 7084). 186 */ 187 if (!e[i].valid && type != ENTRY_PREFIX) 188 continue; 189 190 inet_ntop(AF_INET6, &e[i].target, &buf[buf_len], INET6_ADDRSTRLEN); 191 buf_len += strlen(&buf[buf_len]); 192 193 if (type != ENTRY_HOST) { 194 snprintf(&buf[buf_len], 6, "/%"PRIu16, e[i].length); 195 buf_len += strlen(&buf[buf_len]); 196 197 if (type == ENTRY_ROUTE) { 198 buf[buf_len++] = ','; 199 200 if (!IN6_IS_ADDR_UNSPECIFIED(&e[i].router)) { 201 inet_ntop(AF_INET6, &e[i].router, &buf[buf_len], INET6_ADDRSTRLEN); 202 buf_len += strlen(&buf[buf_len]); 203 } 204 205 snprintf(&buf[buf_len], 23, ",%u,%u", e[i].valid, e[i].priority); 206 buf_len += strlen(&buf[buf_len]); 207 } else { 208 snprintf(&buf[buf_len], 23, ",%u,%u", e[i].preferred, e[i].valid); 209 buf_len += strlen(&buf[buf_len]); 210 } 211 212 if (type == ENTRY_PREFIX && ntohl(e[i].iaid) != 1) { 213 snprintf(&buf[buf_len], 16, ",class=%08x", ntohl(e[i].iaid)); 214 buf_len += strlen(&buf[buf_len]); 215 } 216 217 if (type == ENTRY_PREFIX && e[i].priority) { 218 // priority and router are abused for prefix exclusion 219 snprintf(&buf[buf_len], 11, ",excluded="); 220 buf_len += strlen(&buf[buf_len]); 221 inet_ntop(AF_INET6, &e[i].router, &buf[buf_len], INET6_ADDRSTRLEN); 222 buf_len += strlen(&buf[buf_len]); 223 snprintf(&buf[buf_len], 12, "/%u", e[i].priority); 224 buf_len += strlen(&buf[buf_len]); 225 } 226 } 227 228 buf[buf_len++] = ' '; 229 } 230 231 if (buf[buf_len - 1] == ' ') 232 buf_len--; 233 234 buf[buf_len] = '\0'; 235 putenv(buf); 236 } 237 238 static void search_to_env(const char *name, const uint8_t *start, size_t len) 239 { 240 size_t buf_len = strlen(name); 241 char *buf = realloc(NULL, buf_len + 2 + len); 242 char *c = mempcpy(buf, name, buf_len); 243 *c++ = '='; 244 245 for (struct odhcp6c_entry *e = (struct odhcp6c_entry*)start; 246 (uint8_t*)e < &start[len] && 247 (uint8_t*)odhcp6c_next_entry(e) <= &start[len]; 248 e = odhcp6c_next_entry(e)) { 249 if (!e->valid) 250 continue; 251 c = mempcpy(c, e->auxtarget, e->auxlen); 252 *c++ = ' '; 253 } 254 255 if (c[-1] == ' ') 256 c--; 257 258 *c = '\0'; 259 putenv(buf); 260 } 261 262 static void int_to_env(const char *name, int value) 263 { 264 size_t len = 13 + strlen(name); 265 char *buf = realloc(NULL, len); 266 267 snprintf(buf, len, "%s=%d", name, value); 268 putenv(buf); 269 } 270 271 static void s46_to_env_portparams(const uint8_t *data, size_t len, FILE *fp) 272 { 273 uint8_t *odata; 274 uint16_t otype, olen; 275 276 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 277 if (otype == DHCPV6_OPT_S46_PORTPARAMS && 278 olen == sizeof(struct dhcpv6_s46_portparams)) { 279 struct dhcpv6_s46_portparams *params = (void*)odata; 280 fprintf(fp, "offset=%d,psidlen=%d,psid=%d,", 281 params->offset, params->psid_len, ntohs(params->psid)); 282 } 283 } 284 } 285 286 static void s46_to_env(enum odhcp6c_state state, const uint8_t *data, size_t len) 287 { 288 const char *name = (state == STATE_S46_MAPE) ? "MAPE" : 289 (state == STATE_S46_MAPT) ? "MAPT" : "LW4O6"; 290 291 if (len == 0) 292 return; 293 294 char *str; 295 size_t strsize; 296 297 FILE *fp = open_memstream(&str, &strsize); 298 fputs(name, fp); 299 fputc('=', fp); 300 301 const char *type = (state == STATE_S46_MAPE) ? "map-e" : 302 (state == STATE_S46_MAPT) ? "map-t" : "lw4o6"; 303 304 uint8_t *odata; 305 uint16_t otype, olen; 306 307 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 308 struct dhcpv6_s46_rule *rule = (struct dhcpv6_s46_rule*)odata; 309 struct dhcpv6_s46_v4v6bind *bind = (struct dhcpv6_s46_v4v6bind*)odata; 310 311 if (state != STATE_S46_LW && otype == DHCPV6_OPT_S46_RULE && 312 olen >= sizeof(struct dhcpv6_s46_rule)) { 313 char buf4[INET_ADDRSTRLEN]; 314 char buf6[INET6_ADDRSTRLEN]; 315 struct in6_addr in6 = IN6ADDR_ANY_INIT; 316 317 size_t prefix6len = rule->prefix6_len; 318 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 319 320 if (prefix6len > sizeof(in6) || 321 olen < sizeof(struct dhcpv6_s46_rule) + prefix6len) 322 continue; 323 324 memcpy(&in6, rule->ipv6_prefix, prefix6len); 325 326 inet_ntop(AF_INET, &rule->ipv4_prefix, buf4, sizeof(buf4)); 327 inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6)); 328 329 if (rule->flags & 1) 330 fputs("fmr,", fp); 331 332 fprintf(fp, "type=%s,ealen=%d,prefix4len=%d,prefix6len=%d,ipv4prefix=%s,ipv6prefix=%s,", 333 type, rule->ea_len, rule->prefix4_len, rule->prefix6_len, buf4, buf6); 334 335 s46_to_env_portparams(&rule->ipv6_prefix[prefix6len], 336 olen - sizeof(*rule) - prefix6len, fp); 337 338 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 339 if (state != STATE_S46_MAPT && otype == DHCPV6_OPT_S46_BR && 340 olen == sizeof(struct in6_addr)) { 341 inet_ntop(AF_INET6, odata, buf6, sizeof(buf6)); 342 fprintf(fp, "br=%s,", buf6); 343 } else if (state == STATE_S46_MAPT && otype == DHCPV6_OPT_S46_DMR && 344 olen >= sizeof(struct dhcpv6_s46_dmr)) { 345 struct dhcpv6_s46_dmr *dmr = (struct dhcpv6_s46_dmr*)odata; 346 memset(&in6, 0, sizeof(in6)); 347 size_t prefix6len = dmr->dmr_prefix6_len; 348 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 349 350 if (prefix6len > sizeof(in6) || 351 olen < sizeof(struct dhcpv6_s46_dmr) + prefix6len) 352 continue; 353 354 memcpy(&in6, dmr->dmr_ipv6_prefix, prefix6len); 355 inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6)); 356 fprintf(fp, "dmr=%s/%d,", buf6, dmr->dmr_prefix6_len); 357 } 358 } 359 360 fputc(' ', fp); 361 } else if (state == STATE_S46_LW && otype == DHCPV6_OPT_S46_V4V6BIND && 362 olen >= sizeof(struct dhcpv6_s46_v4v6bind)) { 363 char buf4[INET_ADDRSTRLEN]; 364 char buf6[INET6_ADDRSTRLEN]; 365 struct in6_addr in6 = IN6ADDR_ANY_INIT; 366 367 size_t prefix6len = bind->bindprefix6_len; 368 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 369 370 if (prefix6len > sizeof(in6) || 371 olen < sizeof(struct dhcpv6_s46_v4v6bind) + prefix6len) 372 continue; 373 374 memcpy(&in6, bind->bind_ipv6_prefix, prefix6len); 375 376 inet_ntop(AF_INET, &bind->ipv4_address, buf4, sizeof(buf4)); 377 inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6)); 378 379 fprintf(fp, "type=%s,prefix4len=32,prefix6len=%d,ipv4prefix=%s,ipv6prefix=%s,", 380 type, bind->bindprefix6_len, buf4, buf6); 381 382 s46_to_env_portparams(&bind->bind_ipv6_prefix[prefix6len], 383 olen - sizeof(*bind) - prefix6len, fp); 384 385 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 386 if (otype == DHCPV6_OPT_S46_BR && olen == sizeof(struct in6_addr)) { 387 inet_ntop(AF_INET6, odata, buf6, sizeof(buf6)); 388 fprintf(fp, "br=%s,", buf6); 389 } 390 } 391 392 fputc(' ', fp); 393 } 394 } 395 396 fclose(fp); 397 putenv(str); 398 } 399 400 void script_call(const char *status, int delay, bool resume) 401 { 402 time_t now = odhcp6c_get_milli_time() / 1000; 403 bool running_script = false; 404 405 if (running) { 406 time_t diff = now - started; 407 408 kill(running, SIGTERM); 409 410 if (diff > delay) 411 delay -= diff; 412 else 413 delay = 0; 414 415 running_script = true; 416 } 417 418 if (resume || !running_script || !action[0]) 419 strncpy(action, status, sizeof(action) - 1); 420 421 pid_t pid = fork(); 422 423 if (pid > 0) { 424 running = pid; 425 started = now; 426 427 if (!resume) 428 action[0] = 0; 429 430 } else if (pid == 0) { 431 size_t dns_len, search_len, custom_len, sntp_ip_len, ntp_ip_len, ntp_dns_len; 432 size_t sip_ip_len, sip_fqdn_len, aftr_name_len, cer_len, addr_len; 433 size_t s46_mapt_len, s46_mape_len, s46_lw_len, passthru_len; 434 435 signal(SIGTERM, SIG_DFL); 436 if (delay > 0) { 437 sleep(delay); 438 odhcp6c_expire(false); 439 } 440 441 struct in6_addr *addr = odhcp6c_get_state(STATE_SERVER_ADDR, &addr_len); 442 struct in6_addr *dns = odhcp6c_get_state(STATE_DNS, &dns_len); 443 uint8_t *search = odhcp6c_get_state(STATE_SEARCH, &search_len); 444 uint8_t *custom = odhcp6c_get_state(STATE_CUSTOM_OPTS, &custom_len); 445 struct in6_addr *sntp = odhcp6c_get_state(STATE_SNTP_IP, &sntp_ip_len); 446 struct in6_addr *ntp = odhcp6c_get_state(STATE_NTP_IP, &ntp_ip_len); 447 uint8_t *ntp_dns = odhcp6c_get_state(STATE_NTP_FQDN, &ntp_dns_len); 448 struct in6_addr *sip = odhcp6c_get_state(STATE_SIP_IP, &sip_ip_len); 449 uint8_t *sip_fqdn = odhcp6c_get_state(STATE_SIP_FQDN, &sip_fqdn_len); 450 uint8_t *aftr_name = odhcp6c_get_state(STATE_AFTR_NAME, &aftr_name_len); 451 struct in6_addr *cer = odhcp6c_get_state(STATE_CER, &cer_len); 452 uint8_t *s46_mapt = odhcp6c_get_state(STATE_S46_MAPT, &s46_mapt_len); 453 uint8_t *s46_mape = odhcp6c_get_state(STATE_S46_MAPE, &s46_mape_len); 454 uint8_t *s46_lw = odhcp6c_get_state(STATE_S46_LW, &s46_lw_len); 455 uint8_t *passthru = odhcp6c_get_state(STATE_PASSTHRU, &passthru_len); 456 457 size_t prefix_len, address_len, ra_pref_len, 458 ra_route_len, ra_dns_len, ra_search_len; 459 uint8_t *prefix = odhcp6c_get_state(STATE_IA_PD, &prefix_len); 460 uint8_t *address = odhcp6c_get_state(STATE_IA_NA, &address_len); 461 uint8_t *ra_pref = odhcp6c_get_state(STATE_RA_PREFIX, &ra_pref_len); 462 uint8_t *ra_route = odhcp6c_get_state(STATE_RA_ROUTE, &ra_route_len); 463 uint8_t *ra_dns = odhcp6c_get_state(STATE_RA_DNS, &ra_dns_len); 464 uint8_t *ra_search = odhcp6c_get_state(STATE_RA_SEARCH, &ra_search_len); 465 466 ipv6_to_env("SERVER", addr, addr_len / sizeof(*addr)); 467 ipv6_to_env("RDNSS", dns, dns_len / sizeof(*dns)); 468 ipv6_to_env("SNTP_IP", sntp, sntp_ip_len / sizeof(*sntp)); 469 ipv6_to_env("NTP_IP", ntp, ntp_ip_len / sizeof(*ntp)); 470 fqdn_to_env("NTP_FQDN", ntp_dns, ntp_dns_len); 471 ipv6_to_env("SIP_IP", sip, sip_ip_len / sizeof(*sip)); 472 fqdn_to_env("DOMAINS", search, search_len); 473 fqdn_to_env("SIP_DOMAIN", sip_fqdn, sip_fqdn_len); 474 fqdn_to_env("AFTR", aftr_name, aftr_name_len); 475 ipv6_to_env("CER", cer, cer_len / sizeof(*cer)); 476 s46_to_env(STATE_S46_MAPE, s46_mape, s46_mape_len); 477 s46_to_env(STATE_S46_MAPT, s46_mapt, s46_mapt_len); 478 s46_to_env(STATE_S46_LW, s46_lw, s46_lw_len); 479 bin_to_env(custom, custom_len); 480 481 if (odhcp6c_is_bound()) { 482 entry_to_env("PREFIXES", prefix, prefix_len, ENTRY_PREFIX); 483 entry_to_env("ADDRESSES", address, address_len, ENTRY_ADDRESS); 484 } 485 486 entry_to_env("RA_ADDRESSES", ra_pref, ra_pref_len, ENTRY_ADDRESS); 487 entry_to_env("RA_ROUTES", ra_route, ra_route_len, ENTRY_ROUTE); 488 entry_to_env("RA_DNS", ra_dns, ra_dns_len, ENTRY_HOST); 489 search_to_env("RA_DOMAINS", ra_search, ra_search_len); 490 491 int_to_env("RA_HOPLIMIT", ra_get_hoplimit()); 492 int_to_env("RA_MTU", ra_get_mtu()); 493 int_to_env("RA_REACHABLE", ra_get_reachable()); 494 int_to_env("RA_RETRANSMIT", ra_get_retransmit()); 495 496 char *buf = malloc(10 + passthru_len * 2); 497 strncpy(buf, "PASSTHRU=", 10); 498 script_hexlify(&buf[9], passthru, passthru_len); 499 putenv(buf); 500 501 execv(argv[0], argv); 502 _exit(128); 503 } 504 } 505
This page was automatically generated by LXR 0.3.1. • OpenWrt