1/* $Id: dhcp6s.c,v 1.1.1.1 2006/12/04 00:45:25 Exp $ */ 2/* ported from KAME: dhcp6s.c,v 1.91 2002/09/24 14:20:50 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/stat.h> 35#include <sys/socket.h> 36#include <linux/sockios.h> 37#include <sys/ioctl.h> 38#include <sys/file.h> 39 40#include <sys/uio.h> 41#if TIME_WITH_SYS_TIME 42# include <sys/time.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 <errno.h> 52 53#include <net/if.h> 54#if defined(__FreeBSD__) && __FreeBSD__ >= 3 55#include <net/if_var.h> 56#endif 57 58#include <netinet/in.h> 59 60#include <arpa/inet.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 <netdb.h> 69#include <limits.h> 70 71#include "queue.h" 72#include "timer.h" 73#include "dhcp6.h" 74#include "config.h" 75#include "common.h" 76#include "server6_conf.h" 77#include "lease.h" 78 79typedef enum { DHCP6_CONFINFO_PREFIX, DHCP6_CONFINFO_ADDRS } dhcp6_conftype_t; 80 81struct dhcp6_binding { 82 TAILQ_ENTRY(dhcp6_binding) link; 83 84 dhcp6_conftype_t type; 85 struct duid clientid; 86 void *val; 87 88 u_int32_t duration; 89 struct dhcp6_timer *timer; 90}; 91 92static char *device[100]; 93static int num_device = 0; 94static int debug = 0; 95const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_SERVER; 96int insock; /* inbound udp port */ 97//int outsock; /* outbound udp port */ // Foxconn removed pling 08/15/2009 98extern FILE *server6_lease_file; 99char server6_lease_temp[100]; 100 101static const struct sockaddr_in6 *sa6_any_downstream; 102static u_int16_t upstream_port; 103static struct msghdr rmh; 104static char rdatabuf[BUFSIZ]; 105static int rmsgctllen; 106static char *rmsgctlbuf; 107static struct duid server_duid; 108static struct dns_list arg_dnslist; 109static struct dhcp6_timer *sync_lease_timer; 110 111struct link_decl *subnet = NULL; 112struct host_decl *host = NULL; 113struct rootgroup *globalgroup = NULL; 114 115#define DUID_FILE "/tmp/dhcp6s_duid" //Modified. "/var/lib/dhcpv6/dhcp6s_duid" 116#define DHCP6S_CONF "/etc/dhcp6s.conf" 117 118#define DH6_VALID_MESSAGE(a) \ 119 (a == DH6_SOLICIT || a == DH6_REQUEST || a == DH6_RENEW || \ 120 a == DH6_REBIND || a == DH6_CONFIRM || a == DH6_RELEASE || \ 121 a == DH6_DECLINE || a == DH6_INFORM_REQ) 122 123static void usage __P((void)); 124static void server6_init __P((void)); 125static void server6_mainloop __P((void)); 126static int server6_recv __P((int)); 127static int server6_react_message __P((struct dhcp6_if *, 128 struct in6_pktinfo *, struct dhcp6 *, 129 struct dhcp6_optinfo *, 130 struct sockaddr *, int)); 131static int server6_send __P((int, struct dhcp6_if *, struct dhcp6 *, 132 struct dhcp6_optinfo *, 133 struct sockaddr *, int, 134 struct dhcp6_optinfo *)); 135static struct dhcp6_timer *check_lease_file_timo __P((void *arg)); 136static struct dhcp6 *dhcp6_parse_relay __P((struct dhcp6_relay *, 137 struct dhcp6_relay *, 138 struct dhcp6_optinfo *, 139 struct in6_addr *)); 140static int dhcp6_set_relay __P((struct dhcp6_relay *, 141 struct dhcp6_relay *, 142 struct dhcp6_optinfo *)); 143static void dhcp6_set_relay_option_len __P((struct dhcp6_optinfo *, 144 int len)); 145extern struct link_decl *dhcp6_allocate_link __P((struct dhcp6_if *, struct rootgroup *, 146 struct in6_addr *)); 147extern struct host_decl *dhcp6_allocate_host __P((struct dhcp6_if *, struct rootgroup *, 148 struct dhcp6_optinfo *)); 149 150extern int dhcp6_get_hostconf __P((struct dhcp6_optinfo *, struct dhcp6_optinfo *, 151 struct dhcp6_iaidaddr *, struct host_decl *)); 152 153static void random_init(void) 154{ 155 int f, n; 156 unsigned int seed = time(NULL) & getpid(); 157 char rand_state[256]; 158 159 f = open("/dev/urandom", O_RDONLY); 160 if (f > 0) { 161 n = read(f, rand_state, sizeof(rand_state)); 162 close(f); 163 if (n > 32) { 164 initstate(seed, rand_state, n); 165 return; 166 } 167 } 168 srandom(seed); 169} 170 171int 172main(argc, argv) 173 int argc; 174 char **argv; 175{ 176 int ch; 177 struct in6_addr a; 178 struct dhcp6_listval *dlv; 179 char *progname, *conffile = DHCP6S_CONF; 180 181 if ((progname = strrchr(*argv, '/')) == NULL) 182 progname = *argv; 183 else 184 progname++; 185 186 TAILQ_INIT(&arg_dnslist.addrlist); 187 188 random_init(); 189 190 while ((ch = getopt(argc, argv, "c:dDfn:")) != -1) { 191 switch (ch) { 192 case 'c': 193 conffile = optarg; 194 break; 195 case 'd': 196 debug = 1; 197 break; 198 case 'D': 199 debug = 2; 200 break; 201 case 'f': 202 foreground++; 203 break; 204 case 'n': 205 warnx("-n dnsserv option was obsoleted. " 206 "use configuration file."); 207 if (inet_pton(AF_INET6, optarg, &a) != 1) { 208 errx(1, "invalid DNS server %s", optarg); 209 /* NOTREACHED */ 210 } 211 if ((dlv = malloc(sizeof *dlv)) == NULL) { 212 errx(1, "malloc failed for a DNS server"); 213 /* NOTREACHED */ 214 } 215 dlv->val_addr6 = a; 216 TAILQ_INSERT_TAIL(&arg_dnslist.addrlist, dlv, link); 217 break; 218 default: 219 usage(); 220 /* NOTREACHED */ 221 } 222 } 223 while (optind < argc) { 224 device[num_device] = argv[optind++]; 225 num_device += 1; 226 } 227 228 if (foreground == 0) { 229 if (daemon(0, 0) < 0) 230 err(1, "daemon"); 231 openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); 232 } 233 setloglevel(debug); 234 235 server6_init(); 236 if ((server6_lease_file = init_leases(PATH_SERVER6_LEASE)) == NULL) { 237 dprintf(LOG_ERR, "%s" "failed to parse lease file", 238 FNAME); 239 exit(1); 240 } 241 strcpy(server6_lease_temp, PATH_SERVER6_LEASE); 242 strcat(server6_lease_temp, "XXXXXX"); 243 server6_lease_file = 244 sync_leases(server6_lease_file, PATH_SERVER6_LEASE, server6_lease_temp); 245 if (server6_lease_file == NULL) 246 exit(1); 247 globalgroup = (struct rootgroup *)malloc(sizeof(struct rootgroup)); 248 if (globalgroup == NULL) { 249 dprintf(LOG_ERR, "failed to allocate memory %s", strerror(errno)); 250 exit(1); 251 } 252 memset(globalgroup, 0, sizeof(*globalgroup)); 253 TAILQ_INIT(&globalgroup->scope.dnslist.addrlist); 254 TAILQ_INIT(&globalgroup->scope.siplist); 255 TAILQ_INIT(&globalgroup->scope.ntplist); 256 if ((sfparse(conffile)) != 0) { 257 dprintf(LOG_ERR, "%s" "failed to parse addr configuration file", 258 FNAME); 259 exit(1); 260 } 261 server6_mainloop(); 262 exit(0); 263} 264 265static void 266usage() 267{ 268 fprintf(stderr, 269 "usage: dhcp6s [-c configfile] [-dDf] [interface]\n"); 270 exit(0); 271} 272 273/*------------------------------------------------------------*/ 274 275void 276server6_init() 277{ 278 struct addrinfo hints; 279 struct addrinfo *res, *res2; 280 int error, skfd, i; 281 int on = 1; 282 int ifidx[MAX_DEVICE]; 283 struct ipv6_mreq mreq6; 284 static struct iovec iov; 285 static struct sockaddr_in6 sa6_any_downstream_storage; 286 char buff[1024]; 287 struct ifconf ifc; 288 struct ifreq *ifr; 289 double d; 290 struct timeval timo; 291 /* initialize inbound socket */ 292 memset(&hints, 0, sizeof(hints)); 293 hints.ai_family = PF_INET6; 294 hints.ai_socktype = SOCK_DGRAM; 295 hints.ai_protocol = IPPROTO_UDP; 296 hints.ai_flags = AI_PASSIVE; 297 error = getaddrinfo(NULL, DH6PORT_UPSTREAM, &hints, &res); 298 if (error) { 299 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 300 FNAME, gai_strerror(error)); 301 exit(1); 302 } 303 insock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 304 if (insock < 0) { 305 dprintf(LOG_ERR, "%s" "socket(insock): %s", 306 FNAME, strerror(errno)); 307 exit(1); 308 } 309#ifdef IPV6_RECVPKTINFO 310 if (setsockopt(insock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 311 sizeof(on)) < 0) { 312 dprintf(LOG_ERR, "%s" 313 "setsockopt(inbound, IPV6_RECVPKTINFO): %s", 314 FNAME, strerror(errno)); 315 exit(1); 316 } 317#else 318 if (setsockopt(insock, IPPROTO_IPV6, IPV6_PKTINFO, &on, 319 sizeof(on)) < 0) { 320 dprintf(LOG_ERR, "%s" 321 "setsockopt(inbound, IPV6_PKTINFO): %s", 322 FNAME, strerror(errno)); 323 exit(1); 324 } 325#endif 326 if (bind(insock, res->ai_addr, res->ai_addrlen) < 0) { 327 dprintf(LOG_ERR, "%s" "dhcp6s: bind(insock): %s", 328 FNAME, strerror(errno)); 329 exit(1); 330 } 331 upstream_port = ((struct sockaddr_in6 *) res->ai_addr)->sin6_port; 332 freeaddrinfo(res); 333 334 /* initiallize outbound interface */ 335 hints.ai_flags = AI_PASSIVE; 336 error = getaddrinfo(NULL, DH6PORT_DOWNSTREAM, &hints, &res); 337 if (error) { 338 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 339 FNAME, gai_strerror(error)); 340 exit(1); 341 } 342 /* Foxconn removed start pling 08/15/2009 */ 343 /* Don't use outsock as it conflicts with dhcp6c */ 344#if 0 345 outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 346 if (outsock < 0) { 347 dprintf(LOG_ERR, "%s" "socket(outsock): %s", 348 FNAME, strerror(errno)); 349 exit(1); 350 } 351 if (bind(outsock, res->ai_addr, res->ai_addrlen) < 0) { 352 dprintf(LOG_ERR, "%s" "bind(outsock): %s", 353 FNAME, strerror(errno)); 354 exit(1); 355 } 356#endif 357 /* Foxconn removed end pling 08/15/2009 */ 358 memcpy(&sa6_any_downstream_storage, res->ai_addr, res->ai_addrlen); 359 sa6_any_downstream = 360 (const struct sockaddr_in6*)&sa6_any_downstream_storage; 361 freeaddrinfo(res); 362 363 /* initialize send/receive buffer */ 364 iov.iov_base = (caddr_t)rdatabuf; 365 iov.iov_len = sizeof(rdatabuf); 366 rmh.msg_iov = &iov; 367 rmh.msg_iovlen = 1; 368 rmsgctllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); 369 if ((rmsgctlbuf = (char *)malloc(rmsgctllen)) == NULL) { 370 dprintf(LOG_ERR, "%s" "memory allocation failed", FNAME); 371 exit(1); 372 } 373 if (num_device != 0) { 374 for (i = 0; i < num_device; i++) { 375 ifidx[i] = if_nametoindex(device[i]); 376 if (ifidx[i] == 0) { 377 dprintf(LOG_ERR, "%s" 378 "invalid interface %s", FNAME, device[0]); 379 exit(1); 380 } 381 ifinit(device[i]); 382 } 383 if (get_duid(DUID_FILE, device[0], &server_duid)) { 384 dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME); 385 exit(1); 386 } 387 } else { 388 /* all the interfaces join multicast group */ 389 ifc.ifc_len = sizeof(buff); 390 ifc.ifc_buf = buff; 391 if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) { 392 dprintf(LOG_ERR, "new socket failed"); 393 exit(1); 394 } 395 if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { 396 dprintf(LOG_ERR, "SIOCGIFCONF: %s\n", strerror(errno)); 397 exit(1); 398 } 399 ifr = ifc.ifc_req; 400 for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { 401 dprintf(LOG_DEBUG, "found device %s", ifr->ifr_name); 402 ifidx[num_device] = if_nametoindex(ifr->ifr_name); 403 if (ifidx[num_device] < 0) { 404 dprintf(LOG_ERR, "%s: unknown interface.\n", 405 ifr->ifr_name); 406 continue; 407 } 408 dprintf(LOG_DEBUG, "if %s index is %d", ifr->ifr_name, 409 ifidx[num_device]); 410#ifdef mshirley 411 if (((ifr->ifr_flags & IFF_UP) == 0)) continue; 412#endif 413 if (strcmp(ifr->ifr_name, "lo")) { 414 /* get our DUID */ 415 if (get_duid(DUID_FILE, ifr->ifr_name, &server_duid)) { 416 dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME); 417 exit(1); 418 } 419 } 420 ifinit(ifr->ifr_name); 421 num_device += 1; 422 } 423 } 424 for (i = 0; i < num_device; i++) { 425 hints.ai_flags = 0; 426 error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res2); 427 if (error) { 428 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 429 FNAME, gai_strerror(error)); 430 exit(1); 431 } 432 memset(&mreq6, 0, sizeof(mreq6)); 433 mreq6.ipv6mr_interface = ifidx[i]; 434 memcpy(&mreq6.ipv6mr_multiaddr, 435 &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, 436 sizeof(mreq6.ipv6mr_multiaddr)); 437 if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP, 438 &mreq6, sizeof(mreq6))) { 439 dprintf(LOG_ERR, "%s" "setsockopt(insock, IPV6_JOIN_GROUP) %s", 440 FNAME, strerror(errno)); 441 exit(1); 442 } 443 freeaddrinfo(res2); 444 445 hints.ai_flags = 0; 446 error = getaddrinfo(DH6ADDR_ALLSERVER, DH6PORT_UPSTREAM, 447 &hints, &res2); 448 if (error) { 449 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 450 FNAME, gai_strerror(error)); 451 exit(1); 452 } 453 memset(&mreq6, 0, sizeof(mreq6)); 454 mreq6.ipv6mr_interface = ifidx[i]; 455 memcpy(&mreq6.ipv6mr_multiaddr, 456 &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, 457 sizeof(mreq6.ipv6mr_multiaddr)); 458 if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP, 459 &mreq6, sizeof(mreq6))) { 460 dprintf(LOG_ERR, 461 "%s" "setsockopt(insock, IPV6_JOIN_GROUP): %s", 462 FNAME, strerror(errno)); 463 exit(1); 464 } 465 freeaddrinfo(res2); 466 467 /* Foxconn removed start pling 08/15/2009 */ 468 /* Don't use outsock as it conflicts with dhcp6c */ 469#if 0 470 /* set outgoing interface of multicast packets for DHCP reconfig */ 471 if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF, 472 &ifidx[i], sizeof(ifidx[i])) < 0) { 473 dprintf(LOG_ERR, 474 "%s" "setsockopt(outsock, IPV6_MULTICAST_IF): %s", 475 FNAME, strerror(errno)); 476 exit(1); 477 } 478#endif 479 /* Foxconn removed start pling 08/15/2009 */ 480 } 481 /* set up sync lease file timer */ 482 sync_lease_timer = dhcp6_add_timer(check_lease_file_timo, NULL); 483 d = DHCP6_SYNCFILE_TIME; 484 timo.tv_sec = (long)d; 485 timo.tv_usec = 0; 486 dprintf(LOG_DEBUG, "set timer for syncing file ..."); 487 dhcp6_set_timer(&timo, sync_lease_timer); 488 return; 489} 490 491 492static void 493server6_mainloop() 494{ 495 struct timeval *w; 496 int ret; 497 fd_set r; 498 499 while (1) { 500 w = dhcp6_check_timer(); 501 502 FD_ZERO(&r); 503 FD_SET(insock, &r); 504 ret = select(insock + 1, &r, NULL, NULL, w); 505 switch (ret) { 506 case -1: 507 dprintf(LOG_ERR, "%s" "select: %s", 508 FNAME, strerror(errno)); 509 exit(1); 510 /* NOTREACHED */ 511 case 0: /* timeout */ 512 break; 513 default: 514 break; 515 } 516 if (FD_ISSET(insock, &r)) 517 server6_recv(insock); 518 } 519} 520 521static int 522server6_recv(s) 523 int s; 524{ 525 ssize_t len; 526 struct sockaddr_storage from; 527 int fromlen; 528 struct msghdr mhdr; 529 struct iovec iov; 530 char cmsgbuf[BUFSIZ]; 531 struct cmsghdr *cm; 532 struct in6_pktinfo *pi = NULL; 533 struct dhcp6_if *ifp; 534 struct dhcp6 *dh6; 535 struct dhcp6_optinfo optinfo; 536 struct in6_addr relay; /* the address of the first relay, if any */ 537 memset(&iov, 0, sizeof(iov)); 538 memset(&mhdr, 0, sizeof(mhdr)); 539 540 iov.iov_base = rdatabuf; 541 iov.iov_len = sizeof(rdatabuf); 542 mhdr.msg_name = &from; 543 mhdr.msg_namelen = sizeof(from); 544 mhdr.msg_iov = &iov; 545 mhdr.msg_iovlen = 1; 546 mhdr.msg_control = (caddr_t)cmsgbuf; 547 mhdr.msg_controllen = sizeof(cmsgbuf); 548 549 if ((len = recvmsg(insock, &mhdr, 0)) < 0) { 550 dprintf(LOG_ERR, "%s" "recvmsg: %s", FNAME, strerror(errno)); 551 return -1; 552 } 553 fromlen = mhdr.msg_namelen; 554 555 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm; 556 cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) { 557 if (cm->cmsg_level == IPPROTO_IPV6 && 558 cm->cmsg_type == IPV6_PKTINFO && 559 cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 560 pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 561 } 562 } 563 if (pi == NULL) { 564 dprintf(LOG_NOTICE, "%s" "failed to get packet info", FNAME); 565 return -1; 566 } 567 dprintf(LOG_DEBUG, "received message packet info addr is %s, scope id (%d)", 568 in6addr2str(&pi->ipi6_addr, 0), (unsigned int)pi->ipi6_ifindex); 569 if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) { 570 dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME, 571 (unsigned int)pi->ipi6_ifindex); 572 return -1; 573 } 574 /* Foxconn added start pling 06/04/2014 */ 575 /* Don't accept packets not coming from LAN, 576 * e.g. from router's own dhcp6c client */ 577 if (ifp && strcmp(ifp->ifname, "br0")) { 578 dprintf(LOG_INFO, "%s" "Don't accept pkts from non-LAN interface (%s)", FNAME, ifp->ifname); 579 return -1; 580 } 581 /* Foxconn added end pling 06/04/2014 */ 582 if (len < sizeof(*dh6)) { 583 dprintf(LOG_INFO, "%s" "short packet", FNAME); 584 return -1; 585 } 586 587 dh6 = (struct dhcp6 *)rdatabuf; 588 589 dprintf(LOG_DEBUG, "%s" "received %s from %s", FNAME, 590 dhcp6msgstr(dh6->dh6_msgtype), 591 addr2str((struct sockaddr *)&from)); 592 593 dhcp6_init_options(&optinfo); 594 595 /* 596 * If this is a relayed message, parse all of the relay data, storing 597 * the link addresses, peer addresses, and interface identifiers for 598 * later use. Get a pointer to the original client message. 599 */ 600 if (dh6->dh6_msgtype == DH6_RELAY_FORW) { 601 dh6 = dhcp6_parse_relay((struct dhcp6_relay *) dh6, 602 (struct dhcp6_relay *) (rdatabuf + len), 603 &optinfo, &relay); 604 605 /* 606 * NULL means there was an error in the relay format or no 607 * client message was found. 608 */ 609 if (dh6 == NULL) { 610 dprintf(LOG_INFO, "%s" "failed to parse relay fields " 611 "or could not find client message", FNAME); 612 return -1; 613 } 614 } 615 616 /* 617 * parse and validate options in the request 618 */ 619 /* Foxconn modified start pling 10/04/2010 */ 620 /* Pass extra arg to 'dhcp6_get_options' */ 621#if 0 622 if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1), 623 (struct dhcp6opt *)(rdatabuf + len), &optinfo) < 0) { 624#endif 625 if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1), 626 (struct dhcp6opt *)(rdatabuf + len), &optinfo, 0, 0, 0) < 0) { 627 /* Foxconn modified end pling 10/04/2010 */ 628 dprintf(LOG_INFO, "%s" "failed to parse options", FNAME); 629 return -1; 630 } 631 /* check host decl first */ 632 host = dhcp6_allocate_host(ifp, globalgroup, &optinfo); 633 /* ToDo: allocate subnet after relay agent done 634 * now assume client is on the same link as server 635 * if the subnet couldn't be found return status code NotOnLink to client 636 */ 637 /* 638 * If the relay list is empty, then this is a message received directly 639 * from the client, so client is on the same link as the server. 640 * Otherwise, allocate the client an address based on the first relay 641 * that forwarded the message. 642 */ 643 if (TAILQ_EMPTY(&optinfo.relay_list)) 644 subnet = dhcp6_allocate_link(ifp, globalgroup, NULL); 645 else 646 subnet = dhcp6_allocate_link(ifp, globalgroup, &relay); 647 648 if (!(DH6_VALID_MESSAGE(dh6->dh6_msgtype))) 649 dprintf(LOG_INFO, "%s" "unknown or unsupported msgtype %s", 650 FNAME, dhcp6msgstr(dh6->dh6_msgtype)); 651 else 652 server6_react_message(ifp, pi, dh6, &optinfo, 653 (struct sockaddr *)&from, fromlen); 654 dhcp6_clear_options(&optinfo); 655 return 0; 656} 657 658static int 659server6_react_message(ifp, pi, dh6, optinfo, from, fromlen) 660 struct dhcp6_if *ifp; 661 struct in6_pktinfo *pi; 662 struct dhcp6 *dh6; 663 struct dhcp6_optinfo *optinfo; 664 struct sockaddr *from; 665 int fromlen; 666{ 667 struct dhcp6_optinfo roptinfo; 668 669 int addr_flag = 0; 670 int addr_request = 0; 671 int resptype = DH6_REPLY; 672 int num = DH6OPT_STCODE_SUCCESS; 673 int sending_hint = 0; 674 675 /* message validation according to Section 18.2 of dhcpv6-28 */ 676 677 /* the message must include a Client Identifier option */ 678 if (optinfo->clientID.duid_len == 0) { 679 dprintf(LOG_INFO, "%s" "no server ID option", FNAME); 680 return -1; 681 } else { 682 dprintf(LOG_DEBUG, "%s" "client ID %s", FNAME, 683 duidstr(&optinfo->clientID)); 684 } 685 /* the message must include a Server Identifier option in below messages*/ 686 switch (dh6->dh6_msgtype) { 687 case DH6_REQUEST: 688 case DH6_RENEW: 689 case DH6_DECLINE: 690 case DH6_RELEASE: 691 if (optinfo->serverID.duid_len == 0) { 692 dprintf(LOG_INFO, "%s" "no server ID option", FNAME); 693 return -1; 694 } 695 /* the contents of the Server Identifier option must match ours */ 696 if (duidcmp(&optinfo->serverID, &server_duid)) { 697 dprintf(LOG_INFO, "server ID %s mismatch %s", 698 duidstr(&optinfo->serverID), duidstr(&server_duid)); 699 return -1; 700 } 701 break; 702 default: 703 break; 704 } 705 /* 706 * configure necessary options based on the options in request. 707 */ 708 dhcp6_init_options(&roptinfo); 709 /* server information option */ 710 if (duidcpy(&roptinfo.serverID, &server_duid)) { 711 dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME); 712 goto fail; 713 } 714 /* copy client information back */ 715 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { 716 dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME); 717 goto fail; 718 } 719 /* if the client is not on the link */ 720 if (host == NULL && subnet == NULL) { 721 num = DH6OPT_STCODE_NOTONLINK; 722 /* Draft-28 18.2.2, drop the message if NotOnLink */ 723 if (dh6->dh6_msgtype == DH6_CONFIRM || dh6->dh6_msgtype == DH6_REBIND) 724 goto fail; 725 else 726 goto send; 727 } 728 if (subnet) { 729 roptinfo.pref = subnet->linkscope.server_pref; 730 roptinfo.flags = (optinfo->flags & subnet->linkscope.allow_flags) | 731 subnet->linkscope.send_flags; 732 dnslist = subnet->linkscope.dnslist; 733 siplist = subnet->linkscope.siplist; 734 ntplist = subnet->linkscope.ntplist; 735 } 736 if (host) { 737 roptinfo.pref = host->hostscope.server_pref; 738 roptinfo.flags = (optinfo->flags & host->hostscope.allow_flags) | 739 host->hostscope.send_flags; 740 dnslist = host->hostscope.dnslist; 741 siplist = host->hostscope.siplist; 742 ntplist = host->hostscope.ntplist; 743 } 744 /* prohibit a mixture of old and new style of DNS server config */ 745 if (!TAILQ_EMPTY(&arg_dnslist.addrlist)) { 746 if (!TAILQ_EMPTY(&dnslist.addrlist)) { 747 dprintf(LOG_INFO, "%s" "do not specify DNS servers " 748 "both by command line and by configuration file.", 749 FNAME); 750 exit(1); 751 } 752 dnslist = arg_dnslist; 753 TAILQ_INIT(&arg_dnslist.addrlist); 754 } 755 dprintf(LOG_DEBUG, "server preference is %2x", roptinfo.pref); 756 if (roptinfo.flags & DHCIFF_UNICAST) { 757 /* todo find the right server unicast address to client*/ 758 /* get_linklocal(device, &roptinfo.server_addr) */ 759 memcpy(&roptinfo.server_addr, &ifp->linklocal, 760 sizeof(roptinfo.server_addr)); 761 dprintf(LOG_DEBUG, "%s" "server address is %s", 762 FNAME, in6addr2str(&roptinfo.server_addr, 0)); 763 } 764 /* 765 * When the server receives a Request message via unicast from a 766 * client to which the server has not sent a unicast option, the server 767 * discards the Request message and responds with a Reply message 768 * containing a Status Code option with value UseMulticast, a Server 769 * Identifier option containing the server's DUID, the Client 770 * Identifier option from the client message and no other options. 771 * [dhcpv6-26 18.2.1] 772 */ 773 switch (dh6->dh6_msgtype) { 774 case DH6_REQUEST: 775 case DH6_RENEW: 776 case DH6_DECLINE: 777 /* 778 * If the message was relayed, then do not check whether the message 779 * came in via unicast or multicast, since the relay may be configured 780 * to send messages via unicast. 781 */ 782 if (TAILQ_EMPTY(&optinfo->relay_list) && 783 !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) { 784 if (!(roptinfo.flags & DHCIFF_UNICAST)) { 785 num = DH6OPT_STCODE_USEMULTICAST; 786 goto send; 787 } else 788 break; 789 } 790 break; 791 default: 792 /* 793 * If the message was relayed, then do not check whether the message 794 * came in via unicast or multicast, since the relay may be configured 795 * to send messages via unicast. 796 */ 797 if (TAILQ_EMPTY(&optinfo->relay_list) && 798 !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) { 799 num = DH6OPT_STCODE_USEMULTICAST; 800 goto send; 801 } 802 break; 803 } 804 805 switch (dh6->dh6_msgtype) { 806 case DH6_SOLICIT: 807 /* 808 * If the client has included a Rapid Commit option and the 809 * server has been configured to respond with committed address 810 * assignments and other resources, responds to the Solicit 811 * with a Reply message. 812 * [dhcpv6-28 Section 17.2.1] 813 * [dhcpv6-28 Section 17.2.2] 814 * If Solicit has IA option, responds to Solicit with a Advertise 815 * message. 816 */ 817 if (/*optinfo->iaidinfo.iaid != 0 &&*/ !(roptinfo.flags & DHCIFF_INFO_ONLY)) { /* pling modified 06/03/2014, for iOS compatibility */ 818 memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, 819 sizeof(roptinfo.iaidinfo)); 820 roptinfo.type = optinfo->type; 821 dprintf(LOG_DEBUG, "option type is %d", roptinfo.type); 822 addr_request = 1; 823 if (roptinfo.flags & DHCIFF_RAPID_COMMIT) { 824 resptype = DH6_REPLY; 825 } else { 826 resptype = DH6_ADVERTISE; 827 /* giving hint ?? */ 828 sending_hint = 1; 829 } 830 } 831 break; 832 case DH6_INFORM_REQ: 833 /* don't response to info-req if there is any IA option */ 834 if (optinfo->iaidinfo.iaid != 0) 835 goto fail; 836 /* DNS server */ 837 if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { 838 dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); 839 goto fail; 840 } 841 /* Foxconn added start pling 01/25/2010 */ 842 /* Add SIP and NTP server if available */ 843 if (dhcp6_copy_list(&roptinfo.sip_list, &siplist)) { 844 dprintf(LOG_ERR, "%s" "failed to copy SIP servers", FNAME); 845 goto fail; 846 } 847 if (dhcp6_copy_list(&roptinfo.ntp_list, &ntplist)) { 848 dprintf(LOG_ERR, "%s" "failed to copy NTP servers", FNAME); 849 goto fail; 850 } 851 /* Foxconn added end pling 01/25/2010 */ 852 roptinfo.dns_list.domainlist = dnslist.domainlist; 853 break; 854 case DH6_REQUEST: 855 /* get iaid for that request client for that interface */ 856 if (/*optinfo->iaidinfo.iaid != 0 &&*/ !(roptinfo.flags & DHCIFF_INFO_ONLY)) { /* pling modified 06/03/2014, for iOS compatibility */ 857 memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, 858 sizeof(roptinfo.iaidinfo)); 859 roptinfo.type = optinfo->type; 860 addr_request = 1; 861 } 862 break; 863 /* 864 * Locates the client's binding and verifies that the information 865 * from the client matches the information stored for that client. 866 */ 867 case DH6_RENEW: 868 case DH6_REBIND: 869 case DH6_DECLINE: 870 case DH6_RELEASE: 871 case DH6_CONFIRM: 872 roptinfo.type = optinfo->type; 873 if (dh6->dh6_msgtype == DH6_RENEW || dh6->dh6_msgtype == DH6_REBIND) 874 addr_flag = ADDR_UPDATE; 875 if (dh6->dh6_msgtype == DH6_RELEASE) 876 addr_flag = ADDR_REMOVE; 877 /* Foxconn Bob modified start on 01/09/2015, include DNS option in the reply of renew packet, 878 or some win8 PC can not get DNS server address correctly in TEC's noise test environment */ 879 if (dh6->dh6_msgtype == DH6_CONFIRM || dh6->dh6_msgtype == DH6_RENEW) { 880 /* Foxconn Bob modified end on 01/09/2015 */ 881 /* DNS server */ 882 addr_flag = ADDR_VALIDATE; 883 if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { 884 dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); 885 goto fail; 886 } 887 roptinfo.dns_list.domainlist = dnslist.domainlist; 888 } 889 if (dh6->dh6_msgtype == DH6_DECLINE) 890 addr_flag = ADDR_ABANDON; 891 if (/*optinfo->iaidinfo.iaid != 0*/ 1) { /* pling modified 06/03/2014, for iOS compatibility */ 892 if (!TAILQ_EMPTY(&optinfo->addr_list) && resptype != DH6_ADVERTISE) { 893 struct dhcp6_iaidaddr *iaidaddr; 894 memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, 895 sizeof(roptinfo.iaidinfo)); 896 roptinfo.type = optinfo->type; 897 /* find bindings */ 898 if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) == NULL) { 899 if (dh6->dh6_msgtype == DH6_REBIND) 900 goto fail; 901 num = DH6OPT_STCODE_NOBINDING; 902 dprintf(LOG_INFO, "%s" "Nobinding for client %s iaid %u", 903 FNAME, duidstr(&optinfo->clientID), 904 optinfo->iaidinfo.iaid); 905 break; 906 } 907 if (addr_flag != ADDR_UPDATE) { 908 dhcp6_copy_list(&roptinfo.addr_list, &optinfo->addr_list); 909 } else { 910 /* get static host configuration */ 911 if (host) 912 dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host); 913 /* allow dynamic address assginment for the host too */ 914 if (optinfo->type == IAPD) 915 dhcp6_create_prefixlist(&roptinfo, 916 optinfo, 917 iaidaddr, 918 subnet); 919 else 920 dhcp6_create_addrlist(&roptinfo, optinfo, 921 iaidaddr, subnet); 922 /* in case there is not bindings available */ 923 if (TAILQ_EMPTY(&roptinfo.addr_list)) { 924 num = DH6OPT_STCODE_NOBINDING; 925 dprintf(LOG_INFO, "%s" 926 "Bindings are not on link for client %s iaid %u", 927 FNAME, duidstr(&optinfo->clientID), 928 roptinfo.iaidinfo.iaid); 929 break; 930 } 931 } 932 if (addr_flag == ADDR_VALIDATE) { 933 if (dhcp6_validate_bindings(&roptinfo, iaidaddr)) 934 num = DH6OPT_STCODE_NOBINDING; 935 break; 936 } else { 937 /* do update if this is not a confirm */ 938 if (dhcp6_update_iaidaddr(&roptinfo, addr_flag) 939 != 0) { 940 dprintf(LOG_INFO, "%s" 941 "bindings failed for client %s iaid %u", 942 FNAME, duidstr(&optinfo->clientID), 943 roptinfo.iaidinfo.iaid); 944 num = DH6OPT_STCODE_UNSPECFAIL; 945 break; 946 } 947 } 948 num = DH6OPT_STCODE_SUCCESS; 949 } else 950 num = DH6OPT_STCODE_NOADDRAVAIL; 951 } else 952 dprintf(LOG_ERR, "invalid message type"); 953 break; 954 default: 955 break; 956 } 957 /* 958 * If the Request message contained an Option Request option, the 959 * server MUST include options in the Reply message for any options in 960 * the Option Request option the server is configured to return to the 961 * client. 962 * [dhcpv6-26 18.2.1] 963 * Note: our current implementation always includes all information 964 * that we can provide. So we do not have to check the option request 965 * options. 966 */ 967 if (addr_request == 1) { 968 int found_binding = 0; 969 struct dhcp6_iaidaddr *iaidaddr; 970 /* find bindings */ 971 if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) != NULL) { 972 found_binding = 1; 973 addr_flag = ADDR_UPDATE; 974 } 975 if (host) 976 dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host); 977 /* valid and create addresses list */ 978 if (optinfo->type == IAPD) 979 dhcp6_create_prefixlist(&roptinfo, optinfo, iaidaddr, subnet); 980 else 981 dhcp6_create_addrlist(&roptinfo, optinfo, iaidaddr, subnet); 982 if (TAILQ_EMPTY(&roptinfo.addr_list)) { 983 num = DH6OPT_STCODE_NOADDRAVAIL; 984 } else if (sending_hint == 0) { 985 /* valid client request address list */ 986 if (found_binding) { 987 if (dhcp6_update_iaidaddr(&roptinfo, addr_flag) != 0) { 988 dprintf(LOG_ERR, 989 "assigned ipv6address for client iaid %u failed", 990 roptinfo.iaidinfo.iaid); 991 num = DH6OPT_STCODE_UNSPECFAIL; 992 } else 993 num = DH6OPT_STCODE_SUCCESS; 994 } else { 995 if (dhcp6_add_iaidaddr(&roptinfo) != 0) { 996 dprintf(LOG_ERR, 997 "assigned ipv6address for client iaid %u failed", 998 roptinfo.iaidinfo.iaid); 999 num = DH6OPT_STCODE_UNSPECFAIL; 1000 } else 1001 num = DH6OPT_STCODE_SUCCESS; 1002 } 1003 } 1004 /* DNS server */ 1005 if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { 1006 dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); 1007 goto fail; 1008 } 1009 1010 /* Foxconn added start pling 01/25/2010 */ 1011 if (dhcp6_copy_list(&roptinfo.sip_list, &siplist)) { 1012 dprintf(LOG_ERR, "%s" "failed to copy SIP servers", FNAME); 1013 goto fail; 1014 } 1015 if (dhcp6_copy_list(&roptinfo.ntp_list, &ntplist)) { 1016 dprintf(LOG_ERR, "%s" "failed to copy NTP servers", FNAME); 1017 goto fail; 1018 } 1019 /* Foxconn added end pling 01/25/2010 */ 1020 1021 roptinfo.dns_list.domainlist = dnslist.domainlist; 1022 } 1023 /* add address status code */ 1024 send: 1025 dprintf(LOG_DEBUG, " status code: %s", dhcp6_stcodestr(num)); 1026 if (dhcp6_add_listval(&roptinfo.stcode_list, 1027 &num, DHCP6_LISTVAL_NUM) == NULL) { 1028 dprintf(LOG_ERR, "%s" "failed to copy " 1029 "status code", FNAME); 1030 goto fail; 1031 } 1032 /* send a reply message. */ 1033 if (num != DH6OPT_STCODE_NOADDRAVAIL) /* pling added, Comcast issue workaround */ 1034 (void)server6_send(resptype, ifp, dh6, optinfo, from, fromlen, 1035 &roptinfo); 1036 1037 dhcp6_clear_options(&roptinfo); 1038 return 0; 1039 1040 fail: 1041 dhcp6_clear_options(&roptinfo); 1042 return -1; 1043} 1044 1045/* Foxconn added start pling 04/13/2015 */ 1046/* to lookup the MAC address from link local address via "ip -6 neigh" command */ 1047static int get_client_mac(char *link_local_addr, char *mac) 1048{ 1049 FILE *fp = NULL; 1050 char line[256]; 1051 char *tmp; 1052 int found = 0; 1053 1054 system("/usr/sbin/ip -6 neigh > /tmp/ip6_neigh"); 1055 fp = fopen("/tmp/ip6_neigh", "r"); 1056 if (fp) { 1057 while (!feof(fp)) { 1058 fgets(line, sizeof(line), fp); 1059 if (strstr(line, link_local_addr)) { 1060 tmp = strstr(line, "lladdr"); 1061 if(tmp) { 1062 tmp += strlen("lladdr") + 1; 1063 memcpy(mac, tmp, 17); 1064 found = 1; 1065 break; 1066 } 1067 } 1068 } 1069 fclose(fp); 1070 } 1071 unlink("/tmp/ip6_neigh"); 1072 1073 return found; 1074} 1075/* Foxconn added end pling 04/13/2015 */ 1076 1077static int 1078server6_send(type, ifp, origmsg, optinfo, from, fromlen, roptinfo) 1079 int type; 1080 struct dhcp6_if *ifp; 1081 struct dhcp6 *origmsg; 1082 struct dhcp6_optinfo *optinfo, *roptinfo; 1083 struct sockaddr *from; 1084 int fromlen; 1085{ 1086 char replybuf[BUFSIZ]; 1087 struct sockaddr_in6 dst; 1088 int len, optlen, relaylen = 0; 1089 struct dhcp6 *dh6; 1090 1091 if (sizeof(struct dhcp6) > sizeof(replybuf)) { 1092 dprintf(LOG_ERR, "%s" "buffer size assumption failed", FNAME); 1093 return (-1); 1094 } 1095 1096 if (!TAILQ_EMPTY(&optinfo->relay_list) && 1097 (relaylen = dhcp6_set_relay((struct dhcp6_relay *) replybuf, 1098 (struct dhcp6_relay *) (replybuf + 1099 sizeof (replybuf)), 1100 optinfo)) < 0) { 1101 dprintf(LOG_INFO, "%s" "failed to construct relay message", FNAME); 1102 return (-1); 1103 } 1104 1105 dh6 = (struct dhcp6 *) (replybuf + relaylen); 1106 len = sizeof(*dh6); 1107 memset(dh6, 0, sizeof(*dh6)); 1108 dh6->dh6_msgtypexid = origmsg->dh6_msgtypexid; 1109 dh6->dh6_msgtype = (u_int8_t)type; 1110 1111 /* set options in the reply message */ 1112 if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1), 1113 (struct dhcp6opt *)(replybuf + 1114 sizeof(replybuf)), 1115 roptinfo)) < 0) { 1116 dprintf(LOG_INFO, "%s" "failed to construct reply options", 1117 FNAME); 1118 return (-1); 1119 } 1120 len += optlen; 1121 1122 /* 1123 * If there were any Relay Message options, fill in the option-len 1124 * field(s) with the appropriate value(s). 1125 */ 1126 if (!TAILQ_EMPTY(&optinfo->relay_list)) 1127 dhcp6_set_relay_option_len(optinfo, len); 1128 1129 len += relaylen; 1130 1131 /* specify the destination and send the reply */ 1132 dst = *sa6_any_downstream; 1133 dst.sin6_addr = ((struct sockaddr_in6 *)from)->sin6_addr; 1134 1135 /* RELAY-REPL messages need to be directed back to the port the relay 1136 agent is listening on, namely DH6PORT_UPSTREAM */ 1137 if (relaylen > 0) 1138 dst.sin6_port = upstream_port; 1139 1140 dst.sin6_scope_id = ((struct sockaddr_in6 *)from)->sin6_scope_id; 1141 dprintf(LOG_DEBUG, "send destination address is %s, scope id is %d", 1142 addr2str((struct sockaddr *)&dst), dst.sin6_scope_id); 1143 /* Foxconn modified start pling 08/15/2009 */ 1144 /* why use 'outsock' to send to client? */ 1145 //if (transmit_sa(outsock, &dst, replybuf, len) != 0) { 1146 if (transmit_sa(insock, &dst, replybuf, len) != 0) { 1147 /* Foxconn modified end pling 08/15/2009 */ 1148 dprintf(LOG_ERR, "%s" "transmit %s to %s failed", FNAME, 1149 dhcp6msgstr(type), addr2str((struct sockaddr *)&dst)); 1150 return (-1); 1151 } 1152 1153 dprintf(LOG_DEBUG, "%s" "transmit %s to %s", FNAME, 1154 dhcp6msgstr(type), addr2str((struct sockaddr *)&dst)); 1155 1156 /* Foxconn added start pling 04/13/2015 */ 1157 /* R7000 TD#485: workaround for Mac OS client. 1158 * Mac OS does not respond NA, so add static neigh entry */ 1159 if (type == DH6_REPLY) 1160 { 1161 char command[256]; 1162 unsigned char client_mac[32]; 1163 unsigned char link_local_addr[128]; 1164 struct dhcp6_listval *dp; 1165 1166 memset(&client_mac, 0, sizeof(client_mac)); 1167 if (!TAILQ_EMPTY(&roptinfo->addr_list)) { 1168 for (dp = TAILQ_FIRST(&roptinfo->addr_list); dp; 1169 dp = TAILQ_NEXT(dp, link)) { 1170 sprintf(link_local_addr, "%s", addr2str((struct sockaddr *)&dst)); 1171 dprintf(LOG_DEBUG, "%s" "Global address is %s, lladdr is %s " , FNAME, 1172 in6addr2str(&dp->val_dhcp6addr.addr,0), link_local_addr); 1173 if (get_client_mac(link_local_addr, client_mac)) { 1174 sprintf(command, "/usr/sbin/ip -6 neigh replace %s lladdr %s dev br0", 1175 in6addr2str(&dp->val_dhcp6addr.addr,0), client_mac); 1176 dprintf(LOG_DEBUG, "%s" "Command is '%s' ", FNAME, command); 1177 system(command); 1178 } 1179 } 1180 } 1181 } 1182 /* Foxconn added end pling 04/13/2015 */ 1183 1184 return 0; 1185} 1186 1187static struct dhcp6_timer 1188*check_lease_file_timo(void *arg) 1189{ 1190 double d; 1191 struct timeval timo; 1192 struct stat buf; 1193 FILE *file; 1194 stat(PATH_SERVER6_LEASE, &buf); 1195 strcpy(server6_lease_temp, PATH_SERVER6_LEASE); 1196 strcat(server6_lease_temp, "XXXXXX"); 1197 if (buf.st_size > MAX_FILE_SIZE) { 1198 file = sync_leases(server6_lease_file, PATH_SERVER6_LEASE, server6_lease_temp); 1199 if (file != NULL) 1200 server6_lease_file = file; 1201 } 1202 d = DHCP6_SYNCFILE_TIME; 1203 timo.tv_sec = (long)d; 1204 timo.tv_usec = 0; 1205 dhcp6_set_timer(&timo, sync_lease_timer); 1206 return sync_lease_timer; 1207} 1208 1209/* 1210 * Parse all of the RELAY-FORW messages and interface ID options. Each 1211 * RELAY-FORW messages will have its hop count, link address, peer-address, 1212 * and interface ID (if any) put into a relay_listval structure. 1213 * A pointer to the actual original client message will be returned. 1214 * If this client message cannot be found, NULL is returned to signal an error. 1215 */ 1216static struct dhcp6 * 1217dhcp6_parse_relay(relay_msg, endptr, optinfo, relay_addr) 1218 struct dhcp6_relay *relay_msg; 1219 struct dhcp6_relay *endptr; 1220 struct dhcp6_optinfo *optinfo; 1221 struct in6_addr *relay_addr; 1222{ 1223 struct relay_listval *relay_val; 1224 struct dhcp6 *relayed_msg; /* the original message that the relay 1225 received */ 1226 struct dhcp6opt *option, *option_endptr = (struct dhcp6opt *) endptr; 1227 1228 u_int16_t optlen; 1229 u_int16_t opt; 1230 1231 while ((relay_msg + 1) < endptr) { 1232 relay_val = (struct relay_listval *) 1233 calloc (1, sizeof (struct relay_listval)); 1234 1235 if (relay_val == NULL) { 1236 dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME); 1237 relayfree(&optinfo->relay_list); 1238 return NULL; 1239 } 1240 1241 /* copy the msg-type, hop-count, link-address, and peer-address */ 1242 memcpy (&relay_val->relay, relay_msg, sizeof (struct dhcp6_relay)); 1243 1244 /* set the msg type to relay reply now so that it doesn't need to be 1245 done when formatting the reply */ 1246 relay_val->relay.dh6_msg_type = DH6_RELAY_REPL; 1247 1248 TAILQ_INSERT_TAIL(&optinfo->relay_list, relay_val, link); 1249 1250 /* 1251 * need to record the first relay's link address field for later use. 1252 * The first relay is the last one we see, so keep overwriting the 1253 * relay value. 1254 */ 1255 memcpy (relay_addr, &relay_val->relay.link_addr, 1256 sizeof (struct in6_addr)); 1257 1258 /* now handle the options in the RELAY-FORW message */ 1259 /* 1260 * The only options that should appear in a RELAY-FORW message are: 1261 * - Interface identifier 1262 * - Relay message 1263 * 1264 * All other options are ignored. 1265 */ 1266 option = (struct dhcp6opt *) (relay_msg + 1); 1267 1268 relayed_msg = NULL; /* if this is NULL at the end of the loop, no 1269 relayed message was found */ 1270 1271 /* since the order of options is not specified, all of the options 1272 must be processed */ 1273 while ((option + 1) < option_endptr) { 1274 memcpy (&opt, &option->dh6opt_type, sizeof(opt)); 1275 opt = ntohs(opt); 1276 memcpy (&optlen, &option->dh6opt_len, sizeof(optlen)); 1277 optlen = ntohs(optlen); 1278 1279 if ((char *) (option + 1) + optlen > (char *) option_endptr) { 1280 dprintf(LOG_ERR, "%s" "invalid option length in %s option", 1281 FNAME, dhcp6optstr(opt)); 1282 relayfree(&optinfo->relay_list); 1283 return NULL; 1284 } 1285 1286 if (opt == DH6OPT_INTERFACE_ID) { 1287 /* if this is not the first interface identifier option, 1288 then the message is incorrectly formed */ 1289 if (relay_val->intf_id == NULL) { 1290 if (optlen) { 1291 relay_val->intf_id = (struct intf_id *) 1292 malloc (sizeof (struct intf_id)); 1293 if (relay_val->intf_id == NULL) { 1294 dprintf(LOG_ERR, "%s" "failed to allocate memory", 1295 FNAME); 1296 relayfree(&optinfo->relay_list); 1297 return NULL; 1298 } 1299 else { 1300 relay_val->intf_id->intf_len = optlen; 1301 relay_val->intf_id->intf_id = (char *) 1302 malloc (optlen); 1303 1304 if (relay_val->intf_id->intf_id == NULL) { 1305 dprintf(LOG_ERR, "%s" 1306 "failed to allocate memory", 1307 FNAME); 1308 relayfree(&optinfo->relay_list); 1309 return NULL; 1310 } 1311 else { /* copy the interface identifier so it can 1312 be sent in the reply */ 1313 memcpy (relay_val->intf_id->intf_id, 1314 ((char *) (option + 1)), optlen); 1315 } 1316 } 1317 } 1318 else { 1319 dprintf(LOG_ERR, "%s" "Invalid length for interface " 1320 "identifier option", FNAME); 1321 } 1322 } 1323 else { 1324 dprintf(LOG_INFO, "%s" 1325 "Multiple interface identifier " 1326 "options in RELAY-FORW Message ", 1327 FNAME); 1328 relayfree(&optinfo->relay_list); 1329 return NULL; 1330 } 1331 } 1332 else if (opt == DH6OPT_RELAY_MSG) { 1333 if (relayed_msg == NULL) 1334 relayed_msg = (struct dhcp6 *) (option + 1); 1335 else { 1336 dprintf(LOG_INFO, "%s" "Duplicated Relay Message option", 1337 FNAME); 1338 relayfree(&optinfo->relay_list); 1339 return NULL; 1340 } 1341 } 1342 else /* No other options besides interface identifier and relay 1343 message make sense, so ignore them with a warning */ 1344 dprintf(LOG_INFO, "%s" "Unsupported option %s found in " 1345 "RELAY-FORW message", 1346 FNAME, dhcp6optstr(opt)); 1347 1348 /* advance the option pointer */ 1349 option = (struct dhcp6opt *) (((char *) (option + 1)) + optlen); 1350 } 1351 1352 /* 1353 * If the relayed message is non-NULL and is a regular client 1354 * message, then the relay processing is done. If it is another 1355 * RELAY_FORW message, then continue. If the relayed message is 1356 * NULL, signal an error. 1357 */ 1358 if (relayed_msg != NULL && (char *) (relayed_msg + 1) <= 1359 (char *) endptr) { 1360 /* done if have found the client message */ 1361 if (relayed_msg->dh6_msgtype != DH6_RELAY_FORW) 1362 return relayed_msg; 1363 else 1364 relay_msg = (struct dhcp6_relay *) relayed_msg; 1365 } 1366 else { 1367 dprintf(LOG_ERR, "%s" "invalid relayed message", FNAME); 1368 relayfree(&optinfo->relay_list); 1369 return NULL; 1370 } 1371 } 1372} 1373 1374/* 1375 * Format all of the RELAY-REPL messages and options to send back to the 1376 * client. A RELAY-REPL message and Relay Message option are added for 1377 * each of the relays that were in the RELAY-FORW packet that this is 1378 * in response to. 1379 */ 1380static int 1381dhcp6_set_relay (msg, endptr, optinfo) 1382 struct dhcp6_relay *msg; 1383 struct dhcp6_relay *endptr; 1384 struct dhcp6_optinfo *optinfo; 1385{ 1386 struct relay_listval *relay; 1387 struct dhcp6opt *option; 1388 int relaylen = 0; 1389 u_int16_t type, len; 1390 1391 for (relay = TAILQ_FIRST(&optinfo->relay_list); relay; 1392 relay = TAILQ_NEXT(relay, link)) { 1393 /* bounds check */ 1394 if (((char *) msg) + sizeof (struct dhcp6_relay) >= (char *) endptr) { 1395 dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL", 1396 FNAME); 1397 return -1; 1398 } 1399 1400 memcpy (msg, &relay->relay, sizeof(struct dhcp6_relay)); 1401 1402 relaylen += sizeof(struct dhcp6_relay); 1403 1404 option = (struct dhcp6opt *) (msg + 1); 1405 1406 /* include an Interface Identifier option if it was present in the 1407 original message */ 1408 if (relay->intf_id != NULL) { 1409 /* bounds check */ 1410 if ((((char *) option) + sizeof(struct dhcp6opt) + 1411 relay->intf_id->intf_len) >= (char *) endptr) { 1412 dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL", 1413 FNAME); 1414 return -1; 1415 } 1416 type = htons(DH6OPT_INTERFACE_ID); 1417 memcpy (&option->dh6opt_type, &type, sizeof(type)); 1418 len = htons(relay->intf_id->intf_len); 1419 memcpy (&option->dh6opt_len, &len, sizeof(len)); 1420 memcpy (option + 1, relay->intf_id->intf_id, 1421 relay->intf_id->intf_len); 1422 1423 option = (struct dhcp6opt *)(((char *)(option + 1)) + 1424 relay->intf_id->intf_len); 1425 relaylen += sizeof(struct dhcp6opt) + relay->intf_id->intf_len; 1426 } 1427 1428 /* save a pointer to the relay message option so that it is easier 1429 to fill in the length later */ 1430 relay->option = option; 1431 1432 /* bounds check */ 1433 if ((char *) (option + 1) >= (char *) endptr) { 1434 dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL", 1435 FNAME); 1436 return -1; 1437 } 1438 1439 /* lastly include the Relay Message option, which encapsulates the 1440 message being relayed */ 1441 type = htons(DH6OPT_RELAY_MSG); 1442 memcpy (&option->dh6opt_type, &type, sizeof(type)); 1443 relaylen += sizeof(struct dhcp6opt); 1444 /* dh6opt_len will be set by dhcp6_set_relay_option_len */ 1445 1446 msg = (struct dhcp6_relay *) (option + 1); 1447 } 1448 1449 /* 1450 * if there were no relays, this is an error since this function should 1451 * not have even been called in this case 1452 */ 1453 if (relaylen == 0) 1454 return -1; 1455 else 1456 return relaylen; 1457} 1458 1459/* 1460 * Fill in all of the opt-len fields for the Relay Message options now that 1461 * the length of the entire message is known. 1462 * 1463 * len - the length of the DHCPv6 message to the client (not including any 1464 * relay options) 1465 * 1466 * Precondition: dhcp6_set_relay has already been called and the relay->option 1467 * fields of all of the elements in optinfo->relay_list are 1468 * non-NULL 1469 */ 1470static void 1471dhcp6_set_relay_option_len(optinfo, reply_msg_len) 1472 struct dhcp6_optinfo *optinfo; 1473 int reply_msg_len; 1474{ 1475 struct relay_listval *relay, *last = NULL; 1476 u_int16_t len; 1477 1478 for (relay = TAILQ_LAST(&optinfo->relay_list, relay_list); 1479 relay; relay = TAILQ_PREV(relay, relay_list, link)) { 1480 if (last == NULL) { 1481 len = htons(reply_msg_len); 1482 memcpy (&relay->option->dh6opt_len, &len, sizeof(len)); 1483 last = relay; 1484 } 1485 else { 1486 len = reply_msg_len + (((void *) (last->option + 1)) - 1487 ((void *) (relay->option + 1))); 1488 len = htons(len); 1489 memcpy (&relay->option->dh6opt_len, &len, sizeof(len)); 1490 } 1491 } 1492} 1493