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 */ 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 /* Don't use outsock as it conflicts with dhcp6c */ 343#if 0 344 outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 345 if (outsock < 0) { 346 dprintf(LOG_ERR, "%s" "socket(outsock): %s", 347 FNAME, strerror(errno)); 348 exit(1); 349 } 350 if (bind(outsock, res->ai_addr, res->ai_addrlen) < 0) { 351 dprintf(LOG_ERR, "%s" "bind(outsock): %s", 352 FNAME, strerror(errno)); 353 exit(1); 354 } 355#endif 356 memcpy(&sa6_any_downstream_storage, res->ai_addr, res->ai_addrlen); 357 sa6_any_downstream = 358 (const struct sockaddr_in6*)&sa6_any_downstream_storage; 359 freeaddrinfo(res); 360 361 /* initialize send/receive buffer */ 362 iov.iov_base = (caddr_t)rdatabuf; 363 iov.iov_len = sizeof(rdatabuf); 364 rmh.msg_iov = &iov; 365 rmh.msg_iovlen = 1; 366 rmsgctllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); 367 if ((rmsgctlbuf = (char *)malloc(rmsgctllen)) == NULL) { 368 dprintf(LOG_ERR, "%s" "memory allocation failed", FNAME); 369 exit(1); 370 } 371 if (num_device != 0) { 372 for (i = 0; i < num_device; i++) { 373 ifidx[i] = if_nametoindex(device[i]); 374 if (ifidx[i] == 0) { 375 dprintf(LOG_ERR, "%s" 376 "invalid interface %s", FNAME, device[0]); 377 exit(1); 378 } 379 ifinit(device[i]); 380 } 381 if (get_duid(DUID_FILE, device[0], &server_duid)) { 382 dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME); 383 exit(1); 384 } 385 } else { 386 /* all the interfaces join multicast group */ 387 ifc.ifc_len = sizeof(buff); 388 ifc.ifc_buf = buff; 389 if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) { 390 dprintf(LOG_ERR, "new socket failed"); 391 exit(1); 392 } 393 if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { 394 dprintf(LOG_ERR, "SIOCGIFCONF: %s\n", strerror(errno)); 395 exit(1); 396 } 397 ifr = ifc.ifc_req; 398 for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { 399 dprintf(LOG_DEBUG, "found device %s", ifr->ifr_name); 400 ifidx[num_device] = if_nametoindex(ifr->ifr_name); 401 if (ifidx[num_device] < 0) { 402 dprintf(LOG_ERR, "%s: unknown interface.\n", 403 ifr->ifr_name); 404 continue; 405 } 406 dprintf(LOG_DEBUG, "if %s index is %d", ifr->ifr_name, 407 ifidx[num_device]); 408#ifdef mshirley 409 if (((ifr->ifr_flags & IFF_UP) == 0)) continue; 410#endif 411 if (strcmp(ifr->ifr_name, "lo")) { 412 /* get our DUID */ 413 if (get_duid(DUID_FILE, ifr->ifr_name, &server_duid)) { 414 dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME); 415 exit(1); 416 } 417 } 418 ifinit(ifr->ifr_name); 419 num_device += 1; 420 } 421 } 422 for (i = 0; i < num_device; i++) { 423 hints.ai_flags = 0; 424 error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res2); 425 if (error) { 426 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 427 FNAME, gai_strerror(error)); 428 exit(1); 429 } 430 memset(&mreq6, 0, sizeof(mreq6)); 431 mreq6.ipv6mr_interface = ifidx[i]; 432 memcpy(&mreq6.ipv6mr_multiaddr, 433 &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, 434 sizeof(mreq6.ipv6mr_multiaddr)); 435 if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP, 436 &mreq6, sizeof(mreq6))) { 437 dprintf(LOG_ERR, "%s" "setsockopt(insock, IPV6_JOIN_GROUP) %s", 438 FNAME, strerror(errno)); 439 exit(1); 440 } 441 freeaddrinfo(res2); 442 443 hints.ai_flags = 0; 444 error = getaddrinfo(DH6ADDR_ALLSERVER, DH6PORT_UPSTREAM, 445 &hints, &res2); 446 if (error) { 447 dprintf(LOG_ERR, "%s" "getaddrinfo: %s", 448 FNAME, gai_strerror(error)); 449 exit(1); 450 } 451 memset(&mreq6, 0, sizeof(mreq6)); 452 mreq6.ipv6mr_interface = ifidx[i]; 453 memcpy(&mreq6.ipv6mr_multiaddr, 454 &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, 455 sizeof(mreq6.ipv6mr_multiaddr)); 456 if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP, 457 &mreq6, sizeof(mreq6))) { 458 dprintf(LOG_ERR, 459 "%s" "setsockopt(insock, IPV6_JOIN_GROUP): %s", 460 FNAME, strerror(errno)); 461 exit(1); 462 } 463 freeaddrinfo(res2); 464 465 /* Don't use outsock as it conflicts with dhcp6c */ 466#if 0 467 /* set outgoing interface of multicast packets for DHCP reconfig */ 468 if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF, 469 &ifidx[i], sizeof(ifidx[i])) < 0) { 470 dprintf(LOG_ERR, 471 "%s" "setsockopt(outsock, IPV6_MULTICAST_IF): %s", 472 FNAME, strerror(errno)); 473 exit(1); 474 } 475#endif 476 } 477 /* set up sync lease file timer */ 478 sync_lease_timer = dhcp6_add_timer(check_lease_file_timo, NULL); 479 d = DHCP6_SYNCFILE_TIME; 480 timo.tv_sec = (long)d; 481 timo.tv_usec = 0; 482 dprintf(LOG_DEBUG, "set timer for syncing file ..."); 483 dhcp6_set_timer(&timo, sync_lease_timer); 484 return; 485} 486 487 488static void 489server6_mainloop() 490{ 491 struct timeval *w; 492 int ret; 493 fd_set r; 494 495 while (1) { 496 w = dhcp6_check_timer(); 497 498 FD_ZERO(&r); 499 FD_SET(insock, &r); 500 ret = select(insock + 1, &r, NULL, NULL, w); 501 switch (ret) { 502 case -1: 503 dprintf(LOG_ERR, "%s" "select: %s", 504 FNAME, strerror(errno)); 505 exit(1); 506 /* NOTREACHED */ 507 case 0: /* timeout */ 508 break; 509 default: 510 break; 511 } 512 if (FD_ISSET(insock, &r)) 513 server6_recv(insock); 514 } 515} 516 517static int 518server6_recv(s) 519 int s; 520{ 521 ssize_t len; 522 struct sockaddr_storage from; 523 int fromlen; 524 struct msghdr mhdr; 525 struct iovec iov; 526 char cmsgbuf[BUFSIZ]; 527 struct cmsghdr *cm; 528 struct in6_pktinfo *pi = NULL; 529 struct dhcp6_if *ifp; 530 struct dhcp6 *dh6; 531 struct dhcp6_optinfo optinfo; 532 struct in6_addr relay; /* the address of the first relay, if any */ 533 memset(&iov, 0, sizeof(iov)); 534 memset(&mhdr, 0, sizeof(mhdr)); 535 536 iov.iov_base = rdatabuf; 537 iov.iov_len = sizeof(rdatabuf); 538 mhdr.msg_name = &from; 539 mhdr.msg_namelen = sizeof(from); 540 mhdr.msg_iov = &iov; 541 mhdr.msg_iovlen = 1; 542 mhdr.msg_control = (caddr_t)cmsgbuf; 543 mhdr.msg_controllen = sizeof(cmsgbuf); 544 545 if ((len = recvmsg(insock, &mhdr, 0)) < 0) { 546 dprintf(LOG_ERR, "%s" "recvmsg: %s", FNAME, strerror(errno)); 547 return -1; 548 } 549 fromlen = mhdr.msg_namelen; 550 551 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm; 552 cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) { 553 if (cm->cmsg_level == IPPROTO_IPV6 && 554 cm->cmsg_type == IPV6_PKTINFO && 555 cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 556 pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 557 } 558 } 559 if (pi == NULL) { 560 dprintf(LOG_NOTICE, "%s" "failed to get packet info", FNAME); 561 return -1; 562 } 563 dprintf(LOG_DEBUG, "received message packet info addr is %s, scope id (%d)", 564 in6addr2str(&pi->ipi6_addr, 0), (unsigned int)pi->ipi6_ifindex); 565 if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) { 566 dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME, 567 (unsigned int)pi->ipi6_ifindex); 568 return -1; 569 } 570 if (len < sizeof(*dh6)) { 571 dprintf(LOG_INFO, "%s" "short packet", FNAME); 572 return -1; 573 } 574 575 dh6 = (struct dhcp6 *)rdatabuf; 576 577 dprintf(LOG_DEBUG, "%s" "received %s from %s", FNAME, 578 dhcp6msgstr(dh6->dh6_msgtype), 579 addr2str((struct sockaddr *)&from)); 580 581 dhcp6_init_options(&optinfo); 582 583 /* 584 * If this is a relayed message, parse all of the relay data, storing 585 * the link addresses, peer addresses, and interface identifiers for 586 * later use. Get a pointer to the original client message. 587 */ 588 if (dh6->dh6_msgtype == DH6_RELAY_FORW) { 589 dh6 = dhcp6_parse_relay((struct dhcp6_relay *) dh6, 590 (struct dhcp6_relay *) (rdatabuf + len), 591 &optinfo, &relay); 592 593 /* 594 * NULL means there was an error in the relay format or no 595 * client message was found. 596 */ 597 if (dh6 == NULL) { 598 dprintf(LOG_INFO, "%s" "failed to parse relay fields " 599 "or could not find client message", FNAME); 600 return -1; 601 } 602 } 603 604 /* 605 * parse and validate options in the request 606 */ 607 /* Pass extra arg to 'dhcp6_get_options' */ 608#if 0 609 if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1), 610 (struct dhcp6opt *)(rdatabuf + len), &optinfo) < 0) { 611#endif 612 if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1), 613 (struct dhcp6opt *)(rdatabuf + len), &optinfo, 0, 0, 0) < 0) { 614 dprintf(LOG_INFO, "%s" "failed to parse options", FNAME); 615 return -1; 616 } 617 /* check host decl first */ 618 host = dhcp6_allocate_host(ifp, globalgroup, &optinfo); 619 /* ToDo: allocate subnet after relay agent done 620 * now assume client is on the same link as server 621 * if the subnet couldn't be found return status code NotOnLink to client 622 */ 623 /* 624 * If the relay list is empty, then this is a message received directly 625 * from the client, so client is on the same link as the server. 626 * Otherwise, allocate the client an address based on the first relay 627 * that forwarded the message. 628 */ 629 if (TAILQ_EMPTY(&optinfo.relay_list)) 630 subnet = dhcp6_allocate_link(ifp, globalgroup, NULL); 631 else 632 subnet = dhcp6_allocate_link(ifp, globalgroup, &relay); 633 634 if (!(DH6_VALID_MESSAGE(dh6->dh6_msgtype))) 635 dprintf(LOG_INFO, "%s" "unknown or unsupported msgtype %s", 636 FNAME, dhcp6msgstr(dh6->dh6_msgtype)); 637 else 638 server6_react_message(ifp, pi, dh6, &optinfo, 639 (struct sockaddr *)&from, fromlen); 640 dhcp6_clear_options(&optinfo); 641 return 0; 642} 643 644static int 645server6_react_message(ifp, pi, dh6, optinfo, from, fromlen) 646 struct dhcp6_if *ifp; 647 struct in6_pktinfo *pi; 648 struct dhcp6 *dh6; 649 struct dhcp6_optinfo *optinfo; 650 struct sockaddr *from; 651 int fromlen; 652{ 653 struct dhcp6_optinfo roptinfo; 654 655 int addr_flag = 0; 656 int addr_request = 0; 657 int resptype = DH6_REPLY; 658 int num = DH6OPT_STCODE_SUCCESS; 659 int sending_hint = 0; 660 661 /* message validation according to Section 18.2 of dhcpv6-28 */ 662 663 /* the message must include a Client Identifier option */ 664 if (optinfo->clientID.duid_len == 0) { 665 dprintf(LOG_INFO, "%s" "no server ID option", FNAME); 666 return -1; 667 } else { 668 dprintf(LOG_DEBUG, "%s" "client ID %s", FNAME, 669 duidstr(&optinfo->clientID)); 670 } 671 /* the message must include a Server Identifier option in below messages*/ 672 switch (dh6->dh6_msgtype) { 673 case DH6_REQUEST: 674 case DH6_RENEW: 675 case DH6_DECLINE: 676 case DH6_RELEASE: 677 if (optinfo->serverID.duid_len == 0) { 678 dprintf(LOG_INFO, "%s" "no server ID option", FNAME); 679 return -1; 680 } 681 /* the contents of the Server Identifier option must match ours */ 682 if (duidcmp(&optinfo->serverID, &server_duid)) { 683 dprintf(LOG_INFO, "server ID %s mismatch %s", 684 duidstr(&optinfo->serverID), duidstr(&server_duid)); 685 return -1; 686 } 687 break; 688 default: 689 break; 690 } 691 /* 692 * configure necessary options based on the options in request. 693 */ 694 dhcp6_init_options(&roptinfo); 695 /* server information option */ 696 if (duidcpy(&roptinfo.serverID, &server_duid)) { 697 dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME); 698 goto fail; 699 } 700 /* copy client information back */ 701 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { 702 dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME); 703 goto fail; 704 } 705 /* if the client is not on the link */ 706 if (host == NULL && subnet == NULL) { 707 num = DH6OPT_STCODE_NOTONLINK; 708 /* Draft-28 18.2.2, drop the message if NotOnLink */ 709 if (dh6->dh6_msgtype == DH6_CONFIRM || dh6->dh6_msgtype == DH6_REBIND) 710 goto fail; 711 else 712 goto send; 713 } 714 if (subnet) { 715 roptinfo.pref = subnet->linkscope.server_pref; 716 roptinfo.flags = (optinfo->flags & subnet->linkscope.allow_flags) | 717 subnet->linkscope.send_flags; 718 dnslist = subnet->linkscope.dnslist; 719 siplist = subnet->linkscope.siplist; 720 ntplist = subnet->linkscope.ntplist; 721 } 722 if (host) { 723 roptinfo.pref = host->hostscope.server_pref; 724 roptinfo.flags = (optinfo->flags & host->hostscope.allow_flags) | 725 host->hostscope.send_flags; 726 dnslist = host->hostscope.dnslist; 727 siplist = host->hostscope.siplist; 728 ntplist = host->hostscope.ntplist; 729 } 730 /* prohibit a mixture of old and new style of DNS server config */ 731 if (!TAILQ_EMPTY(&arg_dnslist.addrlist)) { 732 if (!TAILQ_EMPTY(&dnslist.addrlist)) { 733 dprintf(LOG_INFO, "%s" "do not specify DNS servers " 734 "both by command line and by configuration file.", 735 FNAME); 736 exit(1); 737 } 738 dnslist = arg_dnslist; 739 TAILQ_INIT(&arg_dnslist.addrlist); 740 } 741 dprintf(LOG_DEBUG, "server preference is %2x", roptinfo.pref); 742 if (roptinfo.flags & DHCIFF_UNICAST) { 743 /* todo find the right server unicast address to client*/ 744 /* get_linklocal(device, &roptinfo.server_addr) */ 745 memcpy(&roptinfo.server_addr, &ifp->linklocal, 746 sizeof(roptinfo.server_addr)); 747 dprintf(LOG_DEBUG, "%s" "server address is %s", 748 FNAME, in6addr2str(&roptinfo.server_addr, 0)); 749 } 750 /* 751 * When the server receives a Request message via unicast from a 752 * client to which the server has not sent a unicast option, the server 753 * discards the Request message and responds with a Reply message 754 * containing a Status Code option with value UseMulticast, a Server 755 * Identifier option containing the server's DUID, the Client 756 * Identifier option from the client message and no other options. 757 * [dhcpv6-26 18.2.1] 758 */ 759 switch (dh6->dh6_msgtype) { 760 case DH6_REQUEST: 761 case DH6_RENEW: 762 case DH6_DECLINE: 763 /* 764 * If the message was relayed, then do not check whether the message 765 * came in via unicast or multicast, since the relay may be configured 766 * to send messages via unicast. 767 */ 768 if (TAILQ_EMPTY(&optinfo->relay_list) && 769 !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) { 770 if (!(roptinfo.flags & DHCIFF_UNICAST)) { 771 num = DH6OPT_STCODE_USEMULTICAST; 772 goto send; 773 } else 774 break; 775 } 776 break; 777 default: 778 /* 779 * If the message was relayed, then do not check whether the message 780 * came in via unicast or multicast, since the relay may be configured 781 * to send messages via unicast. 782 */ 783 if (TAILQ_EMPTY(&optinfo->relay_list) && 784 !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) { 785 num = DH6OPT_STCODE_USEMULTICAST; 786 goto send; 787 } 788 break; 789 } 790 791 switch (dh6->dh6_msgtype) { 792 case DH6_SOLICIT: 793 /* 794 * If the client has included a Rapid Commit option and the 795 * server has been configured to respond with committed address 796 * assignments and other resources, responds to the Solicit 797 * with a Reply message. 798 * [dhcpv6-28 Section 17.2.1] 799 * [dhcpv6-28 Section 17.2.2] 800 * If Solicit has IA option, responds to Solicit with a Advertise 801 * message. 802 */ 803 if (optinfo->iaidinfo.iaid != 0 && !(roptinfo.flags & DHCIFF_INFO_ONLY)) { 804 memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, 805 sizeof(roptinfo.iaidinfo)); 806 roptinfo.type = optinfo->type; 807 dprintf(LOG_DEBUG, "option type is %d", roptinfo.type); 808 addr_request = 1; 809 if (roptinfo.flags & DHCIFF_RAPID_COMMIT) { 810 resptype = DH6_REPLY; 811 } else { 812 resptype = DH6_ADVERTISE; 813 /* giving hint ?? */ 814 sending_hint = 1; 815 } 816 } 817 break; 818 case DH6_INFORM_REQ: 819 /* don't response to info-req if there is any IA option */ 820 if (optinfo->iaidinfo.iaid != 0) 821 goto fail; 822 /* DNS server */ 823 if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { 824 dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); 825 goto fail; 826 } 827 /* Add SIP and NTP server if available */ 828 if (dhcp6_copy_list(&roptinfo.sip_list, &siplist)) { 829 dprintf(LOG_ERR, "%s" "failed to copy SIP servers", FNAME); 830 goto fail; 831 } 832 if (dhcp6_copy_list(&roptinfo.ntp_list, &ntplist)) { 833 dprintf(LOG_ERR, "%s" "failed to copy NTP servers", FNAME); 834 goto fail; 835 } 836 roptinfo.dns_list.domainlist = dnslist.domainlist; 837 break; 838 case DH6_REQUEST: 839 /* get iaid for that request client for that interface */ 840 if (optinfo->iaidinfo.iaid != 0 && !(roptinfo.flags & DHCIFF_INFO_ONLY)) { 841 memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, 842 sizeof(roptinfo.iaidinfo)); 843 roptinfo.type = optinfo->type; 844 addr_request = 1; 845 } 846 break; 847 /* 848 * Locates the client's binding and verifies that the information 849 * from the client matches the information stored for that client. 850 */ 851 case DH6_RENEW: 852 case DH6_REBIND: 853 case DH6_DECLINE: 854 case DH6_RELEASE: 855 case DH6_CONFIRM: 856 roptinfo.type = optinfo->type; 857 if (dh6->dh6_msgtype == DH6_RENEW || dh6->dh6_msgtype == DH6_REBIND) 858 addr_flag = ADDR_UPDATE; 859 if (dh6->dh6_msgtype == DH6_RELEASE) 860 addr_flag = ADDR_REMOVE; 861 if (dh6->dh6_msgtype == DH6_CONFIRM) { 862 /* DNS server */ 863 addr_flag = ADDR_VALIDATE; 864 if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { 865 dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); 866 goto fail; 867 } 868 roptinfo.dns_list.domainlist = dnslist.domainlist; 869 } 870 if (dh6->dh6_msgtype == DH6_DECLINE) 871 addr_flag = ADDR_ABANDON; 872 if (optinfo->iaidinfo.iaid != 0) { 873 if (!TAILQ_EMPTY(&optinfo->addr_list) && resptype != DH6_ADVERTISE) { 874 struct dhcp6_iaidaddr *iaidaddr; 875 memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, 876 sizeof(roptinfo.iaidinfo)); 877 roptinfo.type = optinfo->type; 878 /* find bindings */ 879 if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) == NULL) { 880 if (dh6->dh6_msgtype == DH6_REBIND) 881 goto fail; 882 num = DH6OPT_STCODE_NOBINDING; 883 dprintf(LOG_INFO, "%s" "Nobinding for client %s iaid %u", 884 FNAME, duidstr(&optinfo->clientID), 885 optinfo->iaidinfo.iaid); 886 break; 887 } 888 if (addr_flag != ADDR_UPDATE) { 889 dhcp6_copy_list(&roptinfo.addr_list, &optinfo->addr_list); 890 } else { 891 /* get static host configuration */ 892 if (host) 893 dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host); 894 /* allow dynamic address assginment for the host too */ 895 if (optinfo->type == IAPD) 896 dhcp6_create_prefixlist(&roptinfo, 897 optinfo, 898 iaidaddr, 899 subnet); 900 else 901 dhcp6_create_addrlist(&roptinfo, optinfo, 902 iaidaddr, subnet); 903 /* in case there is not bindings available */ 904 if (TAILQ_EMPTY(&roptinfo.addr_list)) { 905 num = DH6OPT_STCODE_NOBINDING; 906 dprintf(LOG_INFO, "%s" 907 "Bindings are not on link for client %s iaid %u", 908 FNAME, duidstr(&optinfo->clientID), 909 roptinfo.iaidinfo.iaid); 910 break; 911 } 912 } 913 if (addr_flag == ADDR_VALIDATE) { 914 if (dhcp6_validate_bindings(&roptinfo, iaidaddr)) 915 num = DH6OPT_STCODE_NOBINDING; 916 break; 917 } else { 918 /* do update if this is not a confirm */ 919 if (dhcp6_update_iaidaddr(&roptinfo, addr_flag) 920 != 0) { 921 dprintf(LOG_INFO, "%s" 922 "bindings failed for client %s iaid %u", 923 FNAME, duidstr(&optinfo->clientID), 924 roptinfo.iaidinfo.iaid); 925 num = DH6OPT_STCODE_UNSPECFAIL; 926 break; 927 } 928 } 929 num = DH6OPT_STCODE_SUCCESS; 930 } else 931 num = DH6OPT_STCODE_NOADDRAVAIL; 932 } else 933 dprintf(LOG_ERR, "invalid message type"); 934 break; 935 default: 936 break; 937 } 938 /* 939 * If the Request message contained an Option Request option, the 940 * server MUST include options in the Reply message for any options in 941 * the Option Request option the server is configured to return to the 942 * client. 943 * [dhcpv6-26 18.2.1] 944 * Note: our current implementation always includes all information 945 * that we can provide. So we do not have to check the option request 946 * options. 947 */ 948 if (addr_request == 1) { 949 int found_binding = 0; 950 struct dhcp6_iaidaddr *iaidaddr; 951 /* find bindings */ 952 if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) != NULL) { 953 found_binding = 1; 954 addr_flag = ADDR_UPDATE; 955 } 956 if (host) 957 dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host); 958 /* valid and create addresses list */ 959 if (optinfo->type == IAPD) 960 dhcp6_create_prefixlist(&roptinfo, optinfo, iaidaddr, subnet); 961 else 962 dhcp6_create_addrlist(&roptinfo, optinfo, iaidaddr, subnet); 963 if (TAILQ_EMPTY(&roptinfo.addr_list)) { 964 num = DH6OPT_STCODE_NOADDRAVAIL; 965 } else if (sending_hint == 0) { 966 /* valid client request address list */ 967 if (found_binding) { 968 if (dhcp6_update_iaidaddr(&roptinfo, addr_flag) != 0) { 969 dprintf(LOG_ERR, 970 "assigned ipv6address for client iaid %u failed", 971 roptinfo.iaidinfo.iaid); 972 num = DH6OPT_STCODE_UNSPECFAIL; 973 } else 974 num = DH6OPT_STCODE_SUCCESS; 975 } else { 976 if (dhcp6_add_iaidaddr(&roptinfo) != 0) { 977 dprintf(LOG_ERR, 978 "assigned ipv6address for client iaid %u failed", 979 roptinfo.iaidinfo.iaid); 980 num = DH6OPT_STCODE_UNSPECFAIL; 981 } else 982 num = DH6OPT_STCODE_SUCCESS; 983 } 984 } 985 /* DNS server */ 986 if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { 987 dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); 988 goto fail; 989 } 990 991 if (dhcp6_copy_list(&roptinfo.sip_list, &siplist)) { 992 dprintf(LOG_ERR, "%s" "failed to copy SIP servers", FNAME); 993 goto fail; 994 } 995 if (dhcp6_copy_list(&roptinfo.ntp_list, &ntplist)) { 996 dprintf(LOG_ERR, "%s" "failed to copy NTP servers", FNAME); 997 goto fail; 998 } 999 1000 roptinfo.dns_list.domainlist = dnslist.domainlist; 1001 } 1002 /* add address status code */ 1003 send: 1004 dprintf(LOG_DEBUG, " status code: %s", dhcp6_stcodestr(num)); 1005 if (dhcp6_add_listval(&roptinfo.stcode_list, 1006 &num, DHCP6_LISTVAL_NUM) == NULL) { 1007 dprintf(LOG_ERR, "%s" "failed to copy " 1008 "status code", FNAME); 1009 goto fail; 1010 } 1011 /* send a reply message. */ 1012 (void)server6_send(resptype, ifp, dh6, optinfo, from, fromlen, 1013 &roptinfo); 1014 1015 dhcp6_clear_options(&roptinfo); 1016 return 0; 1017 1018 fail: 1019 dhcp6_clear_options(&roptinfo); 1020 return -1; 1021} 1022 1023static int 1024server6_send(type, ifp, origmsg, optinfo, from, fromlen, roptinfo) 1025 int type; 1026 struct dhcp6_if *ifp; 1027 struct dhcp6 *origmsg; 1028 struct dhcp6_optinfo *optinfo, *roptinfo; 1029 struct sockaddr *from; 1030 int fromlen; 1031{ 1032 char replybuf[BUFSIZ]; 1033 struct sockaddr_in6 dst; 1034 int len, optlen, relaylen = 0; 1035 struct dhcp6 *dh6; 1036 1037 if (sizeof(struct dhcp6) > sizeof(replybuf)) { 1038 dprintf(LOG_ERR, "%s" "buffer size assumption failed", FNAME); 1039 return (-1); 1040 } 1041 1042 if (!TAILQ_EMPTY(&optinfo->relay_list) && 1043 (relaylen = dhcp6_set_relay((struct dhcp6_relay *) replybuf, 1044 (struct dhcp6_relay *) (replybuf + 1045 sizeof (replybuf)), 1046 optinfo)) < 0) { 1047 dprintf(LOG_INFO, "%s" "failed to construct relay message", FNAME); 1048 return (-1); 1049 } 1050 1051 dh6 = (struct dhcp6 *) (replybuf + relaylen); 1052 len = sizeof(*dh6); 1053 memset(dh6, 0, sizeof(*dh6)); 1054 dh6->dh6_msgtypexid = origmsg->dh6_msgtypexid; 1055 dh6->dh6_msgtype = (u_int8_t)type; 1056 1057 /* set options in the reply message */ 1058 if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1), 1059 (struct dhcp6opt *)(replybuf + 1060 sizeof(replybuf)), 1061 roptinfo)) < 0) { 1062 dprintf(LOG_INFO, "%s" "failed to construct reply options", 1063 FNAME); 1064 return (-1); 1065 } 1066 len += optlen; 1067 1068 /* 1069 * If there were any Relay Message options, fill in the option-len 1070 * field(s) with the appropriate value(s). 1071 */ 1072 if (!TAILQ_EMPTY(&optinfo->relay_list)) 1073 dhcp6_set_relay_option_len(optinfo, len); 1074 1075 len += relaylen; 1076 1077 /* specify the destination and send the reply */ 1078 dst = *sa6_any_downstream; 1079 dst.sin6_addr = ((struct sockaddr_in6 *)from)->sin6_addr; 1080 1081 /* RELAY-REPL messages need to be directed back to the port the relay 1082 agent is listening on, namely DH6PORT_UPSTREAM */ 1083 if (relaylen > 0) 1084 dst.sin6_port = upstream_port; 1085 1086 dst.sin6_scope_id = ((struct sockaddr_in6 *)from)->sin6_scope_id; 1087 dprintf(LOG_DEBUG, "send destination address is %s, scope id is %d", 1088 addr2str((struct sockaddr *)&dst), dst.sin6_scope_id); 1089 /* why use 'outsock' to send to client? */ 1090 //if (transmit_sa(outsock, &dst, replybuf, len) != 0) { 1091 if (transmit_sa(insock, &dst, replybuf, len) != 0) { 1092 dprintf(LOG_ERR, "%s" "transmit %s to %s failed", FNAME, 1093 dhcp6msgstr(type), addr2str((struct sockaddr *)&dst)); 1094 return (-1); 1095 } 1096 1097 dprintf(LOG_DEBUG, "%s" "transmit %s to %s", FNAME, 1098 dhcp6msgstr(type), addr2str((struct sockaddr *)&dst)); 1099 1100 return 0; 1101} 1102 1103static struct dhcp6_timer 1104*check_lease_file_timo(void *arg) 1105{ 1106 double d; 1107 struct timeval timo; 1108 struct stat buf; 1109 FILE *file; 1110 stat(PATH_SERVER6_LEASE, &buf); 1111 strcpy(server6_lease_temp, PATH_SERVER6_LEASE); 1112 strcat(server6_lease_temp, "XXXXXX"); 1113 if (buf.st_size > MAX_FILE_SIZE) { 1114 file = sync_leases(server6_lease_file, PATH_SERVER6_LEASE, server6_lease_temp); 1115 if (file != NULL) 1116 server6_lease_file = file; 1117 } 1118 d = DHCP6_SYNCFILE_TIME; 1119 timo.tv_sec = (long)d; 1120 timo.tv_usec = 0; 1121 dhcp6_set_timer(&timo, sync_lease_timer); 1122 return sync_lease_timer; 1123} 1124 1125/* 1126 * Parse all of the RELAY-FORW messages and interface ID options. Each 1127 * RELAY-FORW messages will have its hop count, link address, peer-address, 1128 * and interface ID (if any) put into a relay_listval structure. 1129 * A pointer to the actual original client message will be returned. 1130 * If this client message cannot be found, NULL is returned to signal an error. 1131 */ 1132static struct dhcp6 * 1133dhcp6_parse_relay(relay_msg, endptr, optinfo, relay_addr) 1134 struct dhcp6_relay *relay_msg; 1135 struct dhcp6_relay *endptr; 1136 struct dhcp6_optinfo *optinfo; 1137 struct in6_addr *relay_addr; 1138{ 1139 struct relay_listval *relay_val; 1140 struct dhcp6 *relayed_msg; /* the original message that the relay 1141 received */ 1142 struct dhcp6opt *option, *option_endptr = (struct dhcp6opt *) endptr; 1143 1144 u_int16_t optlen; 1145 u_int16_t opt; 1146 1147 while ((relay_msg + 1) < endptr) { 1148 relay_val = (struct relay_listval *) 1149 calloc (1, sizeof (struct relay_listval)); 1150 1151 if (relay_val == NULL) { 1152 dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME); 1153 relayfree(&optinfo->relay_list); 1154 return NULL; 1155 } 1156 1157 /* copy the msg-type, hop-count, link-address, and peer-address */ 1158 memcpy (&relay_val->relay, relay_msg, sizeof (struct dhcp6_relay)); 1159 1160 /* set the msg type to relay reply now so that it doesn't need to be 1161 done when formatting the reply */ 1162 relay_val->relay.dh6_msg_type = DH6_RELAY_REPL; 1163 1164 TAILQ_INSERT_TAIL(&optinfo->relay_list, relay_val, link); 1165 1166 /* 1167 * need to record the first relay's link address field for later use. 1168 * The first relay is the last one we see, so keep overwriting the 1169 * relay value. 1170 */ 1171 memcpy (relay_addr, &relay_val->relay.link_addr, 1172 sizeof (struct in6_addr)); 1173 1174 /* now handle the options in the RELAY-FORW message */ 1175 /* 1176 * The only options that should appear in a RELAY-FORW message are: 1177 * - Interface identifier 1178 * - Relay message 1179 * 1180 * All other options are ignored. 1181 */ 1182 option = (struct dhcp6opt *) (relay_msg + 1); 1183 1184 relayed_msg = NULL; /* if this is NULL at the end of the loop, no 1185 relayed message was found */ 1186 1187 /* since the order of options is not specified, all of the options 1188 must be processed */ 1189 while ((option + 1) < option_endptr) { 1190 memcpy (&opt, &option->dh6opt_type, sizeof(opt)); 1191 opt = ntohs(opt); 1192 memcpy (&optlen, &option->dh6opt_len, sizeof(optlen)); 1193 optlen = ntohs(optlen); 1194 1195 if ((char *) (option + 1) + optlen > (char *) option_endptr) { 1196 dprintf(LOG_ERR, "%s" "invalid option length in %s option", 1197 FNAME, dhcp6optstr(opt)); 1198 relayfree(&optinfo->relay_list); 1199 return NULL; 1200 } 1201 1202 if (opt == DH6OPT_INTERFACE_ID) { 1203 /* if this is not the first interface identifier option, 1204 then the message is incorrectly formed */ 1205 if (relay_val->intf_id == NULL) { 1206 if (optlen) { 1207 relay_val->intf_id = (struct intf_id *) 1208 malloc (sizeof (struct intf_id)); 1209 if (relay_val->intf_id == NULL) { 1210 dprintf(LOG_ERR, "%s" "failed to allocate memory", 1211 FNAME); 1212 relayfree(&optinfo->relay_list); 1213 return NULL; 1214 } 1215 else { 1216 relay_val->intf_id->intf_len = optlen; 1217 relay_val->intf_id->intf_id = (char *) 1218 malloc (optlen); 1219 1220 if (relay_val->intf_id->intf_id == NULL) { 1221 dprintf(LOG_ERR, "%s" 1222 "failed to allocate memory", 1223 FNAME); 1224 relayfree(&optinfo->relay_list); 1225 return NULL; 1226 } 1227 else { /* copy the interface identifier so it can 1228 be sent in the reply */ 1229 memcpy (relay_val->intf_id->intf_id, 1230 ((char *) (option + 1)), optlen); 1231 } 1232 } 1233 } 1234 else { 1235 dprintf(LOG_ERR, "%s" "Invalid length for interface " 1236 "identifier option", FNAME); 1237 } 1238 } 1239 else { 1240 dprintf(LOG_INFO, "%s" 1241 "Multiple interface identifier " 1242 "options in RELAY-FORW Message ", 1243 FNAME); 1244 relayfree(&optinfo->relay_list); 1245 return NULL; 1246 } 1247 } 1248 else if (opt == DH6OPT_RELAY_MSG) { 1249 if (relayed_msg == NULL) 1250 relayed_msg = (struct dhcp6 *) (option + 1); 1251 else { 1252 dprintf(LOG_INFO, "%s" "Duplicated Relay Message option", 1253 FNAME); 1254 relayfree(&optinfo->relay_list); 1255 return NULL; 1256 } 1257 } 1258 else /* No other options besides interface identifier and relay 1259 message make sense, so ignore them with a warning */ 1260 dprintf(LOG_INFO, "%s" "Unsupported option %s found in " 1261 "RELAY-FORW message", 1262 FNAME, dhcp6optstr(opt)); 1263 1264 /* advance the option pointer */ 1265 option = (struct dhcp6opt *) (((char *) (option + 1)) + optlen); 1266 } 1267 1268 /* 1269 * If the relayed message is non-NULL and is a regular client 1270 * message, then the relay processing is done. If it is another 1271 * RELAY_FORW message, then continue. If the relayed message is 1272 * NULL, signal an error. 1273 */ 1274 if (relayed_msg != NULL && (char *) (relayed_msg + 1) <= 1275 (char *) endptr) { 1276 /* done if have found the client message */ 1277 if (relayed_msg->dh6_msgtype != DH6_RELAY_FORW) 1278 return relayed_msg; 1279 else 1280 relay_msg = (struct dhcp6_relay *) relayed_msg; 1281 } 1282 else { 1283 dprintf(LOG_ERR, "%s" "invalid relayed message", FNAME); 1284 relayfree(&optinfo->relay_list); 1285 return NULL; 1286 } 1287 } 1288} 1289 1290/* 1291 * Format all of the RELAY-REPL messages and options to send back to the 1292 * client. A RELAY-REPL message and Relay Message option are added for 1293 * each of the relays that were in the RELAY-FORW packet that this is 1294 * in response to. 1295 */ 1296static int 1297dhcp6_set_relay (msg, endptr, optinfo) 1298 struct dhcp6_relay *msg; 1299 struct dhcp6_relay *endptr; 1300 struct dhcp6_optinfo *optinfo; 1301{ 1302 struct relay_listval *relay; 1303 struct dhcp6opt *option; 1304 int relaylen = 0; 1305 u_int16_t type, len; 1306 1307 for (relay = TAILQ_FIRST(&optinfo->relay_list); relay; 1308 relay = TAILQ_NEXT(relay, link)) { 1309 /* bounds check */ 1310 if (((char *) msg) + sizeof (struct dhcp6_relay) >= (char *) endptr) { 1311 dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL", 1312 FNAME); 1313 return -1; 1314 } 1315 1316 memcpy (msg, &relay->relay, sizeof(struct dhcp6_relay)); 1317 1318 relaylen += sizeof(struct dhcp6_relay); 1319 1320 option = (struct dhcp6opt *) (msg + 1); 1321 1322 /* include an Interface Identifier option if it was present in the 1323 original message */ 1324 if (relay->intf_id != NULL) { 1325 /* bounds check */ 1326 if ((((char *) option) + sizeof(struct dhcp6opt) + 1327 relay->intf_id->intf_len) >= (char *) endptr) { 1328 dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL", 1329 FNAME); 1330 return -1; 1331 } 1332 type = htons(DH6OPT_INTERFACE_ID); 1333 memcpy (&option->dh6opt_type, &type, sizeof(type)); 1334 len = htons(relay->intf_id->intf_len); 1335 memcpy (&option->dh6opt_len, &len, sizeof(len)); 1336 memcpy (option + 1, relay->intf_id->intf_id, 1337 relay->intf_id->intf_len); 1338 1339 option = (struct dhcp6opt *)(((char *)(option + 1)) + 1340 relay->intf_id->intf_len); 1341 relaylen += sizeof(struct dhcp6opt) + relay->intf_id->intf_len; 1342 } 1343 1344 /* save a pointer to the relay message option so that it is easier 1345 to fill in the length later */ 1346 relay->option = option; 1347 1348 /* bounds check */ 1349 if ((char *) (option + 1) >= (char *) endptr) { 1350 dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL", 1351 FNAME); 1352 return -1; 1353 } 1354 1355 /* lastly include the Relay Message option, which encapsulates the 1356 message being relayed */ 1357 type = htons(DH6OPT_RELAY_MSG); 1358 memcpy (&option->dh6opt_type, &type, sizeof(type)); 1359 relaylen += sizeof(struct dhcp6opt); 1360 /* dh6opt_len will be set by dhcp6_set_relay_option_len */ 1361 1362 msg = (struct dhcp6_relay *) (option + 1); 1363 } 1364 1365 /* 1366 * if there were no relays, this is an error since this function should 1367 * not have even been called in this case 1368 */ 1369 if (relaylen == 0) 1370 return -1; 1371 else 1372 return relaylen; 1373} 1374 1375/* 1376 * Fill in all of the opt-len fields for the Relay Message options now that 1377 * the length of the entire message is known. 1378 * 1379 * len - the length of the DHCPv6 message to the client (not including any 1380 * relay options) 1381 * 1382 * Precondition: dhcp6_set_relay has already been called and the relay->option 1383 * fields of all of the elements in optinfo->relay_list are 1384 * non-NULL 1385 */ 1386static void 1387dhcp6_set_relay_option_len(optinfo, reply_msg_len) 1388 struct dhcp6_optinfo *optinfo; 1389 int reply_msg_len; 1390{ 1391 struct relay_listval *relay, *last = NULL; 1392 u_int16_t len; 1393 1394 for (relay = TAILQ_LAST(&optinfo->relay_list, relay_list); 1395 relay; relay = TAILQ_PREV(relay, relay_list, link)) { 1396 if (last == NULL) { 1397 len = htons(reply_msg_len); 1398 memcpy (&relay->option->dh6opt_len, &len, sizeof(len)); 1399 last = relay; 1400 } 1401 else { 1402 len = reply_msg_len + (((void *) (last->option + 1)) - 1403 ((void *) (relay->option + 1))); 1404 len = htons(len); 1405 memcpy (&relay->option->dh6opt_len, &len, sizeof(len)); 1406 } 1407 } 1408} 1409