1 /* $KAME: dhcp6relay.c,v 1.62 2006/01/19 04:25:16 jinmei Exp $ */
3 * Copyright (C) 2000 WIDE Project.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the project nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/queue.h>
35 #include <sys/signal.h>
39 #include <net/if_var.h>
42 #include <netinet/in.h>
45 #include <netinet6/in6_var.h>
49 #include <arpa/inet.h>
55 #include <stdlib.h> /* XXX: freebsd2 needs this for opt{arg,ind} */
64 #define DHCP6RELAY_PIDFILE "/var/run/dhcp6relay.pid"
65 static char *pid_file
= DHCP6RELAY_PIDFILE
;
67 static int ssock
; /* socket for relaying to servers */
68 static int csock
; /* socket for clients */
69 static int maxfd
; /* maxi file descriptor for select(2) */
72 static sig_atomic_t sig_flags
= 0;
75 static char *relaydevice
;
76 static char *boundaddr
;
77 static char *serveraddr
= DH6ADDR_ALLSERVER
;
78 static char *scriptpath
;
80 static char *rmsgctlbuf
;
81 static socklen_t rmsgctllen
;
82 static struct msghdr rmh
;
83 static char rdatabuf
[BUFSIZ
];
86 static int mhops
= DHCP6_RELAY_MULTICAST_HOPS
;
88 static struct sockaddr_in6 sa6_server
, sa6_client
;
91 TAILQ_ENTRY(ifid_list
) ilink
;
94 TAILQ_HEAD(, ifid_list
) ifid_list
;
96 TAILQ_ENTRY(prefix_list
) plink
;
97 struct sockaddr_in6 paddr
; /* contains meaningless but enough members */
100 TAILQ_HEAD(, prefix_list
) global_prefixes
; /* list of non-link-local prefixes */
101 static char *global_strings
[] = {
102 /* "fec0::/10", site-local unicast addresses were deprecated */
104 "FC00::/7", /* Unique Local Address (RFC4193) */
108 static void usage
__P((void));
109 static struct prefix_list
*make_prefix
__P((char *));
110 static void relay6_init
__P((int, char *[]));
111 static void relay6_loop
__P((void));
112 static void relay6_recv
__P((int, int));
113 static void process_signals
__P((void));
114 static void relay6_signal
__P((int));
115 static int make_msgcontrol
__P((struct msghdr
*, void *, socklen_t
,
116 struct in6_pktinfo
*, int));
117 static void relay_to_server
__P((struct dhcp6
*, ssize_t
,
118 struct sockaddr_in6
*, char *, unsigned int));
119 static void relay_to_client
__P((struct dhcp6_relay
*, ssize_t
,
121 extern int relay6_script
__P((char *, struct sockaddr_in6
*,
122 struct dhcp6
*, int));
129 "usage: dhcp6relay [-dDf] [-b boundaddr] [-H hoplim] "
130 "[-r relay-IF] [-s serveraddr] [-p pidfile] [-S script] IF ...\n");
144 if ((progname
= strrchr(*argv
, '/')) == NULL
)
149 while((ch
= getopt(argc
, argv
, "b:dDfH:r:s:S:p:")) != -1) {
165 mhops
= (int)strtoul(optarg
, &p
, 10);
166 if (!*optarg
|| *p
) {
167 errx(1, "illegal hop limit: %s", optarg
);
170 if (mhops
<= 0 || mhops
> 255) {
171 errx(1, "illegal hop limit: %d", mhops
);
176 relaydevice
= optarg
;
199 if (relaydevice
== NULL
) {
201 fprintf(stderr
, "you should explicitly specify a "
202 "relaying interface, when you are to "
203 "listen on multiple interfaces");
206 relaydevice
= argv
[0];
209 if (foreground
== 0) {
212 if (daemon(0, 0) < 0)
215 for (fd
= 3; fd
< 1024; fd
++)
218 openlog(progname
, LOG_NDELAY
|LOG_PID
, LOG_DAEMON
);
222 /* dump current PID */
224 if ((pidfp
= fopen(pid_file
, "w")) != NULL
) {
225 fprintf(pidfp
, "%d\n", pid
);
229 relay6_init(argc
, argv
);
231 dprintf(LOG_INFO
, FNAME
, "dhcp6relay started");
237 static struct prefix_list
*
241 struct prefix_list
*pent
;
245 struct in6_addr paddr
;
247 /* make a local copy for safety */
248 if (strlcpy(pstr
, pstr0
, sizeof (pstr
)) >= sizeof (pstr
)) {
249 dprintf(LOG_WARNING
, FNAME
,
250 "prefix string too long (maybe bogus): %s", pstr0
);
254 /* parse the string */
255 if ((p
= strchr(pstr
, '/')) == NULL
)
256 plen
= 128; /* assumes it as a host prefix */
259 dprintf(LOG_WARNING
, FNAME
,
260 "no prefix length (ignored): %s", p
+ 1);
263 plen
= (int)strtoul(p
+ 1, &ep
, 10);
265 dprintf(LOG_WARNING
, FNAME
,
266 "illegal prefix length (ignored): %s", p
+ 1);
271 if (inet_pton(AF_INET6
, pstr
, &paddr
) != 1) {
272 dprintf(LOG_ERR
, FNAME
,
273 "inet_pton failed for %s", pstr
);
277 /* allocate a new entry */
278 if ((pent
= (struct prefix_list
*)malloc(sizeof (*pent
))) == NULL
) {
279 dprintf(LOG_WARNING
, FNAME
, "memory allocation failed");
280 return (NULL
); /* or abort? */
283 /* fill in each member of the entry */
284 memset(pent
, 0, sizeof (*pent
));
285 pent
->paddr
.sin6_family
= AF_INET6
;
287 pent
->paddr
.sin6_len
= sizeof (struct sockaddr_in6
);
289 pent
->paddr
.sin6_addr
= paddr
;
296 relay6_init(int ifnum
, char *iflist
[])
298 struct addrinfo hints
;
299 struct addrinfo
*res
, *res2
;
301 struct ipv6_mreq mreq6
;
302 static struct iovec iov
[2];
304 /* initialize non-link-local prefixes list */
305 TAILQ_INIT(&global_prefixes
);
306 for (i
= 0; global_strings
[i
]; i
++) {
307 struct prefix_list
*p
;
309 if ((p
= make_prefix(global_strings
[i
])) != NULL
)
310 TAILQ_INSERT_TAIL(&global_prefixes
, p
, plink
);
313 /* initialize special socket addresses */
314 memset(&hints
, 0, sizeof (hints
));
315 hints
.ai_family
= PF_INET6
;
316 hints
.ai_socktype
= SOCK_DGRAM
;
317 hints
.ai_protocol
= IPPROTO_UDP
;
318 hints
.ai_flags
= AI_PASSIVE
;
319 error
= getaddrinfo(serveraddr
, DH6PORT_UPSTREAM
, &hints
, &res
);
321 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
322 gai_strerror(error
));
325 if (res
->ai_family
!= PF_INET6
||
326 res
->ai_addrlen
< sizeof (sa6_server
)) {
327 /* this should be impossible, but check for safety */
328 dprintf(LOG_ERR
, FNAME
,
329 "getaddrinfo returned a bogus address: %s",
333 /* XXX: assume only one DHCPv6 server address */
334 memcpy(&sa6_server
, res
->ai_addr
, sizeof (sa6_server
));
337 /* initialize send/receive buffer */
338 iov
[0].iov_base
= (caddr_t
)rdatabuf
;
339 iov
[0].iov_len
= sizeof (rdatabuf
);
342 rmsgctllen
= CMSG_SPACE(sizeof (struct in6_pktinfo
));
343 if ((rmsgctlbuf
= (char *)malloc(rmsgctllen
)) == NULL
) {
344 dprintf(LOG_ERR
, FNAME
, "memory allocation failed");
349 * Setup a socket to communicate with clients.
351 memset(&hints
, 0, sizeof (hints
));
352 hints
.ai_family
= PF_INET6
;
353 hints
.ai_socktype
= SOCK_DGRAM
;
354 hints
.ai_protocol
= IPPROTO_UDP
;
355 hints
.ai_flags
= AI_PASSIVE
;
356 error
= getaddrinfo(NULL
, DH6PORT_UPSTREAM
, &hints
, &res
);
358 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
359 gai_strerror(error
));
362 csock
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
364 dprintf(LOG_ERR
, FNAME
, "socket(csock): %s", strerror(errno
));
370 if (setsockopt(csock
, SOL_SOCKET
, SO_REUSEPORT
,
371 &on
, sizeof(on
)) < 0) {
372 dprintf(LOG_ERR
, FNAME
, "setsockopt(csock, SO_REUSEPORT): %s",
377 if (setsockopt(csock
, IPPROTO_IPV6
, IPV6_V6ONLY
,
378 &on
, sizeof (on
)) < 0) {
379 dprintf(LOG_ERR
, FNAME
, "setsockopt(csock, IPV6_V6ONLY): %s",
384 if (bind(csock
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
385 dprintf(LOG_ERR
, FNAME
, "bind(csock): %s", strerror(errno
));
390 #ifdef IPV6_RECVPKTINFO
391 if (setsockopt(csock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
,
392 &on
, sizeof (on
)) < 0) {
393 dprintf(LOG_ERR
, FNAME
, "setsockopt(IPV6_RECVPKTINFO): %s",
398 if (setsockopt(csock
, IPPROTO_IPV6
, IPV6_PKTINFO
,
399 &on
, sizeof (on
)) < 0) {
400 dprintf(LOG_ERR
, FNAME
, "setsockopt(IPV6_PKTINFO): %s",
407 error
= getaddrinfo(DH6ADDR_ALLAGENT
, 0, &hints
, &res2
);
409 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
410 gai_strerror(error
));
413 memset(&mreq6
, 0, sizeof (mreq6
));
414 memcpy(&mreq6
.ipv6mr_multiaddr
,
415 &((struct sockaddr_in6
*)res2
->ai_addr
)->sin6_addr
,
416 sizeof (mreq6
.ipv6mr_multiaddr
));
418 TAILQ_INIT(&ifid_list
);
419 while (ifnum
-- > 0) {
420 char *ifp
= iflist
[0];
421 struct ifid_list
*ifd
;
423 ifd
= (struct ifid_list
*)malloc(sizeof (*ifd
));
425 dprintf(LOG_WARNING
, FNAME
,
426 "memory allocation failed");
429 memset(ifd
, 0, sizeof (*ifd
));
430 ifd
->ifid
= if_nametoindex(ifp
);
431 if (ifd
->ifid
== 0) {
432 dprintf(LOG_ERR
, FNAME
, "invalid interface %s", ifp
);
435 mreq6
.ipv6mr_interface
= ifd
->ifid
;
437 if (setsockopt(csock
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
,
438 &mreq6
, sizeof (mreq6
))) {
439 dprintf(LOG_ERR
, FNAME
,
440 "setsockopt(csock, IPV6_JOIN_GROUP): %s",
444 TAILQ_INSERT_TAIL(&ifid_list
, ifd
, ilink
);
450 * Setup a socket to relay to servers.
452 relayifid
= if_nametoindex(relaydevice
);
454 dprintf(LOG_ERR
, FNAME
, "invalid interface %s", relaydevice
);
456 * We are not really sure if we need to listen on the downstream
457 * port to receive packets from servers. We'll need to clarify the
458 * specification, but we do for now.
460 hints
.ai_flags
= AI_PASSIVE
;
461 error
= getaddrinfo(boundaddr
, DH6PORT_DOWNSTREAM
, &hints
, &res
);
463 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
464 gai_strerror(error
));
467 memcpy(&sa6_client
, res
->ai_addr
, sizeof (sa6_client
));
468 ssock
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
470 dprintf(LOG_ERR
, FNAME
, "socket(outsock): %s",
478 * Both a relay and a client may run on a single node. If we need to
479 * listen on the downstream port, we need REUSEPORT to avoid conflict.
481 if (setsockopt(ssock
, SOL_SOCKET
, SO_REUSEPORT
,
482 &on
, sizeof (on
)) < 0) {
483 dprintf(LOG_ERR
, FNAME
, "setsockopt(ssock, SO_REUSEPORT): %s",
489 if (setsockopt(ssock
, IPPROTO_IPV6
, IPV6_V6ONLY
,
490 &on
, sizeof (on
)) < 0) {
491 dprintf(LOG_ERR
, FNAME
, "setsockopt(ssock, IPV6_V6ONLY): %s",
496 if (bind(ssock
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
497 dprintf(LOG_ERR
, FNAME
, "bind(ssock): %s", strerror(errno
));
503 #ifdef IPV6_RECVPKTINFO
504 if (setsockopt(ssock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
,
505 &on
, sizeof (on
)) < 0) {
506 dprintf(LOG_ERR
, FNAME
, "setsockopt(IPV6_RECVPKTINFO): %s",
511 if (setsockopt(ssock
, IPPROTO_IPV6
, IPV6_PKTINFO
,
512 &on
, sizeof (on
)) < 0) {
513 dprintf(LOG_ERR
, FNAME
, "setsockopt(IPV6_PKTINFO): %s",
519 if (signal(SIGTERM
, relay6_signal
) == SIG_ERR
) {
520 dprintf(LOG_WARNING
, FNAME
, "failed to set signal: %s",
537 sig_flags
|= SIGF_TERM
;
545 if ((sig_flags
& SIGF_TERM
)) {
561 /* we'd rather use FD_COPY here, but it's not POSIX friendly */
563 FD_SET(csock
, &readfds
);
564 FD_SET(ssock
, &readfds
);
566 e
= select(maxfd
+ 1, &readfds
, NULL
, NULL
, NULL
);
568 case 0: /* impossible in our situation */
569 errx(1, "select returned 0");
572 if (errno
!= EINTR
) {
581 if (FD_ISSET(csock
, &readfds
))
582 relay6_recv(csock
, 1);
584 if (FD_ISSET(ssock
, &readfds
))
585 relay6_recv(ssock
, 0);
590 relay6_recv(s
, fromclient
)
594 struct sockaddr_storage from
;
595 struct in6_pktinfo
*pi
= NULL
;
598 struct ifid_list
*ifd
;
599 char ifname
[IF_NAMESIZE
];
601 rmh
.msg_control
= (caddr_t
)rmsgctlbuf
;
602 rmh
.msg_controllen
= rmsgctllen
;
604 rmh
.msg_name
= &from
;
605 rmh
.msg_namelen
= sizeof (from
);
607 if ((len
= recvmsg(s
, &rmh
, 0)) < 0) {
608 dprintf(LOG_WARNING
, FNAME
, "recvmsg: %s", strerror(errno
));
612 dprintf(LOG_DEBUG
, FNAME
, "from %s, size %d",
613 addr2str((struct sockaddr
*)&from
), len
);
615 if (((struct sockaddr
*)&from
)->sa_family
!= AF_INET6
) {
616 dprintf(LOG_WARNING
, FNAME
,
617 "non-IPv6 packet is received (AF %d) ",
618 ((struct sockaddr
*)&from
)->sa_family
);
622 /* get optional information as ancillary data (if available) */
623 for (cm
= (struct cmsghdr
*)CMSG_FIRSTHDR(&rmh
); cm
;
624 cm
= (struct cmsghdr
*)CMSG_NXTHDR(&rmh
, cm
)) {
625 if (cm
->cmsg_level
!= IPPROTO_IPV6
)
628 switch(cm
->cmsg_type
) {
630 pi
= (struct in6_pktinfo
*)CMSG_DATA(cm
);
635 dprintf(LOG_WARNING
, FNAME
,
636 "failed to get the arrival interface");
639 for (ifd
= TAILQ_FIRST(&ifid_list
); ifd
;
640 ifd
= TAILQ_NEXT(ifd
, ilink
)) {
641 if (pi
->ipi6_ifindex
== ifd
->ifid
)
645 * DHCPv6 relay may receive a DHCPv6 packet from a non-listening
646 * interface, when a DHCPv6 server is running on that interface.
647 * This check prevents such reception.
649 if (ifd
== NULL
&& pi
->ipi6_ifindex
!= relayifid
)
651 if (if_indextoname(pi
->ipi6_ifindex
, ifname
) == NULL
) {
652 dprintf(LOG_WARNING
, FNAME
,
653 "if_indextoname(id = %d): %s",
654 pi
->ipi6_ifindex
, strerror(errno
));
658 /* packet validation */
659 if (len
< sizeof (*dh6
)) {
660 dprintf(LOG_INFO
, FNAME
, "short packet (%d bytes)", len
);
664 dh6
= (struct dhcp6
*)rdatabuf
;
665 dprintf(LOG_DEBUG
, FNAME
, "received %s from %s",
666 dhcp6msgstr(dh6
->dh6_msgtype
), addr2str((struct sockaddr
*)&from
));
669 * Relay the packet according to the type. A client message or
670 * a relay forward message is forwarded to servers (or other relays),
671 * and a relay reply message is forwarded to the intended client.
674 switch (dh6
->dh6_msgtype
) {
684 relay_to_server(dh6
, len
, (struct sockaddr_in6
*)&from
,
685 ifname
, htonl(pi
->ipi6_ifindex
));
687 case DH6_RELAY_REPLY
:
689 * The server may send a relay reply to the client
691 * XXX: need to clarify the port issue
693 relay_to_client((struct dhcp6_relay
*)dh6
, len
,
694 (struct sockaddr
*)&from
);
697 dprintf(LOG_INFO
, FNAME
,
698 "unexpected message (%s) on the client side "
699 "from %s", dhcp6msgstr(dh6
->dh6_msgtype
),
700 addr2str((struct sockaddr
*)&from
));
704 if (dh6
->dh6_msgtype
!= DH6_RELAY_REPLY
) {
705 dprintf(LOG_INFO
, FNAME
,
706 "unexpected message (%s) on the server side"
707 "from %s", dhcp6msgstr(dh6
->dh6_msgtype
),
708 addr2str((struct sockaddr
*)&from
));
711 relay_to_client((struct dhcp6_relay
*)dh6
, len
,
712 (struct sockaddr
*)&from
);
717 make_msgcontrol(mh
, ctlbuf
, buflen
, pktinfo
, hlim
)
721 struct in6_pktinfo
*pktinfo
;
725 socklen_t controllen
;
729 controllen
+= CMSG_SPACE(sizeof (*pktinfo
));
731 controllen
+= CMSG_SPACE(sizeof (hlim
));
732 if (buflen
< controllen
)
735 memset(ctlbuf
, 0, buflen
);
736 mh
->msg_controllen
= controllen
;
737 mh
->msg_control
= ctlbuf
;
739 cm
= (struct cmsghdr
*)CMSG_FIRSTHDR(mh
);
741 cm
->cmsg_len
= CMSG_LEN(sizeof (*pktinfo
));
742 cm
->cmsg_level
= IPPROTO_IPV6
;
743 cm
->cmsg_type
= IPV6_PKTINFO
;
744 memcpy(CMSG_DATA((struct cmsghdr
*)cm
), pktinfo
,
747 cm
= CMSG_NXTHDR(mh
, cm
);
751 cm
->cmsg_len
= CMSG_LEN(sizeof (hlim
));
752 cm
->cmsg_level
= IPPROTO_IPV6
;
753 cm
->cmsg_type
= IPV6_HOPLIMIT
;
754 *(int *)CMSG_DATA((struct cmsghdr
*)cm
) = hlim
;
756 cm
= CMSG_NXTHDR(mh
, cm
); /* just in case */
763 relay_to_server(dh6
, len
, from
, ifname
, ifid
)
766 struct sockaddr_in6
*from
;
770 struct dhcp6_optinfo optinfo
;
771 struct dhcp6_relay
*dh6relay
;
772 struct in6_addr linkaddr
;
773 struct prefix_list
*p
;
774 int optlen
, relaylen
;
777 static struct iovec iov
[2];
778 u_char relaybuf
[sizeof (*dh6relay
) + BUFSIZ
];
779 struct in6_pktinfo pktinfo
;
780 char ctlbuf
[CMSG_SPACE(sizeof (struct in6_pktinfo
))
781 + CMSG_SPACE(sizeof (int))];
784 * Prepare a relay forward option.
786 dhcp6_init_options(&optinfo
);
789 if ((optinfo
.relaymsg_msg
= malloc(len
)) == NULL
) {
790 dprintf(LOG_WARNING
, FNAME
,
791 "failed to allocate memory to copy the original packet: "
792 "%s", strerror(errno
));
795 optinfo
.relaymsg_len
= len
;
796 memcpy(optinfo
.relaymsg_msg
, dh6
, len
);
798 /* Interface-id. We always use this option. */
799 if ((optinfo
.ifidopt_id
= malloc(sizeof (ifid
))) == NULL
) {
800 dprintf(LOG_WARNING
, FNAME
,
801 "failed to allocate memory for IFID: %s", strerror(errno
));
804 optinfo
.ifidopt_len
= sizeof (ifid
);
805 memcpy(optinfo
.ifidopt_id
, &ifid
, sizeof (ifid
));
808 * Construct a relay forward message.
810 memset(relaybuf
, 0, sizeof (relaybuf
));
812 dh6relay
= (struct dhcp6_relay
*)relaybuf
;
813 memset(dh6relay
, 0, sizeof (*dh6relay
));
814 dh6relay
->dh6relay_msgtype
= DH6_RELAY_FORW
;
815 memcpy(&dh6relay
->dh6relay_peeraddr
, &from
->sin6_addr
,
816 sizeof (dh6relay
->dh6relay_peeraddr
));
818 /* find a global address to fill in the link address field */
819 memset(&linkaddr
, 0, sizeof (linkaddr
));
820 for (p
= TAILQ_FIRST(&global_prefixes
); p
; p
= TAILQ_NEXT(p
, plink
)) {
821 if (getifaddr(&linkaddr
, ifname
, &p
->paddr
.sin6_addr
,
822 p
->plen
, 1, IN6_IFF_INVALID
) == 0) /* found */
826 dprintf(LOG_NOTICE
, FNAME
,
827 "failed to find a global address on %s", ifname
);
830 * When relaying a message from a client, we need a global
832 * XXX: this may be too strong for the stateless case, but
833 * the DHCPv6 specification seems to require the behavior.
835 if (dh6
->dh6_msgtype
!= DH6_RELAY_FORW
)
839 if (dh6
->dh6_msgtype
== DH6_RELAY_FORW
) {
840 struct dhcp6_relay
*dh6relay0
= (struct dhcp6_relay
*)dh6
;
842 /* Relaying a Message from a Relay Agent */
845 * If the hop-count in the message is greater than or equal to
846 * HOP_COUNT_LIMIT, the relay agent discards the received
848 * [RFC3315 Section 20.1.2]
850 if (dh6relay0
->dh6relay_hcnt
>= DHCP6_RELAY_HOP_COUNT_LIMIT
) {
851 dprintf(LOG_INFO
, FNAME
, "too many relay forwardings");
855 dh6relay
->dh6relay_hcnt
= dh6relay0
->dh6relay_hcnt
+ 1;
858 * We can keep the link-address field 0, regardless of the
859 * scope of the source address, since we always include
860 * interface-ID option.
863 /* Relaying a Message from a Client */
864 memcpy(&dh6relay
->dh6relay_linkaddr
, &linkaddr
,
865 sizeof (dh6relay
->dh6relay_linkaddr
));
866 dh6relay
->dh6relay_hcnt
= 0;
869 relaylen
= sizeof (*dh6relay
);
870 if ((optlen
= dhcp6_set_options(DH6_RELAY_FORW
,
871 (struct dhcp6opt
*)(dh6relay
+ 1),
872 (struct dhcp6opt
*)(relaybuf
+ sizeof (relaybuf
)),
874 dprintf(LOG_INFO
, FNAME
,
875 "failed to construct relay options");
881 * Forward the message.
883 memset(&mh
, 0, sizeof (mh
));
884 iov
[0].iov_base
= relaybuf
;
885 iov
[0].iov_len
= relaylen
;
888 mh
.msg_name
= &sa6_server
;
889 mh
.msg_namelen
= sizeof (sa6_server
);
890 if (IN6_IS_ADDR_MULTICAST(&sa6_server
.sin6_addr
)) {
891 memset(&pktinfo
, 0, sizeof (pktinfo
));
892 pktinfo
.ipi6_ifindex
= relayifid
;
893 if (make_msgcontrol(&mh
, ctlbuf
, sizeof (ctlbuf
),
895 dprintf(LOG_WARNING
, FNAME
,
896 "failed to make message control data");
901 if ((cc
= sendmsg(ssock
, &mh
, 0)) < 0) {
902 dprintf(LOG_WARNING
, FNAME
,
903 "sendmsg %s failed: %s",
904 addr2str((struct sockaddr
*)&sa6_server
), strerror(errno
));
905 } else if (cc
!= relaylen
) {
906 dprintf(LOG_WARNING
, FNAME
,
907 "failed to send a complete packet to %s",
908 addr2str((struct sockaddr
*)&sa6_server
));
910 dprintf(LOG_DEBUG
, FNAME
,
911 "relay a message to a server %s",
912 addr2str((struct sockaddr
*)&sa6_server
));
916 dhcp6_clear_options(&optinfo
);
920 relay_to_client(dh6relay
, len
, from
)
921 struct dhcp6_relay
*dh6relay
;
923 struct sockaddr
*from
;
925 struct dhcp6_optinfo optinfo
;
926 struct sockaddr_in6 peer
;
928 char ifnamebuf
[IFNAMSIZ
];
933 struct in6_pktinfo pktinfo
;
934 static struct iovec iov
[2];
935 char ctlbuf
[CMSG_SPACE(sizeof (struct in6_pktinfo
))];
937 dprintf(LOG_DEBUG
, FNAME
,
938 "dhcp6 relay reply: hop=%d, linkaddr=%s, peeraddr=%s",
939 dh6relay
->dh6relay_hcnt
,
940 in6addr2str(&dh6relay
->dh6relay_linkaddr
, 0),
941 in6addr2str(&dh6relay
->dh6relay_peeraddr
, 0));
944 * parse and validate options in the relay reply message.
946 dhcp6_init_options(&optinfo
);
947 if (dhcp6_get_options((struct dhcp6opt
*)(dh6relay
+ 1),
948 (struct dhcp6opt
*)((char *)dh6relay
+ len
), &optinfo
) < 0) {
949 dprintf(LOG_INFO
, FNAME
, "failed to parse options");
953 /* A relay reply message must include a relay message option */
954 if (optinfo
.relaymsg_msg
== NULL
) {
955 dprintf(LOG_INFO
, FNAME
, "relay reply message from %s "
956 "without a relay message", addr2str(from
));
960 /* minimum validation for the inner message */
961 if (optinfo
.relaymsg_len
< sizeof (struct dhcp6
)) {
962 dprintf(LOG_INFO
, FNAME
, "short relay message from %s",
968 * Extract interface ID which should be included in relay reply
972 if (optinfo
.ifidopt_id
) {
973 if (optinfo
.ifidopt_len
!= sizeof (ifid
)) {
974 dprintf(LOG_INFO
, FNAME
,
975 "unexpected length (%d) for Interface ID from %s",
976 optinfo
.ifidopt_len
, addr2str(from
));
979 memcpy(&ifid
, optinfo
.ifidopt_id
, sizeof (ifid
));
982 /* validation for ID */
983 if ((if_indextoname(ifid
, ifnamebuf
)) == NULL
) {
984 dprintf(LOG_INFO
, FNAME
,
985 "invalid interface ID: %x", ifid
);
990 dprintf(LOG_INFO
, FNAME
,
991 "Interface ID is not included from %s", addr2str(from
));
993 * the responding server should be buggy, but we deal with it.
998 * If we fail, try to get the interface from the link address.
1001 !IN6_IS_ADDR_UNSPECIFIED(&dh6relay
->dh6relay_linkaddr
) &&
1002 !IN6_IS_ADDR_LINKLOCAL(&dh6relay
->dh6relay_linkaddr
)) {
1003 if (getifidfromaddr(&dh6relay
->dh6relay_linkaddr
, &ifid
))
1008 dprintf(LOG_INFO
, FNAME
, "failed to determine relay link");
1013 dh6
= (struct dhcp6
*) optinfo
.relaymsg_msg
;
1014 if (dh6
->dh6_msgtype
!= DH6_RELAY_REPLY
) {
1018 * change dst port to server/relay port, since it's a
1019 * reply to relay, not to a client
1021 peer
.sin6_port
= htons(547); /* DH6PORT_UPSTREAM */
1023 memcpy(&peer
.sin6_addr
, &dh6relay
->dh6relay_peeraddr
,
1024 sizeof (peer
.sin6_addr
));
1025 if (IN6_IS_ADDR_LINKLOCAL(&peer
.sin6_addr
))
1026 peer
.sin6_scope_id
= ifid
; /* XXX: we assume a 1to1 map */
1028 /* construct a message structure specifying the outgoing interface */
1029 memset(&mh
, 0, sizeof (mh
));
1030 iov
[0].iov_base
= optinfo
.relaymsg_msg
;
1031 iov
[0].iov_len
= optinfo
.relaymsg_len
;
1034 mh
.msg_name
= &peer
;
1035 mh
.msg_namelen
= sizeof (peer
);
1036 memset(&pktinfo
, 0, sizeof (pktinfo
));
1037 pktinfo
.ipi6_ifindex
= ifid
;
1038 if (make_msgcontrol(&mh
, ctlbuf
, sizeof (ctlbuf
), &pktinfo
, 0)) {
1039 dprintf(LOG_WARNING
, FNAME
,
1040 "failed to make message control data");
1045 if ((cc
= sendmsg(csock
, &mh
, 0)) < 0) {
1046 dprintf(LOG_WARNING
, FNAME
,
1047 "sendmsg to %s failed: %s",
1048 addr2str((struct sockaddr
*)&peer
), strerror(errno
));
1049 } else if (cc
!= optinfo
.relaymsg_len
) {
1050 dprintf(LOG_WARNING
, FNAME
,
1051 "failed to send a complete packet to %s",
1052 addr2str((struct sockaddr
*)&peer
));
1054 dprintf(LOG_DEBUG
, FNAME
,
1055 "relay a message to a client %s",
1056 addr2str((struct sockaddr
*)&peer
));
1059 if (relayed
&& scriptpath
!= NULL
)
1060 relay6_script(scriptpath
, &peer
, dh6
, optinfo
.relaymsg_len
);
1063 dhcp6_clear_options(&optinfo
);