1/* $Id: dhcp6c.c,v 1.1.1.1 2006/12/04 00:45:23 Exp $ */ 2/* ported from KAME: dhcp6c.c,v 1.97 2002/09/24 14:20:49 itojun Exp */ 3 4/* 5 * Copyright (C) 1998 and 1999 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/types.h> 34#include <sys/socket.h> 35#include <sys/uio.h> 36#include <sys/stat.h> 37#include <sys/ioctl.h> 38#include <unistd.h> 39#include <errno.h> 40#if TIME_WITH_SYS_TIME 41# include <sys/time.h> 42# include <sys/timeb.h> 43# include <time.h> 44#else 45# if HAVE_SYS_TIME_H 46# include <sys/time.h> 47# else 48# include <time.h> 49# endif 50#endif 51#include <net/if.h> 52#include <linux/sockios.h> 53#if defined(__FreeBSD__) && __FreeBSD__ >= 3 54#include <net/if_var.h> 55#endif 56 57#include <arpa/inet.h> 58#include <netdb.h> 59 60#include <signal.h> 61#include <stdio.h> 62#include <stdarg.h> 63#include <syslog.h> 64#include <stdlib.h> 65#include <unistd.h> 66#include <string.h> 67#include <err.h> 68#include <ifaddrs.h> 69 70#include "queue.h" 71#include "dhcp6.h" 72#include "config.h" 73#include "common.h" 74#include "timer.h" 75#include "lease.h" 76 77static int debug = 0; 78static u_long sig_flags = 0; 79#define SIGF_TERM 0x1 80#define SIGF_HUP 0x2 81#define SIGF_CLEAN 0x4 82 83#define DHCP6S_VALID_REPLY(a) \ 84 (a == DHCP6S_REQUEST || a == DHCP6S_RENEW || \ 85 a == DHCP6S_REBIND || a == DHCP6S_DECLINE || \ 86 a == DHCP6S_RELEASE || a == DHCP6S_CONFIRM || \ 87 a == DHCP6S_INFOREQ) 88 89 90const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_CLIENT; 91 92static char *device = NULL; 93static int num_device = 0; 94static struct iaid_table iaidtab[100]; 95static u_int8_t client6_request_flag = 0; 96static char leasename[100]; 97 98#define CLIENT6_RELEASE_ADDR 0x1 99#define CLIENT6_CONFIRM_ADDR 0x2 100#define CLIENT6_REQUEST_ADDR 0x4 101#define CLIENT6_DECLINE_ADDR 0x8 102 103#define CLIENT6_INFO_REQ 0x10 104 105int insock; /* inbound udp port */ 106//int outsock; /* outbound udp port */ 107int nlsock; 108 109extern char *raproc_file; 110extern char *ifproc_file; 111extern struct ifproc_info *dadlist; 112extern FILE *client6_lease_file; 113extern struct dhcp6_iaidaddr client6_iaidaddr; 114FILE *dhcp6_resolv_file; 115static const struct sockaddr_in6 *sa6_allagent; 116static struct duid client_duid; 117 118static void usage __P((void)); 119static void client6_init __P((char *)); 120static void client6_ifinit __P((char *)); 121void free_servers __P((struct dhcp6_if *)); 122static void free_resources __P((struct dhcp6_if *)); 123static int create_request_list __P((int)); 124static void client6_mainloop __P((void)); 125static void process_signals __P((void)); 126static struct dhcp6_serverinfo *find_server __P((struct dhcp6_if *, 127 struct duid *)); 128static struct dhcp6_serverinfo *allocate_newserver __P((struct dhcp6_if *, struct dhcp6_optinfo *)); 129static struct dhcp6_serverinfo *select_server __P((struct dhcp6_if *)); 130void client6_send __P((struct dhcp6_event *)); 131int client6_send_newstate __P((struct dhcp6_if *, int)); 132static void client6_recv __P((void)); 133static int client6_recvadvert __P((struct dhcp6_if *, struct dhcp6 *, 134 ssize_t, struct dhcp6_optinfo *)); 135static int client6_recvreply __P((struct dhcp6_if *, struct dhcp6 *, 136 ssize_t, struct dhcp6_optinfo *)); 137static void client6_signal __P((int)); 138static struct dhcp6_event *find_event_withid __P((struct dhcp6_if *, 139 u_int32_t)); 140static struct dhcp6_timer *check_lease_file_timo __P((void *)); 141static struct dhcp6_timer *check_link_timo __P((void *)); 142static struct dhcp6_timer *check_dad_timo __P((void *)); 143static void setup_check_timer __P((struct dhcp6_if *)); 144static void setup_interface __P((char *)); 145struct dhcp6_timer *client6_timo __P((void *)); 146extern int client6_ifaddrconf __P((ifaddrconf_cmd_t, struct dhcp6_addr *)); 147extern struct dhcp6_timer *syncfile_timo __P((void *)); 148extern int radvd_parse (struct dhcp6_iaidaddr *, int); 149 150#define DHCP6C_CONF "/tmp/dhcp6c.conf" 151#define DHCP6C_USER_CONF "/tmp/dhcp6c_user.conf" 152#define DHCP6C_PIDFILE "/var/run/dhcpv6/dhcp6c.pid" 153#define DUID_FILE "/tmp/dhcp6c_duid" 154 155static int pid; 156char client6_lease_temp[256]; 157struct dhcp6_list request_list; 158struct dhcp6_list request_prefix_list; 159 160/* For testing purpose */ 161u_int32_t xid_solicit = 0; 162u_int32_t xid_request = 0; 163 164/* User secondary conf file to store info such as user-class */ 165int parse_user_file(char *user_file) 166{ 167 FILE *fp = NULL; 168 char line[1024]; 169 char user_class_key[] = "user_class"; 170 char *newline1; 171 char *newline2; 172 173 /* Initial client user class to empty string */ 174 set_dhcpc_user_class(""); 175 176 fp = fopen(user_file, "r"); 177 if (fp != NULL) 178 { 179 while (!feof(fp)) 180 { 181 fgets(line, sizeof(line), fp); 182 183 /* Remove trailing newline characters, if any */ 184 newline1 = strstr(line, "\r"); 185 newline2 = strstr(line, "\n"); 186 if (newline1) 187 *newline1 = '\0'; 188 if (newline2) 189 *newline2 = '\0'; 190 191 /* Extract the key */ 192 if (strncmp(line, user_class_key, strlen(user_class_key)) == 0) 193 set_dhcpc_user_class(&line[strlen(user_class_key)+1]); 194 } 195 fclose(fp); 196 } 197 return 0; 198} 199 200/* Return the interface where dhcp is run on */ 201char* get_dhcpc_dev_name(void) 202{ 203 return device; 204} 205 206int 207main(argc, argv) 208 int argc; 209 char **argv; 210{ 211 int ch; 212 char *progname, *conffile = DHCP6C_CONF; 213 char *user_file = DHCP6C_USER_CONF; 214 FILE *pidfp; 215 char *addr; 216 217 pid = getpid(); 218 srandom(time(NULL) & pid); 219 220 if ((progname = strrchr(*argv, '/')) == NULL) 221 progname = *argv; 222 else 223 progname++; 224 225 TAILQ_INIT(&request_list); 226 TAILQ_INIT(&request_prefix_list); 227 228 /* Since 'user class' string may contain any printable char. 229 * The current dhcp6s config file parsing (using yyparse) 230 * can't handle this properly. 231 * So we put user-class string in a separate file, 232 * specified using '-u' option. 233 */ 234 //while ((ch = getopt(argc, argv, "c:r:R:P:dDfI")) != -1) { 235 while ((ch = getopt(argc, argv, "c:u:r:R:P:dDfI")) != -1) { 236 switch (ch) { 237 case 'c': 238 conffile = optarg; 239 break; 240 /* Another config file to store info, such as user-class, etc */ 241 case 'u': 242 user_file = optarg; 243 break; 244 case 'P': 245 client6_request_flag |= CLIENT6_REQUEST_ADDR; 246 for (addr = strtok(optarg, " "); addr; addr = strtok(NULL, " ")) { 247 struct dhcp6_listval *lv; 248 if ((lv = (struct dhcp6_listval *)malloc(sizeof(*lv))) 249 == NULL) { 250 dprintf(LOG_ERR, "failed to allocate memory"); 251 exit(1); 252 } 253 memset(lv, 0, sizeof(*lv)); 254 if (inet_pton(AF_INET6, strtok(addr, "/"), 255 &lv->val_dhcp6addr.addr) < 1) { 256 dprintf(LOG_ERR, 257 "invalid ipv6address for release"); 258 usage(); 259 exit(1); 260 } 261 lv->val_dhcp6addr.type = IAPD; 262 lv->val_dhcp6addr.plen = atoi(strtok(NULL, "/")); 263 lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE; 264 TAILQ_INSERT_TAIL(&request_list, lv, link); 265 } 266 break; 267 268 case 'R': 269 client6_request_flag |= CLIENT6_REQUEST_ADDR; 270 for (addr = strtok(optarg, " "); addr; addr = strtok(NULL, " ")) { 271 struct dhcp6_listval *lv; 272 if ((lv = (struct dhcp6_listval *)malloc(sizeof(*lv))) 273 == NULL) { 274 dprintf(LOG_ERR, "failed to allocate memory"); 275 exit(1); 276 } 277 memset(lv, 0, sizeof(*lv)); 278 if (inet_pton(AF_INET6, addr, &lv->val_dhcp6addr.addr) < 1) { 279 dprintf(LOG_ERR, 280 "invalid ipv6address for release"); 281 usage(); 282 exit(1); 283 } 284 lv->val_dhcp6addr.type = IANA; 285 lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE; 286 TAILQ_INSERT_TAIL(&request_list, lv, link); 287 } 288 break; 289 case 'r': 290 client6_request_flag |= CLIENT6_RELEASE_ADDR; 291 if (strcmp(optarg, "all")) { 292 for (addr = strtok(optarg, " "); addr; 293 addr = strtok(NULL, " ")) { 294 struct dhcp6_listval *lv; 295 if ((lv = (struct dhcp6_listval *)malloc(sizeof(*lv))) 296 == NULL) { 297 dprintf(LOG_ERR, "failed to allocate memory"); 298 exit(1); 299 } 300 memset(lv, 0, sizeof(*lv)); 301 if (inet_pton(AF_INET6, addr, 302 &lv->val_dhcp6addr.addr) < 1) { 303 dprintf(LOG_ERR, 304 "invalid ipv6address for release"); 305 usage(); 306 exit(1); 307 } 308 lv->val_dhcp6addr.type = IANA; 309 TAILQ_INSERT_TAIL(&request_list, lv, link); 310 } 311 } 312 break; 313 case 'I': 314 client6_request_flag |= CLIENT6_INFO_REQ; 315 break; 316 case 'd': 317 debug = 1; 318 break; 319 case 'D': 320 debug = 2; 321 break; 322 case 'f': 323 foreground++; 324 break; 325 default: 326 usage(); 327 exit(0); 328 } 329 } 330 argc -= optind; 331 argv += optind; 332 333 if (argc != 1) { 334 usage(); 335 exit(0); 336 } 337 device = argv[0]; 338 339 if (foreground == 0) { 340 if (daemon(0, 0) < 0) 341 err(1, "daemon"); 342 openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); 343 } 344 setloglevel(debug); 345 346 /* dump current PID */ 347 if ((pidfp = fopen(DHCP6C_PIDFILE, "w")) != NULL) { 348 fprintf(pidfp, "%d\n", pid); 349 fclose(pidfp); 350 } 351 352 ifinit(device); 353 354 /* Parse the second conf file, before reading original config file */ 355 parse_user_file(user_file); 356 357 if ((cfparse(conffile)) != 0) { 358 dprintf(LOG_ERR, "%s" "failed to parse configuration file", 359 FNAME); 360 exit(1); 361 } 362 363 /* Clear SIP and NTP servers params upon restart */ 364 system("nvram set ipv6_sip_servers=\"\""); 365 system("nvram set ipv6_ntp_servers=\"\""); 366 367 client6_init(device); 368 client6_ifinit(device); 369 client6_mainloop(); 370 exit(0); 371} 372 373static void 374usage() 375{ 376 377 fprintf(stderr, 378 "usage: dhcpc [-c configfile] [-r all or (ipv6address ipv6address...)]\n" 379 " [-R (ipv6 address ipv6address...) [-dDIf] interface\n"); 380} 381 382/*------------------------------------------------------------*/ 383 384void 385client6_init(device) 386 char *device; 387{ 388 struct addrinfo hints, *res; 389 static struct sockaddr_in6 sa6_allagent_storage; 390 int error, on = 1; 391 struct dhcp6_if *ifp; 392 int ifidx; 393 char linklocal[64]; 394 struct in6_addr lladdr; 395 396 ifidx = if_nametoindex(device); 397 if (ifidx == 0) { 398 dprintf(LOG_ERR, "if_nametoindex(%s)", device); 399 exit(1); 400 } 401 402 /* get our DUID */ 403 if (get_duid(DUID_FILE, device, &client_duid)) { 404 dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME); 405 exit(1); 406 } 407 if (get_linklocal(device, &lladdr) < 0) { 408 exit(1); 409 } 410 if (inet_ntop(AF_INET6, &lladdr, linklocal, sizeof(linklocal)) < 0) { 411 exit(1); 412 } 413 dprintf(LOG_DEBUG, "link local addr is %s", linklocal); 414 415 memset(&hints, 0, sizeof(hints)); 416 hints.ai_family = PF_INET6; 417 hints.ai_socktype = SOCK_DGRAM; 418 hints.ai_protocol = IPPROTO_UDP; 419 hints.ai_flags = 0; 420 error = getaddrinfo(linklocal, DH6PORT_DOWNSTREAM, &hints, &res); 421 if (error) { 422 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 423 FNAME, strerror(error)); 424 exit(1); 425 } 426 insock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 427 if (insock < 0) { 428 dprintf(LOG_ERR, "%s" "socket(inbound)", FNAME); 429 exit(1); 430 } 431#ifdef IPV6_RECVPKTINFO 432 if (setsockopt(insock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 433 sizeof(on)) < 0) { 434 dprintf(LOG_ERR, "%s" 435 "setsockopt(inbound, IPV6_RECVPKTINFO): %s", 436 FNAME, strerror(errno)); 437 exit(1); 438 } 439#else 440 if (setsockopt(insock, IPPROTO_IPV6, IPV6_PKTINFO, &on, 441 sizeof(on)) < 0) { 442 dprintf(LOG_ERR, "%s" 443 "setsockopt(inbound, IPV6_PKTINFO): %s", 444 FNAME, strerror(errno)); 445 exit(1); 446 } 447#endif 448 ((struct sockaddr_in6 *)(res->ai_addr))->sin6_scope_id = ifidx; 449 dprintf(LOG_DEBUG, "res addr is %s/%d", addr2str(res->ai_addr), res->ai_addrlen); 450 if (bind(insock, res->ai_addr, res->ai_addrlen) < 0) { 451 dprintf(LOG_ERR, "%s" "bind(inbound): %s", 452 FNAME, strerror(errno)); 453 exit(1); 454 } 455 freeaddrinfo(res); 456 457 hints.ai_flags = 0; 458 error = getaddrinfo(linklocal, DH6PORT_UPSTREAM, &hints, &res); 459 if (error) { 460 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 461 FNAME, gai_strerror(error)); 462 exit(1); 463 } 464#if 0 465 outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 466 if (outsock < 0) { 467 dprintf(LOG_ERR, "%s" "socket(outbound): %s", 468 FNAME, strerror(errno)); 469 exit(1); 470 } 471 if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF, 472 &ifidx, sizeof(ifidx)) < 0) { 473 dprintf(LOG_ERR, "%s" 474 "setsockopt(outbound, IPV6_MULTICAST_IF): %s", 475 FNAME, strerror(errno)); 476 exit(1); 477 } 478 ((struct sockaddr_in6 *)(res->ai_addr))->sin6_scope_id = ifidx; 479 if (bind(outsock, res->ai_addr, res->ai_addrlen) < 0) { 480 dprintf(LOG_ERR, "%s" "bind(outbound): %s", 481 FNAME, strerror(errno)); 482 exit(1); 483 } 484#endif 485 freeaddrinfo(res); 486 /* open a socket to watch the off-on link for confirm messages */ 487 if ((nlsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 488 dprintf(LOG_ERR, "%s" "open a socket: %s", 489 FNAME, strerror(errno)); 490 exit(1); 491 } 492 memset(&hints, 0, sizeof(hints)); 493 hints.ai_family = PF_INET6; 494 hints.ai_socktype = SOCK_DGRAM; 495 hints.ai_protocol = IPPROTO_UDP; 496 error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res); 497 if (error) { 498 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 499 FNAME, gai_strerror(error)); 500 exit(1); 501 } 502 memcpy(&sa6_allagent_storage, res->ai_addr, res->ai_addrlen); 503 sa6_allagent = (const struct sockaddr_in6 *)&sa6_allagent_storage; 504 freeaddrinfo(res); 505 506 /* client interface configuration */ 507 if ((ifp = find_ifconfbyname(device)) == NULL) { 508 dprintf(LOG_ERR, "%s" "interface %s not configured", 509 FNAME, device); 510 exit(1); 511 } 512 //ifp->outsock = outsock; 513 514 if (signal(SIGHUP, client6_signal) == SIG_ERR) { 515 dprintf(LOG_WARNING, "%s" "failed to set signal: %s", 516 FNAME, strerror(errno)); 517 exit(1); 518 } 519 if (signal(SIGTERM|SIGKILL, client6_signal) == SIG_ERR) { 520 dprintf(LOG_WARNING, "%s" "failed to set signal: %s", 521 FNAME, strerror(errno)); 522 exit(1); 523 } 524 if (signal(SIGINT, client6_signal) == SIG_ERR) { 525 dprintf(LOG_WARNING, "%s" "failed to set signal: %s", 526 FNAME, strerror(errno)); 527 exit(1); 528 } 529} 530 531static void 532client6_ifinit(char *device) 533{ 534 struct dhcp6_if *ifp = dhcp6_if; 535 struct dhcp6_event *ev; 536 char iaidstr[20]; 537 538 dhcp6_init_iaidaddr(); 539 /* get iaid for each interface */ 540 if (num_device == 0) { 541 if ((num_device = create_iaid(&iaidtab[0], num_device)) < 0) 542 exit(1); 543 /* Per Netgear spec, use 1 as the IAID for IA_NA */ 544 //ifp->iaidinfo.iaid = get_iaid(ifp->ifname, &iaidtab[0], num_device); 545 ifp->iaidinfo.iaid = 1; //get_iaid(ifp->ifname, &iaidtab[0], num_device); 546 if (ifp->iaidinfo.iaid == 0) { 547 dprintf(LOG_DEBUG, "%s" 548 "interface %s iaid failed to be created", 549 FNAME, ifp->ifname); 550 exit(1); 551 } 552 dprintf(LOG_DEBUG, "%s" "interface %s iaid is %u", 553 FNAME, ifp->ifname, ifp->iaidinfo.iaid); 554 } 555 client6_iaidaddr.ifp = ifp; 556 memcpy(&client6_iaidaddr.client6_info.iaidinfo, &ifp->iaidinfo, 557 sizeof(client6_iaidaddr.client6_info.iaidinfo)); 558 duidcpy(&client6_iaidaddr.client6_info.clientid, &client_duid); 559 /* parse the lease file */ 560 strcpy(leasename, PATH_CLIENT6_LEASE); 561 sprintf(iaidstr, "%u", ifp->iaidinfo.iaid); 562 strcat(leasename, iaidstr); 563 if ((client6_lease_file = 564 init_leases(leasename)) == NULL) { 565 dprintf(LOG_ERR, "%s" "failed to parse lease file", FNAME); 566 exit(1); 567 } 568 strcpy(client6_lease_temp, leasename); 569 strcat(client6_lease_temp, "XXXXXX"); 570 client6_lease_file = 571 sync_leases(client6_lease_file, leasename, client6_lease_temp); 572 if (client6_lease_file == NULL) 573 exit(1); 574 if (!TAILQ_EMPTY(&client6_iaidaddr.lease_list)) { 575 struct dhcp6_listval *lv; 576 if (!(client6_request_flag & CLIENT6_REQUEST_ADDR) && 577 !(client6_request_flag & CLIENT6_RELEASE_ADDR)) 578 client6_request_flag |= CLIENT6_CONFIRM_ADDR; 579 if (TAILQ_EMPTY(&request_list)) { 580 if (create_request_list(1) < 0) 581 exit(1); 582 } else if (client6_request_flag & CLIENT6_RELEASE_ADDR) { 583 for (lv = TAILQ_FIRST(&request_list); lv; 584 lv = TAILQ_NEXT(lv, link)) { 585 if (dhcp6_find_lease(&client6_iaidaddr, 586 &lv->val_dhcp6addr) == NULL) { 587 dprintf(LOG_INFO, "this address %s is not" 588 " leased by this client", 589 in6addr2str(&lv->val_dhcp6addr.addr,0)); 590 exit(0); 591 } 592 } 593 } 594 } else if (client6_request_flag & CLIENT6_RELEASE_ADDR) { 595 dprintf(LOG_INFO, "no ipv6 addresses are leased by client"); 596 exit(0); 597 } 598 setup_interface(ifp->ifname); 599 ifp->link_flag |= IFF_RUNNING; 600 601 /* get addrconf prefix from kernel */ 602 (void)get_if_rainfo(ifp); 603 604 /* set up check link timer and sync file timer */ 605 if ((ifp->link_timer = 606 dhcp6_add_timer(check_link_timo, ifp)) < 0) { 607 dprintf(LOG_ERR, "%s" "failed to create a timer", FNAME); 608 exit(1); 609 } 610 if ((ifp->sync_timer = dhcp6_add_timer(check_lease_file_timo, ifp)) < 0) { 611 dprintf(LOG_ERR, "%s" "failed to create a timer", FNAME); 612 exit(1); 613 } 614 /* DAD timer set up after getting the address */ 615 ifp->dad_timer = NULL; 616 /* create an event for the initial delay */ 617 if ((ev = dhcp6_create_event(ifp, DHCP6S_INIT)) == NULL) { 618 dprintf(LOG_ERR, "%s" "failed to create an event", 619 FNAME); 620 exit(1); 621 } 622 ifp->servers = NULL; 623 ev->ifp->current_server = NULL; 624 TAILQ_INSERT_TAIL(&ifp->event_list, ev, link); 625 if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { 626 dprintf(LOG_ERR, "%s" "failed to add a timer for %s", 627 FNAME, ifp->ifname); 628 exit(1); 629 } 630 dhcp6_reset_timer(ev); 631} 632 633static void 634free_resources(struct dhcp6_if *ifp) 635{ 636 struct dhcp6_event *ev, *ev_next; 637 struct dhcp6_lease *sp, *sp_next; 638 struct stat buf; 639 if (client6_iaidaddr.client6_info.type == IAPD && 640 !TAILQ_EMPTY(&client6_iaidaddr.lease_list)) 641 radvd_parse(&client6_iaidaddr, ADDR_REMOVE); 642 else { 643 for (sp = TAILQ_FIRST(&client6_iaidaddr.lease_list); sp; sp = sp_next) { 644 sp_next = TAILQ_NEXT(sp, link); 645 if (client6_ifaddrconf(IFADDRCONF_REMOVE, &sp->lease_addr) != 0) 646 dprintf(LOG_INFO, "%s" "deconfiging address %s failed", 647 FNAME, in6addr2str(&sp->lease_addr.addr, 0)); 648 } 649 } 650 dprintf(LOG_DEBUG, "%s" " remove all events on interface", FNAME); 651 /* cancel all outstanding events for each interface */ 652 for (ev = TAILQ_FIRST(&ifp->event_list); ev; ev = ev_next) { 653 ev_next = TAILQ_NEXT(ev, link); 654 dhcp6_remove_event(ev); 655 } 656 { 657 /* restore /etc/radv.conf.bak back to /etc/radvd.conf */ 658 if (!lstat(RADVD_CONF_BAK_FILE, &buf)) 659 rename(RADVD_CONF_BAK_FILE, RADVD_CONF_FILE); 660 /* restore /etc/resolv.conf.dhcpv6.bak back to /etc/resolv.conf */ 661 if (!lstat(RESOLV_CONF_BAK_FILE, &buf)) { 662 if (rename(RESOLV_CONF_BAK_FILE, RESOLV_CONF_FILE)) 663 dprintf(LOG_ERR, "%s" " failed to backup resolv.conf", FNAME); 664 } 665 } 666 free_servers(ifp); 667} 668 669static void 670process_signals() 671{ 672 if ((sig_flags & SIGF_TERM)) { 673 dprintf(LOG_INFO, FNAME "exiting"); 674 675 /* Release IANA/IAPD before exiting */ 676 create_request_list(0); 677 client6_send_newstate(dhcp6_if, DHCP6S_RELEASE); 678 679 free_resources(dhcp6_if); 680 unlink(DHCP6C_PIDFILE); 681 exit(0); 682 } 683 if ((sig_flags & SIGF_HUP)) { 684 dprintf(LOG_INFO, FNAME "restarting"); 685 686 /* Release IANA/IAPD before restarting */ 687 create_request_list(0); 688 client6_send_newstate(dhcp6_if, DHCP6S_RELEASE); 689 690 free_resources(dhcp6_if); 691 client6_ifinit(device); 692 } 693 if ((sig_flags & SIGF_CLEAN)) { 694 695 /* Release IANA/IAPD before exiting */ 696 create_request_list(0); 697 client6_send_newstate(dhcp6_if, DHCP6S_RELEASE); 698 699 free_resources(dhcp6_if); 700 exit(0); 701 } 702 sig_flags = 0; 703} 704 705static void 706client6_mainloop() 707{ 708 struct timeval *w; 709 int ret; 710 fd_set r; 711 712 while(1) { 713 if (sig_flags) 714 process_signals(); 715 w = dhcp6_check_timer(); 716 717 FD_ZERO(&r); 718 FD_SET(insock, &r); 719 720 ret = select(insock + 1, &r, NULL, NULL, w); 721 switch (ret) { 722 case -1: 723 if (errno != EINTR) { 724 dprintf(LOG_ERR, "%s" "select: %s", 725 FNAME, strerror(errno)); 726 exit(1); 727 } 728 break; 729 case 0: /* timeout */ 730 break; /* dhcp6_check_timer() will treat the case */ 731 default: /* received a packet */ 732 client6_recv(); 733 } 734 } 735} 736 737struct dhcp6_timer * 738client6_timo(arg) 739 void *arg; 740{ 741 struct dhcp6_event *ev = (struct dhcp6_event *)arg; 742 struct dhcp6_if *ifp; 743 struct timeval now; 744 745 ifp = ev->ifp; 746 ev->timeouts++; 747 gettimeofday(&now, NULL); 748 if ((ev->max_retrans_cnt && ev->timeouts >= ev->max_retrans_cnt) || 749 (ev->max_retrans_dur && (now.tv_sec - ev->start_time.tv_sec) 750 >= ev->max_retrans_dur)) { 751 dprintf(LOG_INFO, "%s" "no responses were received", FNAME); 752 753 /* WNR3500L TD170, after multiple re-transmit of REQUEST 754 * message, if we did not receive a valid response, 755 * go back to SOLICIT state */ 756 if (ev->state == DHCP6S_REQUEST) { 757 ev->timeouts = 0; 758 free_servers(ifp); 759 ev->state = DHCP6S_SOLICIT; 760 dhcp6_set_timeoparam(ev); 761 client6_send(ev); 762 dhcp6_reset_timer(ev); 763 return (ev->timer); 764 } 765 766 dhcp6_remove_event(ev); 767 return (NULL); 768 } 769 770 switch(ev->state) { 771 case DHCP6S_INIT: 772 /* From INIT state client could 773 * go to CONFIRM state if the client reboots; 774 * go to RELEASE state if the client issues a release; 775 * go to INFOREQ state if the client requests info-only; 776 * go to SOLICIT state if the client requests addresses; 777 */ 778 ev->timeouts = 0; /* indicate to generate a new XID. */ 779 /* 780 * three cases client send information request: 781 * 1. configuration file includes information-only 782 * 2. command line includes -I 783 * 3. check interface flags if managed bit isn't set and 784 * if otherconf bit set by RA 785 * and information-only, conmand line -I are not set. 786 */ 787 if ((ifp->send_flags & DHCIFF_INFO_ONLY) || 788 789 /* Ignore the "M" and "O" flags in RA. Just send DHCP solicit */ 790 (client6_request_flag & CLIENT6_INFO_REQ) /* || 791 (!(ifp->ra_flag & IF_RA_MANAGED) && 792 (ifp->ra_flag & IF_RA_OTHERCONF))*/ ) 793 794 ev->state = DHCP6S_INFOREQ; 795 else if (client6_request_flag & CLIENT6_RELEASE_ADDR) 796 /* do release */ 797 ev->state = DHCP6S_RELEASE; 798 else if (client6_request_flag & CLIENT6_CONFIRM_ADDR) { 799 struct dhcp6_listval *lv; 800 /* do confirm for reboot for IANA, IATA*/ 801 if (client6_iaidaddr.client6_info.type == IAPD) 802 ev->state = DHCP6S_REBIND; 803 else 804 ev->state = DHCP6S_CONFIRM; 805 for (lv = TAILQ_FIRST(&request_list); lv; 806 lv = TAILQ_NEXT(lv, link)) { 807 lv->val_dhcp6addr.preferlifetime = 0; 808 lv->val_dhcp6addr.validlifetime = 0; 809 } 810 } else 811 ev->state = DHCP6S_SOLICIT; 812 dhcp6_set_timeoparam(ev); 813 814 /* In auto-detect mode, we only send the SOLICIT messages 815 * 3 times. 816 */ 817 if (ifp->send_flags & DHCIFF_SOLICIT_ONLY) { 818 ev->max_retrans_cnt = SOL_MAX_RC_AUTODETECT; 819 dprintf(LOG_ERR, "%s" "Set SOLICIT message to %d times", 820 FNAME, SOL_MAX_RC_AUTODETECT); 821 } 822 823 case DHCP6S_SOLICIT: 824 if (ifp->servers) { 825 ifp->current_server = select_server(ifp); 826 if (ifp->current_server == NULL) { 827 /* this should not happen! */ 828 dprintf(LOG_ERR, "%s" "can't find a server", 829 FNAME); 830 exit(1); 831 } 832 /* if get the address assginment break */ 833 if (!TAILQ_EMPTY(&client6_iaidaddr.lease_list)) { 834 dhcp6_remove_event(ev); 835 return (NULL); 836 } 837 ev->timeouts = 0; 838 ev->state = DHCP6S_REQUEST; 839 dhcp6_set_timeoparam(ev); 840 } 841 /* In auto-detect mode, we need to send two SOLICIT 842 * messages, one with IANA+IAPD, one with IAPD only. 843 */ 844 if (ifp->send_flags & DHCIFF_SOLICIT_ONLY) { 845 client6_send(ev); 846 ifp->send_flags |= DHCIFF_PREFIX_DELEGATION; 847 client6_send(ev); 848 ifp->send_flags &=~ DHCIFF_PREFIX_DELEGATION; 849 break; /* don't fall through */ 850 } 851 //case DHCP6S_INFOREQ: 852 case DHCP6S_REQUEST: 853 client6_send(ev); 854 break; 855 856 /* Use different function to send INFO-REQ */ 857 case DHCP6S_INFOREQ: 858 client6_send_info_req(ev); 859 break; 860 861 case DHCP6S_RELEASE: 862 case DHCP6S_DECLINE: 863 case DHCP6S_CONFIRM: 864 case DHCP6S_RENEW: 865 case DHCP6S_REBIND: 866 if (!TAILQ_EMPTY(&request_list)) 867 client6_send(ev); 868 else { 869 dprintf(LOG_INFO, "%s" 870 "all information to be updated were canceled", 871 FNAME); 872 dhcp6_remove_event(ev); 873 return (NULL); 874 } 875 break; 876 default: 877 break; 878 } 879 dhcp6_reset_timer(ev); 880 return (ev->timer); 881} 882 883static struct dhcp6_serverinfo * 884select_server(ifp) 885 struct dhcp6_if *ifp; 886{ 887 struct dhcp6_serverinfo *s; 888 889 for (s = ifp->servers; s; s = s->next) { 890 if (s->active) { 891 dprintf(LOG_DEBUG, "%s" "picked a server (ID: %s)", 892 FNAME, duidstr(&s->optinfo.serverID)); 893 return (s); 894 } 895 } 896 897 return (NULL); 898} 899 900static void 901client6_signal(sig) 902 int sig; 903{ 904 905 dprintf(LOG_INFO, FNAME "received a signal (%d)", sig); 906 907 switch (sig) { 908 case SIGTERM: 909 sig_flags |= SIGF_TERM; 910 break; 911 case SIGHUP: 912 sig_flags |= SIGF_HUP; 913 break; 914 case SIGINT: 915 case SIGKILL: 916 sig_flags |= SIGF_CLEAN; 917 break; 918 default: 919 break; 920 } 921} 922 923void 924client6_send(ev) 925 struct dhcp6_event *ev; 926{ 927 struct dhcp6_if *ifp; 928 char buf[BUFSIZ]; 929 struct sockaddr_in6 dst; 930 struct dhcp6 *dh6; 931 struct dhcp6_optinfo optinfo; 932 ssize_t optlen, len; 933 struct timeval duration, now; 934 935 ifp = ev->ifp; 936 937 dh6 = (struct dhcp6 *)buf; 938 memset(dh6, 0, sizeof(*dh6)); 939 940 switch(ev->state) { 941 case DHCP6S_SOLICIT: 942 dh6->dh6_msgtype = DH6_SOLICIT; 943 break; 944 case DHCP6S_REQUEST: 945 if (ifp->current_server == NULL) { 946 dprintf(LOG_ERR, "%s" "assumption failure", FNAME); 947 exit(1); 948 } 949 dh6->dh6_msgtype = DH6_REQUEST; 950 break; 951 case DHCP6S_RENEW: 952 if (ifp->current_server == NULL) { 953 dprintf(LOG_ERR, "%s" "assumption failure", FNAME); 954 exit(1); 955 } 956 dh6->dh6_msgtype = DH6_RENEW; 957 break; 958 case DHCP6S_DECLINE: 959 if (ifp->current_server == NULL) { 960 dprintf(LOG_ERR, "%s" "assumption failure", FNAME); 961 exit(1); 962 } 963 dh6->dh6_msgtype = DH6_DECLINE; 964 break; 965 case DHCP6S_INFOREQ: 966 dh6->dh6_msgtype = DH6_INFORM_REQ; 967 break; 968 case DHCP6S_REBIND: 969 dh6->dh6_msgtype = DH6_REBIND; 970 break; 971 case DHCP6S_CONFIRM: 972 dh6->dh6_msgtype = DH6_CONFIRM; 973 break; 974 case DHCP6S_RELEASE: 975 dh6->dh6_msgtype = DH6_RELEASE; 976 break; 977 default: 978 dprintf(LOG_ERR, "%s" "unexpected state %d", FNAME, ev->state); 979 exit(1); 980 } 981 /* 982 * construct options 983 */ 984 dhcp6_init_options(&optinfo); 985 if (ev->timeouts == 0) { 986 gettimeofday(&ev->start_time, NULL); 987 optinfo.elapsed_time = 0; 988 /* 989 * A client SHOULD generate a random number that cannot easily 990 * be guessed or predicted to use as the transaction ID for 991 * each new message it sends. 992 * 993 * A client MUST leave the transaction-ID unchanged in 994 * retransmissions of a message. [dhcpv6-26 15.1] 995 */ 996 ev->xid = random() & DH6_XIDMASK; 997 998 /* For Testing purposes !!! */ 999 if (ev->state == DHCP6S_SOLICIT && xid_solicit) { 1000 dprintf(LOG_DEBUG, "%s" 1001 "**TESTING** Use user-defined xid_sol %lu", FNAME, xid_solicit); 1002 ev->xid = xid_solicit & DH6_XIDMASK; 1003 } else if (ev->state == DHCP6S_REQUEST && xid_request) { 1004 dprintf(LOG_DEBUG, "%s" 1005 "**TESTING** Use user-defined xid_req %lu", FNAME, xid_request); 1006 ev->xid = xid_request & DH6_XIDMASK; 1007 } 1008 1009 dprintf(LOG_DEBUG, "%s" "ifp %p event %p a new XID (%x) is generated", 1010 FNAME, ifp, ev, ev->xid); 1011 } else { 1012 unsigned int etime; 1013 gettimeofday(&now, NULL); 1014 timeval_sub(&now, &(ev->start_time), &duration); 1015 optinfo.elapsed_time = 1016 etime = (duration.tv_sec) * 100 + (duration.tv_usec) / 10000; 1017 if (etime > DHCP6_ELAPSEDTIME_MAX) 1018 optinfo.elapsed_time = DHCP6_ELAPSEDTIME_MAX; 1019 else 1020 optinfo.elapsed_time = etime; 1021 } 1022 dh6->dh6_xid &= ~ntohl(DH6_XIDMASK); 1023 dh6->dh6_xid |= htonl(ev->xid); 1024 len = sizeof(*dh6); 1025 1026 1027 /* server ID */ 1028 switch(ev->state) { 1029 case DHCP6S_REQUEST: 1030 case DHCP6S_RENEW: 1031 case DHCP6S_DECLINE: 1032 if (&ifp->current_server->optinfo == NULL) 1033 exit(1); 1034 dprintf(LOG_DEBUG, "current server ID %s", 1035 duidstr(&ifp->current_server->optinfo.serverID)); 1036 if (duidcpy(&optinfo.serverID, 1037 &ifp->current_server->optinfo.serverID)) { 1038 dprintf(LOG_ERR, "%s" "failed to copy server ID", 1039 FNAME); 1040 goto end; 1041 } 1042 break; 1043 case DHCP6S_RELEASE: 1044 if (duidcpy(&optinfo.serverID, &client6_iaidaddr.client6_info.serverid)) { 1045 dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME); 1046 goto end; 1047 } 1048 break; 1049 } 1050 /* client ID */ 1051 if (duidcpy(&optinfo.clientID, &client_duid)) { 1052 dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME); 1053 goto end; 1054 } 1055 1056 /* User-class */ 1057 strcpy(optinfo.user_class, ifp->user_class); 1058 1059 /* option request options */ 1060 /* DHCPv6 readylogo: DHCP confirm message should not 1061 * have request for DNS/Domain list. 1062 */ 1063 if (ev->state != DHCP6S_CONFIRM) 1064 if (dhcp6_copy_list(&optinfo.reqopt_list, &ifp->reqopt_list)) { 1065 dprintf(LOG_ERR, "%s" "failed to copy requested options", 1066 FNAME); 1067 goto end; 1068 } 1069 1070 switch(ev->state) { 1071 case DHCP6S_SOLICIT: 1072 /* rapid commit */ 1073 if (ifp->send_flags & DHCIFF_RAPID_COMMIT) 1074 optinfo.flags |= DHCIFF_RAPID_COMMIT; 1075 if (!(ifp->send_flags & DHCIFF_INFO_ONLY) || 1076 (client6_request_flag & CLIENT6_REQUEST_ADDR)) { 1077 memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo, 1078 sizeof(optinfo.iaidinfo)); 1079 if (ifp->send_flags & DHCIFF_PREFIX_DELEGATION) 1080 optinfo.type = IAPD; 1081 else if (ifp->send_flags & DHCIFF_TEMP_ADDRS) 1082 optinfo.type = IATA; 1083 else 1084 optinfo.type = IANA; 1085 } 1086 /* support for client preferred ipv6 address */ 1087 if (client6_request_flag & CLIENT6_REQUEST_ADDR) { 1088 if (dhcp6_copy_list(&optinfo.addr_list, &request_list)) 1089 goto end; 1090 } 1091 /* In auto-detect mode, we need to send two SOLICIT 1092 * messages. 2nd one has IAPD only. 1093 */ 1094 if ((ifp->send_flags & DHCIFF_SOLICIT_ONLY) && 1095 (ifp->send_flags & DHCIFF_PREFIX_DELEGATION)) { 1096 optinfo.type = IAPD; 1097 } 1098 break; 1099 case DHCP6S_REQUEST: 1100 if (!(ifp->send_flags & DHCIFF_INFO_ONLY)) { 1101 /* Should copy 'current_server's addr info */ 1102#if 0 1103 memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo, 1104 sizeof(optinfo.iaidinfo)); 1105#endif 1106 memcpy(&optinfo.iaidinfo, &(ifp->current_server->optinfo.iaidinfo), 1107 sizeof(optinfo.iaidinfo)); 1108 if (dhcp6_copy_list(&optinfo.addr_list, &(ifp->current_server->optinfo.addr_list))) 1109 goto end; 1110 if (dhcp6_copy_list(&optinfo.prefix_list, 1111 &(ifp->current_server->optinfo.prefix_list))) 1112 goto end; 1113 dprintf(LOG_DEBUG, "%s IAID is %u", FNAME, optinfo.iaidinfo.iaid); 1114 if (ifp->send_flags & DHCIFF_TEMP_ADDRS) 1115 optinfo.type = IATA; 1116 else if (ifp->send_flags & DHCIFF_PREFIX_DELEGATION) 1117 optinfo.type = IAPD; 1118 else 1119 optinfo.type = IANA; 1120 } 1121 break; 1122 case DHCP6S_RENEW: 1123 case DHCP6S_REBIND: 1124 case DHCP6S_RELEASE: 1125 case DHCP6S_CONFIRM: 1126 case DHCP6S_DECLINE: 1127 memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo, 1128 sizeof(optinfo.iaidinfo)); 1129 optinfo.type = client6_iaidaddr.client6_info.type; 1130 if (ev->state == DHCP6S_CONFIRM) { 1131 optinfo.iaidinfo.renewtime = 0; 1132 optinfo.iaidinfo.rebindtime = 0; 1133 } 1134 if (!TAILQ_EMPTY(&request_list)) { 1135 if (dhcp6_copy_list(&optinfo.addr_list, &request_list)) 1136 goto end; 1137 } else { 1138 if (ev->state == DHCP6S_RELEASE) { 1139 dprintf(LOG_INFO, "release empty address list"); 1140 exit(1); 1141 } 1142 } 1143 /* PUt the IAPD prefix in the DHCP packet */ 1144 if (!TAILQ_EMPTY(&request_prefix_list)) { 1145 if (dhcp6_copy_list(&optinfo.prefix_list, &request_prefix_list)) 1146 goto end; 1147 } 1148 if (client6_request_flag & CLIENT6_RELEASE_ADDR) { 1149 if (dhcp6_update_iaidaddr(&optinfo, ADDR_REMOVE)) { 1150 dprintf(LOG_INFO, "client release failed"); 1151 exit(1); 1152 } 1153 if (client6_iaidaddr.client6_info.type == IAPD) 1154 radvd_parse(&client6_iaidaddr, ADDR_REMOVE); 1155 } 1156 break; 1157 default: 1158 break; 1159 } 1160 /* set options in the message */ 1161 if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1), 1162 (struct dhcp6opt *)(buf + sizeof(buf)), 1163 &optinfo)) < 0) { 1164 dprintf(LOG_INFO, "%s" "failed to construct options", FNAME); 1165 goto end; 1166 } 1167 len += optlen; 1168 1169 /* 1170 * Unless otherwise specified, a client sends DHCP messages to the 1171 * All_DHCP_Relay_Agents_and_Servers or the DHCP_Anycast address. 1172 * [dhcpv6-26 Section 13.] 1173 * Our current implementation always follows the case. 1174 */ 1175 switch(ev->state) { 1176 case DHCP6S_REQUEST: 1177 case DHCP6S_RENEW: 1178 case DHCP6S_DECLINE: 1179 case DHCP6S_RELEASE: 1180 if (ifp->current_server && 1181 !IN6_IS_ADDR_UNSPECIFIED(&ifp->current_server->server_addr)) { 1182 struct addrinfo hints, *res; 1183 int error; 1184 memset(&hints, 0, sizeof(hints)); 1185 hints.ai_family = PF_INET6; 1186 hints.ai_socktype = SOCK_DGRAM; 1187 hints.ai_protocol = IPPROTO_UDP; 1188 error = getaddrinfo(in6addr2str(&ifp->current_server->server_addr,0), 1189 DH6PORT_UPSTREAM, &hints, &res); 1190 if (error) { 1191 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 1192 FNAME, gai_strerror(error)); 1193 exit(1); 1194 } 1195 memcpy(&dst, res->ai_addr, res->ai_addrlen); 1196 break; 1197 } 1198 default: 1199 dst = *sa6_allagent; 1200 break; 1201 } 1202 dst.sin6_scope_id = ifp->linkid; 1203 dprintf(LOG_DEBUG, "send dst if %s addr is %s scope id is %d", 1204 ifp->ifname, addr2str((struct sockaddr *)&dst), ifp->linkid); 1205 /* why use 'outsock' here? */ 1206 //if (sendto(ifp->outsock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst, 1207 if (sendto(insock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst, 1208 sizeof(dst)) == -1) { 1209 dprintf(LOG_ERR, FNAME "transmit failed: %s", strerror(errno)); 1210 goto end; 1211 } 1212 1213 dprintf(LOG_DEBUG, "%s" "send %s to %s", FNAME, 1214 dhcp6msgstr(dh6->dh6_msgtype), 1215 addr2str((struct sockaddr *)&dst)); 1216 1217 end: 1218 dhcp6_clear_options(&optinfo); 1219 return; 1220} 1221 1222void 1223client6_send_info_req(ev) 1224 struct dhcp6_event *ev; 1225{ 1226 struct dhcp6_if *ifp; 1227 char buf[BUFSIZ]; 1228 struct sockaddr_in6 dst; 1229 struct dhcp6 *dh6; 1230 struct dhcp6_optinfo optinfo; 1231 ssize_t optlen, len; 1232 struct timeval duration, now; 1233 1234 ifp = ev->ifp; 1235 1236 dh6 = (struct dhcp6 *)buf; 1237 memset(dh6, 0, sizeof(*dh6)); 1238 1239 dh6->dh6_msgtype = DH6_INFORM_REQ; 1240 1241 /* 1242 * construct options 1243 */ 1244 dhcp6_init_options(&optinfo); 1245 if (ev->timeouts == 0) { 1246 gettimeofday(&ev->start_time, NULL); 1247 optinfo.elapsed_time = 0; 1248 /* 1249 * A client SHOULD generate a random number that cannot easily 1250 * be guessed or predicted to use as the transaction ID for 1251 * each new message it sends. 1252 * 1253 * A client MUST leave the transaction-ID unchanged in 1254 * retransmissions of a message. [dhcpv6-26 15.1] 1255 */ 1256 ev->xid = random() & DH6_XIDMASK; 1257 dprintf(LOG_DEBUG, "%s" "ifp %p event %p a new XID (%x) is generated", 1258 FNAME, ifp, ev, ev->xid); 1259 } else { 1260 unsigned int etime; 1261 gettimeofday(&now, NULL); 1262 timeval_sub(&now, &(ev->start_time), &duration); 1263 optinfo.elapsed_time = 1264 etime = (duration.tv_sec) * 100 + (duration.tv_usec) / 10000; 1265 if (etime > DHCP6_ELAPSEDTIME_MAX) 1266 optinfo.elapsed_time = DHCP6_ELAPSEDTIME_MAX; 1267 else 1268 optinfo.elapsed_time = etime; 1269 } 1270 dh6->dh6_xid &= ~ntohl(DH6_XIDMASK); 1271 dh6->dh6_xid |= htonl(ev->xid); 1272 len = sizeof(*dh6); 1273 1274 /* client ID */ 1275 if (duidcpy(&optinfo.clientID, &client_duid)) { 1276 dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME); 1277 goto end; 1278 } 1279 1280 /* User-class */ 1281 strcpy(optinfo.user_class, ifp->user_class); 1282 1283 /* option request options */ 1284 if (dhcp6_copy_list(&optinfo.reqopt_list, &ifp->reqopt_list)) { 1285 dprintf(LOG_ERR, "%s" "failed to copy requested options", 1286 FNAME); 1287 goto end; 1288 } 1289 1290 /* set options in the message */ 1291 if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1), 1292 (struct dhcp6opt *)(buf + sizeof(buf)), 1293 &optinfo)) < 0) { 1294 dprintf(LOG_INFO, "%s" "failed to construct options", FNAME); 1295 goto end; 1296 } 1297 len += optlen; 1298 1299 /* Special hack to add SIP server and NTP server options */ 1300 buf[len-3] += 4; 1301 buf[len++] = 0; 1302 buf[len++] = DH6OPT_SIP_SERVERS; 1303 buf[len++] = 0; 1304 buf[len++] = DH6OPT_NTP_SERVERS; 1305 1306 /* 1307 * Unless otherwise specified, a client sends DHCP messages to the 1308 * All_DHCP_Relay_Agents_and_Servers or the DHCP_Anycast address. 1309 * [dhcpv6-26 Section 13.] 1310 * Our current implementation always follows the case. 1311 */ 1312 switch(ev->state) { 1313 case DHCP6S_REQUEST: 1314 case DHCP6S_RENEW: 1315 case DHCP6S_DECLINE: 1316 case DHCP6S_RELEASE: 1317 if (ifp->current_server && 1318 !IN6_IS_ADDR_UNSPECIFIED(&ifp->current_server->server_addr)) { 1319 struct addrinfo hints, *res; 1320 int error; 1321 memset(&hints, 0, sizeof(hints)); 1322 hints.ai_family = PF_INET6; 1323 hints.ai_socktype = SOCK_DGRAM; 1324 hints.ai_protocol = IPPROTO_UDP; 1325 error = getaddrinfo(in6addr2str(&ifp->current_server->server_addr,0), 1326 DH6PORT_UPSTREAM, &hints, &res); 1327 if (error) { 1328 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 1329 FNAME, gai_strerror(error)); 1330 exit(1); 1331 } 1332 memcpy(&dst, res->ai_addr, res->ai_addrlen); 1333 break; 1334 } 1335 default: 1336 dst = *sa6_allagent; 1337 break; 1338 } 1339 dst.sin6_scope_id = ifp->linkid; 1340 dprintf(LOG_DEBUG, "send dst if %s addr is %s scope id is %d", 1341 ifp->ifname, addr2str((struct sockaddr *)&dst), ifp->linkid); 1342 /* why use 'outsock' here? */ 1343 //if (sendto(ifp->outsock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst, 1344 if (sendto(insock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst, 1345 sizeof(dst)) == -1) { 1346 dprintf(LOG_ERR, FNAME "transmit failed: %s", strerror(errno)); 1347 goto end; 1348 } 1349 1350 dprintf(LOG_DEBUG, "%s" "send %s to %s", FNAME, 1351 dhcp6msgstr(dh6->dh6_msgtype), 1352 addr2str((struct sockaddr *)&dst)); 1353 1354 end: 1355 dhcp6_clear_options(&optinfo); 1356 return; 1357} 1358 1359static void 1360client6_recv() 1361{ 1362 char rbuf[BUFSIZ], cmsgbuf[BUFSIZ]; 1363 struct msghdr mhdr; 1364 struct iovec iov; 1365 struct sockaddr_storage from; 1366 struct dhcp6_if *ifp; 1367 struct dhcp6opt *p, *ep; 1368 struct dhcp6_optinfo optinfo; 1369 ssize_t len; 1370 struct dhcp6 *dh6; 1371 struct cmsghdr *cm; 1372 struct in6_pktinfo *pi = NULL; 1373 1374 memset(&iov, 0, sizeof(iov)); 1375 memset(&mhdr, 0, sizeof(mhdr)); 1376 1377 iov.iov_base = (caddr_t)rbuf; 1378 iov.iov_len = sizeof(rbuf); 1379 mhdr.msg_name = (caddr_t)&from; 1380 mhdr.msg_namelen = sizeof(from); 1381 mhdr.msg_iov = &iov; 1382 mhdr.msg_iovlen = 1; 1383 mhdr.msg_control = (caddr_t)cmsgbuf; 1384 mhdr.msg_controllen = sizeof(cmsgbuf); 1385 if ((len = recvmsg(insock, &mhdr, 0)) < 0) { 1386 dprintf(LOG_ERR, "%s" "recvmsg: %s", FNAME, strerror(errno)); 1387 return; 1388 } 1389 1390 /* detect receiving interface */ 1391 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm; 1392 cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) { 1393 if (cm->cmsg_level == IPPROTO_IPV6 && 1394 cm->cmsg_type == IPV6_PKTINFO && 1395 cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 1396 pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 1397 } 1398 } 1399 if (pi == NULL) { 1400 dprintf(LOG_NOTICE, "%s" "failed to get packet info", FNAME); 1401 return; 1402 } 1403 if ((ifp = find_ifconfbyid(pi->ipi6_ifindex)) == NULL) { 1404 dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME, 1405 (unsigned int)pi->ipi6_ifindex); 1406 return; 1407 } 1408 dprintf(LOG_DEBUG, "receive packet info ifname %s, addr is %s scope id is %d", 1409 ifp->ifname, in6addr2str(&pi->ipi6_addr, 0), pi->ipi6_ifindex); 1410 dh6 = (struct dhcp6 *)rbuf; 1411 1412 dprintf(LOG_DEBUG, "%s" "receive %s from %s scope id %d %s", FNAME, 1413 dhcp6msgstr(dh6->dh6_msgtype), 1414 addr2str((struct sockaddr *)&from), 1415 ((struct sockaddr_in6 *)&from)->sin6_scope_id, 1416 ifp->ifname); 1417 1418 /* get options */ 1419 dhcp6_init_options(&optinfo); 1420 p = (struct dhcp6opt *)(dh6 + 1); 1421 ep = (struct dhcp6opt *)((char *)dh6 + len); 1422 1423 /* Pass some extra arguments to 'dhcp6_get_options' 1424 * to better determine whether this packet is ok or not. 1425 */ 1426 struct dhcp6_event *ev; 1427 ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); 1428 if (ev == NULL) { 1429 dprintf(LOG_INFO, "%s" "XID mismatch", FNAME); 1430 return; 1431 } 1432 if (dhcp6_get_options(p, ep, &optinfo, dh6->dh6_msgtype, 1433 ev->state, ifp->send_flags) < 0) { 1434 //if (dhcp6_get_options(p, ep, &optinfo) < 0) { 1435 dprintf(LOG_INFO, "%s" "failed to parse options", FNAME); 1436#ifdef TEST 1437 return; 1438#endif 1439 } 1440 1441 switch(dh6->dh6_msgtype) { 1442 case DH6_ADVERTISE: 1443 (void)client6_recvadvert(ifp, dh6, len, &optinfo); 1444 break; 1445 case DH6_REPLY: 1446 (void)client6_recvreply(ifp, dh6, len, &optinfo); 1447 break; 1448 default: 1449 dprintf(LOG_INFO, "%s" "received an unexpected message (%s) " 1450 "from %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype), 1451 addr2str((struct sockaddr *)&from)); 1452 break; 1453 } 1454 1455 dhcp6_clear_options(&optinfo); 1456 return; 1457} 1458 1459static int 1460client6_recvadvert(ifp, dh6, len, optinfo0) 1461 struct dhcp6_if *ifp; 1462 struct dhcp6 *dh6; 1463 ssize_t len; 1464 struct dhcp6_optinfo *optinfo0; 1465{ 1466 struct dhcp6_serverinfo *newserver; 1467 struct dhcp6_event *ev; 1468 struct dhcp6_listval *lv; 1469 1470 /* find the corresponding event based on the received xid */ 1471 ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); 1472 if (ev == NULL) { 1473 dprintf(LOG_INFO, "%s" "XID mismatch", FNAME); 1474 return -1; 1475 } 1476 /* if server policy doesn't allow rapid commit 1477 if (ev->state != DHCP6S_SOLICIT || 1478 (ifp->send_flags & DHCIFF_RAPID_COMMIT)) { 1479 */ 1480 if (ev->state != DHCP6S_SOLICIT) { 1481 dprintf(LOG_INFO, "%s" "unexpected advertise", FNAME); 1482 return -1; 1483 } 1484 1485 /* packet validation based on Section 15.3 of dhcpv6-26. */ 1486 if (optinfo0->serverID.duid_len == 0) { 1487 dprintf(LOG_INFO, "%s" "no server ID option", FNAME); 1488 return -1; 1489 } else { 1490 dprintf(LOG_DEBUG, "%s" "server ID: %s, pref=%2x", FNAME, 1491 duidstr(&optinfo0->serverID), 1492 optinfo0->pref); 1493 } 1494 if (optinfo0->clientID.duid_len == 0) { 1495 dprintf(LOG_INFO, "%s" "no client ID option", FNAME); 1496 return -1; 1497 } 1498 if (duidcmp(&optinfo0->clientID, &client_duid)) { 1499 dprintf(LOG_INFO, "%s" "client DUID mismatch", FNAME); 1500 return -1; 1501 } 1502 1503 /* 1504 * The client MUST ignore any Advertise message that includes a Status 1505 * Code option containing any error. 1506 */ 1507 for (lv = TAILQ_FIRST(&optinfo0->stcode_list); lv; 1508 lv = TAILQ_NEXT(lv, link)) { 1509 dprintf(LOG_INFO, "%s" "status code: %s", 1510 FNAME, dhcp6_stcodestr(lv->val_num)); 1511 if (lv->val_num != DH6OPT_STCODE_SUCCESS) { 1512 return (-1); 1513 } 1514 } 1515 1516 /* ignore the server if it is known */ 1517 if (find_server(ifp, &optinfo0->serverID)) { 1518 dprintf(LOG_INFO, "%s" "duplicated server (ID: %s)", 1519 FNAME, duidstr(&optinfo0->serverID)); 1520 return -1; 1521 } 1522 1523 /* In Ipv6 auto mode, write result to a file */ 1524 if (ifp->send_flags & DHCIFF_SOLICIT_ONLY) { 1525 FILE *fp = NULL; 1526 /* For auto-detect mode, if recv ADVERTISE mesg with 1527 * IAPD-only, write to different file. 1528 */ 1529 if (optinfo0->type == IAPD) 1530 fp = fopen("/tmp/wan_dhcp6c_iapd", "w"); 1531 else 1532 fp = fopen("/tmp/wan_dhcp6c", "w"); 1533 if (fp) { 1534 fprintf(fp, "1"); 1535 fclose(fp); 1536 } 1537 return 0; 1538 } 1539 1540 newserver = allocate_newserver(ifp, optinfo0); 1541 if (newserver == NULL) 1542 return (-1); 1543 1544 /* for some reason, 'allocate_newserver' did not copy 1545 * the IAID info. So we do it here... 1546 */ 1547 memcpy(&(newserver->optinfo.iaidinfo), 1548 &(optinfo0->iaidinfo), 1549 sizeof(struct dhcp6_iaid_info)); 1550 1551 /* if the server has an extremely high preference, just use it. */ 1552 if (newserver->pref == DH6OPT_PREF_MAX) { 1553 ev->timeouts = 0; 1554 ev->state = DHCP6S_REQUEST; 1555 ifp->current_server = newserver; 1556 dhcp6_set_timeoparam(ev); 1557 dhcp6_reset_timer(ev); 1558 client6_send(ev); 1559 1560 } else if (ifp->servers->next == NULL) { 1561 struct timeval *rest, elapsed, tv_rt, tv_irt, timo; 1562 1563 rest = dhcp6_timer_rest(ev->timer); 1564 tv_rt.tv_sec = (ev->retrans * 1000) / 1000000; 1565 tv_rt.tv_usec = (ev->retrans * 1000) % 1000000; 1566 tv_irt.tv_sec = (ev->init_retrans * 1000) / 1000000; 1567 tv_irt.tv_usec = (ev->init_retrans * 1000) % 1000000; 1568 timeval_sub(&tv_rt, rest, &elapsed); 1569 if (TIMEVAL_LEQ(elapsed, tv_irt)) 1570 timeval_sub(&tv_irt, &elapsed, &timo); 1571 else 1572 timo.tv_sec = timo.tv_usec = 0; 1573 1574 dprintf(LOG_DEBUG, "%s" "reset timer for %s to %d.%06d", 1575 FNAME, ifp->ifname, 1576 (int)timo.tv_sec, (int)timo.tv_usec); 1577 1578 dhcp6_set_timer(&timo, ev->timer); 1579 } 1580 /* if the client send preferred addresses reqeust in SOLICIT */ 1581 if (!TAILQ_EMPTY(&optinfo0->addr_list)) 1582 dhcp6_copy_list(&request_list, &optinfo0->addr_list); 1583 /* Store IAPD to the request_prefix_list, for later use by DHCP renew */ 1584 if (!TAILQ_EMPTY(&optinfo0->prefix_list)) 1585 dhcp6_copy_list(&request_prefix_list, &optinfo0->prefix_list); 1586 return 0; 1587} 1588 1589static struct dhcp6_serverinfo * 1590find_server(ifp, duid) 1591 struct dhcp6_if *ifp; 1592 struct duid *duid; 1593{ 1594 struct dhcp6_serverinfo *s; 1595 1596 for (s = ifp->servers; s; s = s->next) { 1597 if (duidcmp(&s->optinfo.serverID, duid) == 0) 1598 return (s); 1599 } 1600 1601 return (NULL); 1602} 1603 1604static struct dhcp6_serverinfo * 1605allocate_newserver(ifp, optinfo) 1606 struct dhcp6_if *ifp; 1607 struct dhcp6_optinfo *optinfo; 1608{ 1609 struct dhcp6_serverinfo *newserver, **sp; 1610 1611 /* keep the server */ 1612 if ((newserver = malloc(sizeof(*newserver))) == NULL) { 1613 dprintf(LOG_ERR, "%s" "memory allocation failed for server", 1614 FNAME); 1615 return (NULL); 1616 } 1617 memset(newserver, 0, sizeof(*newserver)); 1618 dhcp6_init_options(&newserver->optinfo); 1619 if (dhcp6_copy_options(&newserver->optinfo, optinfo)) { 1620 dprintf(LOG_ERR, "%s" "failed to copy options", FNAME); 1621 free(newserver); 1622 return (NULL); 1623 } 1624 dprintf(LOG_DEBUG, "%s" "new server DUID %s, len %d ", 1625 FNAME, duidstr(&newserver->optinfo.serverID), 1626 newserver->optinfo.serverID.duid_len); 1627 if (optinfo->pref != DH6OPT_PREF_UNDEF) 1628 newserver->pref = optinfo->pref; 1629 if (optinfo->flags & DHCIFF_UNICAST) 1630 memcpy(&newserver->server_addr, &optinfo->server_addr, 1631 sizeof(newserver->server_addr)); 1632 newserver->active = 1; 1633 for (sp = &ifp->servers; *sp; sp = &(*sp)->next) { 1634 if ((*sp)->pref != DH6OPT_PREF_MAX && 1635 (*sp)->pref < newserver->pref) { 1636 break; 1637 } 1638 } 1639 newserver->next = *sp; 1640 *sp = newserver; 1641 return newserver; 1642} 1643 1644void 1645free_servers(ifp) 1646 struct dhcp6_if *ifp; 1647{ 1648 struct dhcp6_serverinfo *sp, *sp_next; 1649 /* free all servers we've seen so far */ 1650 for (sp = ifp->servers; sp; sp = sp_next) { 1651 sp_next = sp->next; 1652 dprintf(LOG_DEBUG, "%s" "removing server (ID: %s)", 1653 FNAME, duidstr(&sp->optinfo.serverID)); 1654 dhcp6_clear_options(&sp->optinfo); 1655 free(sp); 1656 } 1657 ifp->servers = NULL; 1658 ifp->current_server = NULL; 1659} 1660 1661static int not_on_link_count = 0; 1662 1663static int 1664client6_recvreply(ifp, dh6, len, optinfo) 1665 struct dhcp6_if *ifp; 1666 struct dhcp6 *dh6; 1667 ssize_t len; 1668 struct dhcp6_optinfo *optinfo; 1669{ 1670 struct dhcp6_listval *lv; 1671 struct dhcp6_event *ev; 1672 int addr_status_code = DH6OPT_STCODE_UNSPECFAIL; 1673 struct dhcp6_serverinfo *newserver; 1674 int newstate = 0; 1675 /* find the corresponding event based on the received xid */ 1676 dprintf(LOG_DEBUG, "%s" "reply message XID is (%x)", 1677 FNAME, ntohl(dh6->dh6_xid) & DH6_XIDMASK); 1678 ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); 1679 if (ev == NULL) { 1680 dprintf(LOG_INFO, "%s" "XID mismatch", FNAME); 1681 return -1; 1682 } 1683 1684 if (!(DHCP6S_VALID_REPLY(ev->state)) && 1685 (ev->state != DHCP6S_SOLICIT || 1686 !(optinfo->flags & DHCIFF_RAPID_COMMIT))) { 1687 dprintf(LOG_INFO, "%s" "unexpected reply", FNAME); 1688 return -1; 1689 } 1690 1691 dhcp6_clear_list(&request_list); 1692 1693 /* A Reply message must contain a Server ID option */ 1694 if (optinfo->serverID.duid_len == 0) { 1695 dprintf(LOG_INFO, "%s" "no server ID option", FNAME); 1696 return -1; 1697 } 1698 dprintf(LOG_DEBUG, "%s" "serverID is %s len is %d", FNAME, 1699 duidstr(&optinfo->serverID), optinfo->serverID.duid_len); 1700 /* get current server */ 1701 switch (ev->state) { 1702 case DHCP6S_SOLICIT: 1703 case DHCP6S_CONFIRM: 1704 case DHCP6S_REBIND: 1705 newserver = allocate_newserver(ifp, optinfo); 1706 if (newserver == NULL) 1707 return (-1); 1708 ifp->current_server = newserver; 1709 duidcpy(&client6_iaidaddr.client6_info.serverid, 1710 &ifp->current_server->optinfo.serverID); 1711 break; 1712 default: 1713 break; 1714 } 1715 /* 1716 * DUID in the Client ID option (which must be contained for our 1717 * client implementation) must match ours. 1718 */ 1719 if (optinfo->clientID.duid_len == 0) { 1720 dprintf(LOG_INFO, "%s" "no client ID option", FNAME); 1721 return -1; 1722 } 1723 if (duidcmp(&optinfo->clientID, &client_duid)) { 1724 dprintf(LOG_INFO, "%s" "client DUID mismatch", FNAME); 1725 return -1; 1726 } 1727 1728 if (!TAILQ_EMPTY(&optinfo->dns_list.addrlist) || 1729 optinfo->dns_list.domainlist != NULL) { 1730 resolv_parse(&optinfo->dns_list); 1731 } 1732 /* 1733 * The client MAY choose to report any status code or message from the 1734 * status code option in the Reply message. 1735 * [dhcpv6-26 Section 18.1.8] 1736 */ 1737 addr_status_code = 0; 1738 for (lv = TAILQ_FIRST(&optinfo->stcode_list); lv; 1739 lv = TAILQ_NEXT(lv, link)) { 1740 dprintf(LOG_INFO, "%s" "status code: %s", 1741 FNAME, dhcp6_stcodestr(lv->val_num)); 1742 switch (lv->val_num) { 1743 case DH6OPT_STCODE_SUCCESS: 1744 case DH6OPT_STCODE_UNSPECFAIL: 1745 case DH6OPT_STCODE_NOADDRAVAIL: 1746 case DH6OPT_STCODE_NOPREFIXAVAIL: 1747 case DH6OPT_STCODE_NOBINDING: 1748 case DH6OPT_STCODE_NOTONLINK: 1749 case DH6OPT_STCODE_USEMULTICAST: 1750 if (addr_status_code == 0) 1751 addr_status_code = lv->val_num; 1752 default: 1753 break; 1754 } 1755 } 1756 switch (addr_status_code) { 1757 case DH6OPT_STCODE_UNSPECFAIL: 1758 case DH6OPT_STCODE_USEMULTICAST: 1759 dprintf(LOG_INFO, "%s" "status code: %s", FNAME, 1760 dhcp6_stcodestr(addr_status_code)); 1761 /* retransmit the message with multicast address */ 1762 /* how many time allow the retransmission with error status code? */ 1763 return -1; 1764 } 1765 1766 /* 1767 * Update configuration information to be renewed or rebound 1768 * declined, confirmed, released. 1769 * Note that the returned list may be empty, in which case 1770 * the waiting information should be removed. 1771 */ 1772 switch (ev->state) { 1773 case DHCP6S_SOLICIT: 1774 if (optinfo->iaidinfo.iaid == 0) 1775 break; 1776 else if (!optinfo->flags & DHCIFF_RAPID_COMMIT) { 1777 newstate = DHCP6S_REQUEST; 1778 break; 1779 } 1780 case DHCP6S_REQUEST: 1781 /* NotOnLink: 1. SOLICIT 1782 * NoAddrAvail: Information Request */ 1783 switch(addr_status_code) { 1784 case DH6OPT_STCODE_NOTONLINK: 1785 dprintf(LOG_DEBUG, "%s" 1786 "got a NotOnLink reply for request/rapid commit," 1787 " sending solicit.", FNAME); 1788 /* WNR3500L TD170, need to send request without any IP for 1789 * 3 times, then back to solicit state. 1790 */ 1791 //newstate = DHCP6S_SOLICIT; 1792 not_on_link_count++; 1793 if (not_on_link_count <= REQ_MAX_RC_NOTONLINK) { 1794 /* Clear the IA / PD address, so they won't appear in the 1795 * request pkt. */ 1796 dhcp6_clear_list(&(ifp->current_server->optinfo.addr_list)); 1797 dhcp6_clear_list(&(ifp->current_server->optinfo.prefix_list)); 1798 newstate = DHCP6S_REQUEST; 1799 } else { 1800 /* Three times, back to SOLICIT state */ 1801 not_on_link_count = 0; 1802 free_servers(ifp); 1803 newstate = DHCP6S_SOLICIT; 1804 } 1805 break; 1806 case DH6OPT_STCODE_NOADDRAVAIL: 1807 case DH6OPT_STCODE_NOPREFIXAVAIL: 1808 dprintf(LOG_DEBUG, "%s" 1809 "got a NoAddrAvail reply for request/rapid commit," 1810 " sending inforeq.", FNAME); 1811 not_on_link_count = 0; 1812 optinfo->iaidinfo.iaid = 0; 1813 newstate = DHCP6S_INFOREQ; 1814 break; 1815 case DH6OPT_STCODE_SUCCESS: 1816 case DH6OPT_STCODE_UNDEFINE: 1817 default: 1818 not_on_link_count = 0; 1819 if (!TAILQ_EMPTY(&optinfo->addr_list)) { 1820 (void)get_if_rainfo(ifp); 1821 dhcp6_add_iaidaddr(optinfo); 1822 if (optinfo->type == IAPD) { 1823 radvd_parse(&client6_iaidaddr, ADDR_UPDATE); 1824 /* 1. Execute callback now as IAPD only does not need DAD. 1825 * 2. Send Info-req to get additional info 1826 */ 1827 dhcp6c_dad_callback(); 1828 newstate = DHCP6S_INFOREQ; 1829 } 1830 else if (ifp->dad_timer == NULL && (ifp->dad_timer = 1831 dhcp6_add_timer(check_dad_timo, ifp)) < 0) { 1832 dprintf(LOG_INFO, "%s" "failed to create a timer for " 1833 " DAD", FNAME); 1834 } 1835 setup_check_timer(ifp); 1836 /* WNR3500L TD#175, send info-req after we complete the 1837 * DAD check. */ 1838 //client6_send_info_req(ev); 1839 } 1840 break; 1841 } 1842 break; 1843 case DHCP6S_RENEW: 1844 case DHCP6S_REBIND: 1845 if (client6_request_flag & CLIENT6_CONFIRM_ADDR) 1846 goto rebind_confirm; 1847 /* NoBinding for RENEW, REBIND, send REQUEST */ 1848 switch(addr_status_code) { 1849 case DH6OPT_STCODE_NOBINDING: 1850 newstate = DHCP6S_REQUEST; 1851 dprintf(LOG_DEBUG, "%s" 1852 "got a NoBinding reply, sending request.", FNAME); 1853 dhcp6_remove_iaidaddr(&client6_iaidaddr); 1854 break; 1855 case DH6OPT_STCODE_NOADDRAVAIL: 1856 case DH6OPT_STCODE_NOPREFIXAVAIL: 1857 case DH6OPT_STCODE_UNSPECFAIL: 1858 break; 1859 case DH6OPT_STCODE_SUCCESS: 1860 case DH6OPT_STCODE_UNDEFINE: 1861 default: 1862 dhcp6_update_iaidaddr(optinfo, ADDR_UPDATE); 1863 if (optinfo->type == IAPD) 1864 radvd_parse(&client6_iaidaddr, ADDR_UPDATE); 1865 system("killall -SIGUSR1 radvd"); 1866 /* Send info-req to get SIP server and NTP server */ 1867 client6_send_info_req(ev); 1868 break; 1869 } 1870 break; 1871 case DHCP6S_CONFIRM: 1872 /* NOtOnLink for a Confirm, send SOLICIT message */ 1873rebind_confirm: client6_request_flag &= ~CLIENT6_CONFIRM_ADDR; 1874 switch(addr_status_code) { 1875 struct timeb now; 1876 struct timeval timo; 1877 time_t offset; 1878 case DH6OPT_STCODE_NOTONLINK: 1879 case DH6OPT_STCODE_NOBINDING: 1880 case DH6OPT_STCODE_NOADDRAVAIL: 1881 dprintf(LOG_DEBUG, "%s" 1882 "got a NotOnLink reply for confirm, sending solicit.", FNAME); 1883 /* remove event data list */ 1884 free_servers(ifp); 1885 newstate = DHCP6S_SOLICIT; 1886 break; 1887 case DH6OPT_STCODE_SUCCESS: 1888 case DH6OPT_STCODE_UNDEFINE: 1889 dprintf(LOG_DEBUG, "%s" "got an expected reply for confirm", FNAME); 1890 ftime(&now); 1891 client6_iaidaddr.state = ACTIVE; 1892 if ((client6_iaidaddr.timer = dhcp6_add_timer(dhcp6_iaidaddr_timo, 1893 &client6_iaidaddr)) == NULL) { 1894 dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u", 1895 FNAME, client6_iaidaddr.client6_info.iaidinfo.iaid); 1896 return (-1); 1897 } 1898 if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0) { 1899 client6_iaidaddr.client6_info.iaidinfo.renewtime 1900 = get_min_preferlifetime(&client6_iaidaddr)/2; 1901 } 1902 if (client6_iaidaddr.client6_info.iaidinfo.rebindtime == 0) { 1903 client6_iaidaddr.client6_info.iaidinfo.rebindtime 1904 = (get_min_preferlifetime(&client6_iaidaddr)*4)/5; 1905 } 1906 offset = now.time - client6_iaidaddr.start_date; 1907 if ( offset > client6_iaidaddr.client6_info.iaidinfo.renewtime) 1908 timo.tv_sec = 0; 1909 else 1910 timo.tv_sec = client6_iaidaddr.client6_info.iaidinfo.renewtime - offset; 1911 timo.tv_usec = 0; 1912 dhcp6_set_timer(&timo, client6_iaidaddr.timer); 1913 /* check DAD */ 1914 if (optinfo->type != IAPD && ifp->dad_timer == NULL && 1915 (ifp->dad_timer = dhcp6_add_timer(check_dad_timo, ifp)) < 0) { 1916 dprintf(LOG_INFO, "%s" "failed to create a timer for " 1917 " DAD", FNAME); 1918 } 1919 setup_check_timer(ifp); 1920 break; 1921 default: 1922 break; 1923 } 1924 break; 1925 case DHCP6S_DECLINE: 1926 /* send REQUEST message to server with none decline address */ 1927 dprintf(LOG_DEBUG, "%s" 1928 "got an expected reply for decline, sending request.", FNAME); 1929 /* Should restart the 4-packet process, from SOLICIT */ 1930#if 0 1931 create_request_list(0); 1932 /* remove event data list */ 1933 newstate = DHCP6S_REQUEST; 1934#endif 1935 free_servers(ifp); 1936 newstate = DHCP6S_SOLICIT; 1937 break; 1938 case DHCP6S_RELEASE: 1939 dprintf(LOG_INFO, "%s" "got an expected release, exit.", FNAME); 1940 dhcp6_remove_event(ev); 1941 exit(0); 1942 default: 1943 break; 1944 } 1945 dhcp6_remove_event(ev); 1946 if (newstate) { 1947 client6_send_newstate(ifp, newstate); 1948 } else 1949 dprintf(LOG_DEBUG, "%s" "got an expected reply, sleeping.", FNAME); 1950 TAILQ_INIT(&request_list); 1951 return 0; 1952} 1953 1954int 1955client6_send_newstate(ifp, state) 1956 struct dhcp6_if *ifp; 1957 int state; 1958{ 1959 struct dhcp6_event *ev; 1960 if ((ev = dhcp6_create_event(ifp, state)) == NULL) { 1961 dprintf(LOG_ERR, "%s" "failed to create an event", 1962 FNAME); 1963 return (-1); 1964 } 1965 if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { 1966 dprintf(LOG_ERR, "%s" "failed to add a timer for %s", 1967 FNAME, ifp->ifname); 1968 free(ev); 1969 return(-1); 1970 } 1971 TAILQ_INSERT_TAIL(&ifp->event_list, ev, link); 1972 ev->timeouts = 0; 1973 dhcp6_set_timeoparam(ev); 1974 /* WNR3500L TD170, modify the maximum re-send counter of 1975 * Request message to 3 if a "NotOnLink" status is 1976 * received. 1977 */ 1978 if (state == DHCP6S_REQUEST && not_on_link_count) 1979 ev->max_retrans_cnt = REQ_MAX_RC_NOTONLINK; 1980 dhcp6_reset_timer(ev); 1981 1982 /* Use diff function to send INFO-REQ */ 1983 if (state == DHCP6S_INFOREQ) 1984 client6_send_info_req(ev); 1985 else 1986 client6_send(ev); 1987 return 0; 1988} 1989 1990static struct dhcp6_event * 1991find_event_withid(ifp, xid) 1992 struct dhcp6_if *ifp; 1993 u_int32_t xid; 1994{ 1995 struct dhcp6_event *ev; 1996 1997 for (ev = TAILQ_FIRST(&ifp->event_list); ev; 1998 ev = TAILQ_NEXT(ev, link)) { 1999 dprintf(LOG_DEBUG, "%s" "ifp %p event %p id is %x", 2000 FNAME, ifp, ev, ev->xid); 2001 if (ev->xid == xid) 2002 return (ev); 2003 } 2004 2005 return (NULL); 2006} 2007 2008static int 2009create_request_list(int reboot) 2010{ 2011 struct dhcp6_lease *cl; 2012 struct dhcp6_listval *lv; 2013 /* create an address list for release all/confirm */ 2014 for (cl = TAILQ_FIRST(&client6_iaidaddr.lease_list); cl; 2015 cl = TAILQ_NEXT(cl, link)) { 2016 /* IANA, IAPD */ 2017 if ((lv = malloc(sizeof(*lv))) == NULL) { 2018 dprintf(LOG_ERR, "%s" 2019 "failed to allocate memory for an ipv6 addr", FNAME); 2020 exit(1); 2021 } 2022 memcpy(&lv->val_dhcp6addr, &cl->lease_addr, 2023 sizeof(lv->val_dhcp6addr)); 2024 lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE; 2025 TAILQ_INSERT_TAIL(&request_list, lv, link); 2026 /* config the interface for reboot */ 2027 if (reboot && client6_iaidaddr.client6_info.type != IAPD && 2028 (client6_request_flag & CLIENT6_CONFIRM_ADDR)) { 2029 if (client6_ifaddrconf(IFADDRCONF_ADD, &cl->lease_addr) != 0) { 2030 dprintf(LOG_INFO, "config address failed: %s", 2031 in6addr2str(&cl->lease_addr.addr, 0)); 2032 return (-1); 2033 } 2034 } 2035 } 2036 /* update radvd.conf for prefix delegation */ 2037 if (reboot && client6_iaidaddr.client6_info.type == IAPD && 2038 (client6_request_flag & CLIENT6_CONFIRM_ADDR)) 2039 radvd_parse(&client6_iaidaddr, ADDR_UPDATE); 2040 return (0); 2041} 2042 2043static void setup_check_timer(struct dhcp6_if *ifp) 2044{ 2045 double d; 2046 struct timeval timo; 2047 d = DHCP6_CHECKLINK_TIME; 2048 timo.tv_sec = (long)d; 2049 timo.tv_usec = 0; 2050 dprintf(LOG_DEBUG, "set timer for checking link ..."); 2051 dhcp6_set_timer(&timo, ifp->link_timer); 2052 if (ifp->dad_timer != NULL) { 2053 d = DHCP6_CHECKDAD_TIME; 2054 timo.tv_sec = (long)d; 2055 timo.tv_usec = 0; 2056 dprintf(LOG_DEBUG, "set timer for checking DAD ..."); 2057 dhcp6_set_timer(&timo, ifp->dad_timer); 2058 } 2059 d = DHCP6_SYNCFILE_TIME; 2060 timo.tv_sec = (long)d; 2061 timo.tv_usec = 0; 2062 dprintf(LOG_DEBUG, "set timer for syncing file ..."); 2063 dhcp6_set_timer(&timo, ifp->sync_timer); 2064 return; 2065} 2066 2067static struct dhcp6_timer 2068*check_lease_file_timo(void *arg) 2069{ 2070 struct dhcp6_if *ifp = (struct dhcp6_if *)arg; 2071 double d; 2072 struct timeval timo; 2073 struct stat buf; 2074 FILE *file; 2075 stat(leasename, &buf); 2076 strcpy(client6_lease_temp, leasename); 2077 strcat(client6_lease_temp, "XXXXXX"); 2078 if (buf.st_size > MAX_FILE_SIZE) { 2079 file = sync_leases(client6_lease_file, leasename, client6_lease_temp); 2080 if ( file != NULL) 2081 client6_lease_file = file; 2082 } 2083 d = DHCP6_SYNCFILE_TIME; 2084 timo.tv_sec = (long)d; 2085 timo.tv_usec = 0; 2086 dhcp6_set_timer(&timo, ifp->sync_timer); 2087 return ifp->sync_timer; 2088} 2089 2090static struct dhcp6_timer 2091*check_dad_timo(void *arg) 2092{ 2093 struct dhcp6_if *ifp = (struct dhcp6_if *)arg; 2094 int newstate = DHCP6S_REQUEST; 2095 if (client6_iaidaddr.client6_info.type == IAPD) 2096 goto end; 2097 dprintf(LOG_DEBUG, "enter checking dad ..."); 2098 if (dad_parse(ifproc_file) < 0) { 2099 dprintf(LOG_ERR, "parse /proc/net/if_inet6 failed"); 2100 goto end; 2101 } 2102 if (TAILQ_EMPTY(&request_list)) 2103 goto end; 2104 /* remove RENEW timer for client6_iaidaddr */ 2105 if (client6_iaidaddr.timer != NULL) 2106 dhcp6_remove_timer(client6_iaidaddr.timer); 2107 newstate = DHCP6S_DECLINE; 2108 client6_send_newstate(ifp, newstate); 2109end: 2110 /* one time check for DAD */ 2111 dhcp6_remove_timer(ifp->dad_timer); 2112 ifp->dad_timer = NULL; 2113 2114 /* Send info-req to get DNS/SIP/NTP, etc, per Netgear spec. */ 2115 if (newstate != DHCP6S_DECLINE) { 2116 dhcp6c_dad_callback(); 2117 dprintf(LOG_DEBUG, "send info-req"); 2118 newstate = DHCP6S_INFOREQ; 2119 client6_send_newstate(ifp, newstate); 2120 } 2121 2122 return NULL; 2123} 2124 2125static struct dhcp6_timer 2126*check_link_timo(void *arg) 2127{ 2128 struct dhcp6_if *ifp = (struct dhcp6_if *)arg; 2129 struct ifreq ifr; 2130 struct timeval timo; 2131 double d; 2132 int newstate; 2133 dprintf(LOG_DEBUG, "enter checking link ..."); 2134 strncpy(ifr.ifr_name, dhcp6_if->ifname, IFNAMSIZ); 2135 if (ioctl(nlsock, SIOCGIFFLAGS, &ifr) < 0) { 2136 dprintf(LOG_DEBUG, "ioctl SIOCGIFFLAGS failed"); 2137 goto settimer; 2138 } 2139 if (ifr.ifr_flags & IFF_RUNNING) { 2140 /* check previous flag 2141 * set current flag UP */ 2142 if (ifp->link_flag & IFF_RUNNING) { 2143 goto settimer; 2144 } 2145 /* check current state ACTIVE */ 2146 if (client6_iaidaddr.state == ACTIVE) { 2147 /* remove timer for renew/rebind 2148 * send confirm for ipv6address or 2149 * rebind for prefix delegation */ 2150 dhcp6_remove_timer(client6_iaidaddr.timer); 2151 client6_request_flag &= CLIENT6_CONFIRM_ADDR; 2152 create_request_list(0); 2153 if (client6_iaidaddr.client6_info.type == IAPD) 2154 newstate = DHCP6S_REBIND; 2155 else 2156 newstate = DHCP6S_CONFIRM; 2157 client6_send_newstate(ifp, newstate); 2158 } 2159 dprintf(LOG_INFO, "interface is from down to up"); 2160 ifp->link_flag |= IFF_RUNNING; 2161 } else { 2162 dprintf(LOG_INFO, "interface is down"); 2163 /* set flag_prev flag DOWN */ 2164 ifp->link_flag &= ~IFF_RUNNING; 2165 } 2166settimer: 2167 d = DHCP6_CHECKLINK_TIME; 2168 timo.tv_sec = (long)d; 2169 timo.tv_usec = 0; 2170 dhcp6_set_timer(&timo, ifp->link_timer); 2171 return ifp->link_timer; 2172} 2173 2174static void 2175setup_interface(char *ifname) 2176{ 2177 struct ifreq ifr; 2178 /* check the interface */ 2179 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 2180again: 2181 if (ioctl(nlsock, SIOCGIFFLAGS, &ifr) < 0) { 2182 dprintf(LOG_ERR, "ioctl SIOCGIFFLAGS failed"); 2183 exit(1); 2184 } 2185 if (!ifr.ifr_flags & IFF_UP) { 2186 ifr.ifr_flags |= IFF_UP; 2187 if (ioctl(nlsock, SIOCSIFFLAGS, &ifr) < 0) { 2188 dprintf(LOG_ERR, "ioctl SIOCSIFFLAGS failed"); 2189 exit(1); 2190 } 2191 if (ioctl(nlsock, SIOCGIFFLAGS, &ifr) < 0) { 2192 dprintf(LOG_ERR, "ioctl SIOCGIFFLAGS failed"); 2193 exit(1); 2194 } 2195 } 2196 if (!ifr.ifr_flags & IFF_RUNNING) { 2197 dprintf(LOG_INFO, "NIC is not connected to the network, " 2198 "please connect it. dhcp6c is sleeping ..."); 2199 sleep(10); 2200 goto again; 2201 } 2202 return; 2203} 2204