1/*	$OpenBSD: dhcrelay6.c,v 1.4 2021/10/24 21:24:18 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 2017 Rafael Zalamena <rzalamena@openbsd.org>
5 * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org>
6 * Copyright (c) 1997, 1998, 1999 The Internet Software Consortium.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 *    of its contributors may be used to endorse or promote products derived
20 *    from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises.  To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''.  To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
41 */
42
43#include <sys/socket.h>
44
45#include <arpa/inet.h>
46
47#include <net/if.h>
48#include <netinet/in.h>
49#include <netinet/ip.h>
50#include <netinet/ip6.h>
51#include <netinet/udp.h>
52#include <netinet/if_ether.h>
53
54#include <errno.h>
55#include <fcntl.h>
56#include <netdb.h>
57#include <paths.h>
58#include <pwd.h>
59#include <stdarg.h>
60#include <stdlib.h>
61#include <stdio.h>
62#include <stdint.h>
63#include <string.h>
64#include <syslog.h>
65#include <time.h>
66#include <unistd.h>
67
68#include "dhcp.h"
69#include "dhcpd.h"
70#include "log.h"
71
72/*
73 * RFC 3315 Section 5.1 Multicast Addresses:
74 * All_DHCP_Relay_Agents_and_Servers: FF02::1:2
75 * All_DHCP_Servers: FF05::1:3
76 */
77struct in6_addr		 in6alldhcprelay = {
78	{{ 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0, 0x02 }}
79};
80struct in6_addr		 in6alldhcp = {
81	{{ 0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0, 0x03 }}
82};
83
84__dead void usage(void);
85struct server_list *parse_destination(const char *);
86int	 rdaemon(int);
87void	 relay6_setup(void);
88int	 s6fromaddr(struct sockaddr_in6 *, const char *, const char *, int);
89char	*print_hw_addr(int, int, unsigned char *);
90const char *dhcp6type2str(uint8_t);
91int	 relay6_pushrelaymsg(struct packet_ctx *, struct interface_info *,
92	    uint8_t *, size_t *, size_t);
93int	 relay6_poprelaymsg(struct packet_ctx *, struct interface_info **,
94	    uint8_t *, size_t *);
95void	 rai_configure(struct packet_ctx *, struct interface_info *);
96void	 relay6_logsrcaddr(struct packet_ctx *, struct interface_info *,
97	    uint8_t);
98void	 relay6(struct interface_info *, void *, size_t,
99	    struct packet_ctx *);
100void	 mcast6_recv(struct protocol *);
101
102/* Shared variables */
103int			 clientsd;
104int			 serversd;
105int			 oflag;
106time_t			 cur_time;
107
108struct intfq		 intflist;
109struct serverq		 svlist;
110struct interface_info	*interfaces;
111char			*rai_data;
112size_t			 rai_datalen;
113uint32_t		 enterpriseno = OPENBSD_ENTERPRISENO;
114char			*remote_data;
115size_t			 remote_datalen;
116enum dhcp_relay_mode	 drm = DRM_LAYER3;
117
118__dead void
119usage(void)
120{
121	extern char	*__progname;
122
123	fprintf(stderr, "usage: %s [-dlov] [-E enterprise-number] "
124	    "[-I interface-id] [-R remote-id]\n"
125	    "\t-i interface destination ...\n",
126	    __progname);
127	exit(1);
128}
129
130struct server_list *
131parse_destination(const char *dest)
132{
133	struct server_list	*sp;
134	const char		*ifname;
135	char			 buf[128];
136
137	if ((sp = calloc(1, sizeof(*sp))) == NULL)
138		fatal("calloc");
139	TAILQ_INSERT_HEAD(&svlist, sp, entry);
140
141	/* Detect interface only destinations. */
142	if ((sp->intf = iflist_getbyname(dest)) != NULL)
143		return sp;
144
145	/* Split address from interface and save it. */
146	ifname = strchr(dest, '%');
147	if (ifname == NULL)
148		fatalx("%s doesn't specify an output interface", dest);
149
150	if (strlcpy(buf, dest, sizeof(buf)) >= sizeof(buf))
151		fatalx("%s is an invalid IPv6 address", dest);
152
153	/* Remove '%' from the address string. */
154	buf[ifname - dest] = 0;
155	if ((sp->intf = iflist_getbyname(ifname + 1)) == NULL)
156		fatalx("interface '%s' not found", ifname);
157	if (s6fromaddr(ss2sin6(&sp->to), buf,
158	    DHCP6_SERVER_PORT_STR, 1) == -1)
159		fatalx("%s: unknown host", buf);
160
161	/*
162	 * User configured a non-local address, we must require a
163	 * proper address to route this.
164	 */
165	if (!IN6_IS_ADDR_LINKLOCAL(&ss2sin6(&sp->to)->sin6_addr))
166		sp->siteglobaladdr = 1;
167
168	return sp;
169}
170
171int
172main(int argc, char *argv[])
173{
174	int			 devnull = -1, daemonize = 1, debug = 0;
175	const char		*errp;
176	struct passwd		*pw;
177	int			 ch;
178
179	log_init(1, LOG_DAEMON);	/* log to stderr until daemonized */
180	log_setverbose(1);
181
182	setup_iflist();
183
184	while ((ch = getopt(argc, argv, "dE:I:i:loR:v")) != -1) {
185		switch (ch) {
186		case 'd':
187			daemonize = 0;
188			break;
189		case 'E':
190			enterpriseno = strtonum(optarg, 1, UINT32_MAX, &errp);
191			if (errp != NULL)
192				fatalx("invalid enterprise number: %s", errp);
193			break;
194		case 'I':
195			rai_data = optarg;
196			rai_datalen = strlen(optarg);
197			if (rai_datalen == 0)
198				fatalx("can't use empty Interface-ID");
199			break;
200		case 'i':
201			if (interfaces != NULL)
202				usage();
203
204			interfaces = iflist_getbyname(optarg);
205			if (interfaces == NULL)
206				fatalx("interface '%s' not found", optarg);
207			break;
208		case 'l':
209			drm = DRM_LAYER2;
210			break;
211		case 'o':
212			oflag = 1;
213			break;
214		case 'R':
215			remote_data = optarg;
216			remote_datalen = strlen(remote_data);
217			if (remote_datalen == 0)
218				fatalx("can't use empty Remote-ID");
219			break;
220		case 'v':
221			daemonize = 0;
222			debug = 1;
223			break;
224
225		default:
226			usage();
227		}
228	}
229
230	argc -= optind;
231	argv += optind;
232	while (argc > 0) {
233		parse_destination(argv[0]);
234		argc--;
235		argv++;
236	}
237
238	if (daemonize) {
239		devnull = open(_PATH_DEVNULL, O_RDWR);
240		if (devnull == -1)
241			fatal("open(%s)", _PATH_DEVNULL);
242	}
243	if (interfaces == NULL)
244		fatalx("no interface given");
245	if (TAILQ_EMPTY(&svlist))
246		fatalx("no destination selected");
247
248	relay6_setup();
249	bootp_packet_handler = relay6;
250
251	tzset();
252	time(&cur_time);
253
254	if ((pw = getpwnam(DHCRELAY6_USER)) == NULL)
255		fatalx("user \"%s\" not found", DHCRELAY6_USER);
256	if (chroot(pw->pw_dir) == -1)
257		fatal("chroot");
258	if (chdir("/") == -1)
259		fatal("chdir(\"/\")");
260	if (setgroups(1, &pw->pw_gid) ||
261	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
262	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
263		fatal("can't drop privileges");
264
265	if (daemonize) {
266		if (rdaemon(devnull) == -1)
267			fatal("rdaemon");
268	}
269	log_init(daemonize == 0, LOG_DAEMON);	/* stop loggoing to stderr */
270	log_setverbose(debug);
271
272	if (pledge("inet stdio route", NULL) == -1)
273		fatalx("pledge");
274
275	dispatch();
276	/* not reached */
277
278	exit(0);
279}
280
281int
282rdaemon(int devnull)
283{
284	if (devnull == -1) {
285		errno = EBADF;
286		return (-1);
287	}
288	if (fcntl(devnull, F_GETFL) == -1)
289		return (-1);
290
291	switch (fork()) {
292	case -1:
293		return (-1);
294	case 0:
295		break;
296	default:
297		_exit(0);
298	}
299
300	if (setsid() == -1)
301		return (-1);
302
303	(void)dup2(devnull, STDIN_FILENO);
304	(void)dup2(devnull, STDOUT_FILENO);
305	(void)dup2(devnull, STDERR_FILENO);
306	if (devnull > 2)
307		(void)close(devnull);
308
309	return (0);
310}
311
312int
313s6fromaddr(struct sockaddr_in6 *sin6, const char *addr, const char *serv,
314    int passive)
315{
316	struct sockaddr_in6	*sin6p;
317	struct addrinfo		*aip;
318	struct addrinfo		 ai;
319	int			 rv;
320
321	memset(&ai, 0, sizeof(ai));
322	ai.ai_family = PF_INET6;
323	ai.ai_socktype = SOCK_DGRAM;
324	ai.ai_protocol = IPPROTO_UDP;
325	ai.ai_flags = (passive) ? AI_PASSIVE : 0;
326	if ((rv = getaddrinfo(addr, serv, &ai, &aip)) != 0) {
327		log_debug("getaddrinfo: %s", gai_strerror(rv));
328		return -1;
329	}
330
331	sin6p = (struct sockaddr_in6 *)aip->ai_addr;
332	*sin6 = *sin6p;
333
334	freeaddrinfo(aip);
335
336	return 0;
337}
338
339void
340relay6_setup(void)
341{
342	struct interface_info	*intf;
343	struct server_list	*sp;
344	int			 flag = 1;
345	struct sockaddr_in6	 sin6;
346	struct ipv6_mreq	 mreq6;
347
348	/* Don't allow disabled interfaces. */
349	TAILQ_FOREACH(sp, &svlist, entry) {
350		if (sp->intf == NULL)
351			continue;
352
353		if (sp->intf->dead)
354			fatalx("interface '%s' is down", sp->intf->name);
355	}
356
357	/* Check for layer 2 dependencies. */
358	if (drm == DRM_LAYER2) {
359		TAILQ_FOREACH(sp, &svlist, entry) {
360			sp->intf = register_interface(sp->intf->name,
361			    got_one);
362			if (sp->intf == NULL)
363				fatalx("destination interface "
364				    "registration failed");
365		}
366		interfaces = register_interface(interfaces->name, got_one);
367		if (interfaces == NULL)
368			fatalx("input interface not configured");
369
370		return;
371	}
372
373	/*
374	 * Layer 3 requires at least one IPv6 address on all configured
375	 * interfaces.
376	 */
377	TAILQ_FOREACH(sp, &svlist, entry) {
378		if (!sp->intf->ipv6)
379			fatalx("%s: no IPv6 address configured",
380			    sp->intf->name);
381
382		if (sp->siteglobaladdr && !sp->intf->gipv6)
383			fatalx("%s: no IPv6 site/global address configured",
384			    sp->intf->name);
385	}
386	if (!interfaces->ipv6)
387		fatalx("%s: no IPv6 address configured", interfaces->name);
388
389	/* Setup the client side socket. */
390	clientsd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
391	if (clientsd == -1)
392		fatal("socket");
393
394	if (setsockopt(clientsd, SOL_SOCKET, SO_REUSEPORT, &flag,
395	    sizeof(flag)) == -1)
396		fatal("setsockopt(SO_REUSEPORT)");
397
398	if (s6fromaddr(&sin6, NULL, DHCP6_SERVER_PORT_STR, 1) == -1)
399		fatalx("s6fromaddr");
400	if (bind(clientsd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
401		fatal("bind");
402
403	if (setsockopt(clientsd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &flag,
404	    sizeof(flag)) == -1)
405		fatal("setsockopt(IPV6_RECVPKTINFO)");
406
407	memset(&mreq6, 0, sizeof(mreq6));
408	if (s6fromaddr(&sin6, DHCP6_ADDR_RELAYSERVER, NULL, 0) == -1)
409		fatalx("s6fromaddr");
410	memcpy(&mreq6.ipv6mr_multiaddr, &sin6.sin6_addr,
411	    sizeof(mreq6.ipv6mr_multiaddr));
412	TAILQ_FOREACH(intf, &intflist, entry) {
413		/* Skip interfaces without IPv6. */
414		if (!intf->ipv6)
415			continue;
416
417		mreq6.ipv6mr_interface = intf->index;
418		if (setsockopt(clientsd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
419		    &mreq6, sizeof(mreq6)) == -1)
420			fatal("setsockopt(IPV6_JOIN_GROUP)");
421	}
422
423	add_protocol("clientsd", clientsd, mcast6_recv, &clientsd);
424
425	/* Setup the server side socket. */
426	serversd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
427	if (serversd == -1)
428		fatal("socket");
429
430	if (setsockopt(serversd, SOL_SOCKET, SO_REUSEPORT, &flag,
431	    sizeof(flag)) == -1)
432		fatal("setsockopt(SO_REUSEPORT)");
433
434	if (s6fromaddr(&sin6, NULL, DHCP6_SERVER_PORT_STR, 1) == -1)
435		fatalx("s6fromaddr");
436	if (bind(serversd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
437		fatal("bind");
438
439	if (setsockopt(serversd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &flag,
440	    sizeof(flag)) == -1)
441		fatal("setsockopt(IPV6_RECVPKTINFO)");
442
443	add_protocol("serversd", serversd, mcast6_recv, &serversd);
444}
445
446char *
447print_hw_addr(int htype, int hlen, unsigned char *data)
448{
449	static char	 habuf[49];
450	char		*s = habuf;
451	int		 i, j, slen = sizeof(habuf);
452
453	if (htype == 0 || hlen == 0) {
454bad:
455		strlcpy(habuf, "<null>", sizeof habuf);
456		return habuf;
457	}
458
459	for (i = 0; i < hlen; i++) {
460		j = snprintf(s, slen, "%02x", data[i]);
461		if (j <= 0 || j >= slen)
462			goto bad;
463		j = strlen (s);
464		s += j;
465		slen -= (j + 1);
466		*s++ = ':';
467	}
468	*--s = '\0';
469	return habuf;
470}
471
472const char *
473v6addr2str(struct in6_addr *addr)
474{
475	static int	bufpos = 0;
476	static char	buf[3][256];
477
478	bufpos = (bufpos + 1) % 3;
479	buf[bufpos][0] = '[';
480	if (inet_ntop(AF_INET6, addr, &buf[bufpos][1],
481	    sizeof(buf[bufpos])) == NULL)
482		return "[unknown]";
483
484	strlcat(buf[bufpos], "]", sizeof(buf[bufpos]));
485
486	return buf[bufpos];
487}
488
489const char *
490dhcp6type2str(uint8_t msgtype)
491{
492	switch (msgtype) {
493	case DHCP6_MT_REQUEST:
494		return "REQUEST";
495	case DHCP6_MT_RENEW:
496		return "RENEW";
497	case DHCP6_MT_REBIND:
498		return "REBIND";
499	case DHCP6_MT_RELEASE:
500		return "RELEASE";
501	case DHCP6_MT_DECLINE:
502		return "DECLINE";
503	case DHCP6_MT_INFORMATIONREQUEST:
504		return "INFORMATION-REQUEST";
505	case DHCP6_MT_SOLICIT:
506		return "SOLICIT";
507	case DHCP6_MT_ADVERTISE:
508		return "ADVERTISE";
509	case DHCP6_MT_CONFIRM:
510		return "CONFIRM";
511	case DHCP6_MT_REPLY:
512		return "REPLY";
513	case DHCP6_MT_RECONFIGURE:
514		return "RECONFIGURE";
515	case DHCP6_MT_RELAYREPL:
516		return "RELAY-REPLY";
517	case DHCP6_MT_RELAYFORW:
518		return "RELAY-FORWARD";
519	default:
520		return "UNKNOWN";
521	}
522}
523
524int
525relay6_pushrelaymsg(struct packet_ctx *pc, struct interface_info *intf,
526    uint8_t *p, size_t *plen, size_t ptotal)
527{
528	struct dhcp6_relay_packet	*dsr;
529	struct dhcp6_option		*dso;
530	size_t				 rmlen, dhcplen, optoff;
531	size_t				 railen, remotelen;
532
533	if (pc->pc_raidata != NULL)
534		railen = sizeof(*dso) + pc->pc_raidatalen;
535	else
536		railen = 0;
537
538	if (pc->pc_remote)
539		remotelen = sizeof(*dso) + ENTERPRISENO_LEN +
540		    pc->pc_remotelen;
541	else
542		remotelen = 0;
543
544	/*
545	 * Check if message bigger than MTU and log (RFC 6221
546	 * Section 5.3.1).
547	 */
548	dhcplen = sizeof(*dsr) + railen + remotelen + sizeof(*dso) + *plen;
549	rmlen = sizeof(struct ether_header) + sizeof(struct ip6_hdr) +
550	    sizeof(struct udphdr) + dhcplen;
551	if (rmlen > ptotal) {
552		log_info("Relay message too big");
553		return -1;
554	}
555
556	/* Move the DHCP payload to option. */
557	optoff = sizeof(*dsr) + railen + remotelen + sizeof(*dso);
558	memmove(p + optoff, p, *plen);
559
560	/* Write the new DHCP packet header for relay-message. */
561	dsr = (struct dhcp6_relay_packet *)p;
562	dsr->dsr_msgtype = DHCP6_MT_RELAYFORW;
563
564	/*
565	 * When the destination is All_DHCP_Relay_Agents_and_Servers we
566	 * start the hop count from zero, otherwise set it to
567	 * DHCP6_HOP_LIMIT to limit the packet to a single network.
568	 */
569	if (memcmp(&ss2sin6(&pc->pc_dst)->sin6_addr,
570	    &in6alldhcprelay, sizeof(in6alldhcprelay)) == 0)
571		dsr->dsr_hopcount = 0;
572	else
573		dsr->dsr_hopcount = DHCP6_HOP_LIMIT;
574
575	/*
576	 * XXX RFC 6221 Section 6.1: layer 2 mode does not set
577	 * linkaddr, but we'll use our link-local always to identify the
578	 * interface where the packet came in so we don't need to keep
579	 * the interface addresses updated.
580	 */
581	dsr->dsr_linkaddr = intf->linklocal;
582
583	memcpy(&dsr->dsr_peer, &ss2sin6(&pc->pc_src)->sin6_addr,
584	    sizeof(dsr->dsr_peer));
585
586	/* Append Interface-ID DHCP option to identify this segment. */
587	if (railen > 0) {
588		dso = dsr->dsr_options;
589		dso->dso_code = htons(DHCP6_OPT_INTERFACEID);
590		dso->dso_length = htons(pc->pc_raidatalen);
591		memcpy(dso->dso_data, pc->pc_raidata, pc->pc_raidatalen);
592	}
593
594	/* Append the Remote-ID DHCP option to identify this segment. */
595	if (remotelen > 0) {
596		dso = (struct dhcp6_option *)
597		    ((uint8_t *)dsr->dsr_options + railen);
598		dso->dso_code = htons(DHCP6_OPT_REMOTEID);
599		dso->dso_length =
600		    htons(sizeof(pc->pc_enterpriseno) + pc->pc_remotelen);
601		memcpy(dso->dso_data, &pc->pc_enterpriseno,
602		    sizeof(pc->pc_enterpriseno));
603		memcpy(dso->dso_data + sizeof(pc->pc_enterpriseno),
604		    pc->pc_remote, pc->pc_remotelen);
605	}
606
607	/* Write the Relay-Message option header. */
608	dso = (struct dhcp6_option *)
609	    ((uint8_t *)dsr->dsr_options + railen + remotelen);
610	dso->dso_code = htons(DHCP6_OPT_RELAY_MSG);
611	dso->dso_length = htons(*plen);
612
613	/* Update the packet length. */
614	*plen = dhcplen;
615
616	return 0;
617}
618
619int
620relay6_poprelaymsg(struct packet_ctx *pc, struct interface_info **intf,
621    uint8_t *p, size_t *plen)
622{
623	struct dhcp6_relay_packet	*dsr = (struct dhcp6_relay_packet *)p;
624	struct dhcp6_packet		*ds = NULL;
625	struct dhcp6_option		*dso;
626	struct in6_addr			 linkaddr;
627	size_t				 pleft = *plen, ifnamelen = 0;
628	size_t				 dsolen, dhcplen = 0;
629	uint16_t			 optcode;
630	char				 ifname[64];
631
632	*intf = NULL;
633
634	/* Sanity check: this is a relay message of the right type. */
635	if (dsr->dsr_msgtype != DHCP6_MT_RELAYREPL) {
636		log_debug("Invalid relay-message (%s) to pop",
637		    dhcp6type2str(dsr->dsr_msgtype));
638		return -1;
639	}
640
641	/* Set the client address based on relay message. */
642	ss2sin6(&pc->pc_dst)->sin6_addr = dsr->dsr_peer;
643	linkaddr = dsr->dsr_linkaddr;
644
645	dso = dsr->dsr_options;
646	pleft -= sizeof(*dsr);
647	while (pleft > sizeof(*dso)) {
648		optcode = ntohs(dso->dso_code);
649		dsolen = sizeof(*dso) + ntohs(dso->dso_length);
650
651		/* Sanity check: do we have the payload? */
652		if (dsolen > pleft) {
653			log_debug("invalid packet: payload greater than "
654			    "packet content (%ld, bytes left %ld)",
655			    dsolen, pleft);
656			return -1;
657		}
658
659		/* Use the interface suggested by the packet. */
660		if (optcode == DHCP6_OPT_INTERFACEID) {
661			ifnamelen = dsolen - sizeof(*dso);
662			if (ifnamelen >= sizeof(ifname)) {
663				log_info("received interface id with "
664				    "truncated interface name");
665				ifnamelen = sizeof(ifname) - 1;
666			}
667
668			memcpy(ifname, dso->dso_data, ifnamelen);
669			ifname[ifnamelen] = 0;
670
671			dso = (struct dhcp6_option *)
672			    ((uint8_t *)dso + dsolen);
673			pleft -= dsolen;
674			continue;
675		}
676
677		/* Ignore unsupported options. */
678		if (optcode != DHCP6_OPT_RELAY_MSG) {
679			log_debug("ignoring option type %d", optcode);
680			dso = (struct dhcp6_option *)
681			    ((uint8_t *)dso + dsolen);
682			pleft -= dsolen;
683			continue;
684		}
685
686		/* Save the pointer for the DHCP payload. */
687		ds = (struct dhcp6_packet *)dso->dso_data;
688		dhcplen = ntohs(dso->dso_length);
689
690		dso = (struct dhcp6_option *)((uint8_t *)dso + dsolen);
691		pleft -= dsolen;
692	}
693	if (ds == NULL || dhcplen == 0) {
694		log_debug("Could not find relay-message option");
695		return -1;
696	}
697
698	/* Move the encapsulated DHCP payload. */
699	memmove(p, ds, dhcplen);
700	*plen = dhcplen;
701
702	/*
703	 * If the new message is for the client, we must change the
704	 * destination port to the client's, otherwise keep the port
705	 * for the next relay.
706	 */
707	ds = (struct dhcp6_packet *)p;
708	if (ds->ds_msgtype != DHCP6_MT_RELAYREPL)
709		ss2sin6(&pc->pc_dst)->sin6_port =
710		    htons(DHCP6_CLIENT_PORT);
711
712	/* No Interface-ID specified. */
713	if (ifnamelen == 0)
714		goto use_linkaddr;
715
716	/* Look out for the specified interface, */
717	if ((*intf = iflist_getbyname(ifname)) == NULL) {
718		log_debug("  Interface-ID found, but no interface matches.");
719
720		/*
721		 * Use client interface as fallback, but try
722		 * link-address (if any) before giving up.
723		 */
724		*intf = interfaces;
725	}
726
727 use_linkaddr:
728	/* Use link-addr to determine output interface if present. */
729	if (memcmp(&linkaddr, &in6addr_any, sizeof(linkaddr)) != 0) {
730		if ((*intf = iflist_getbyaddr6(&linkaddr)) != NULL)
731			return 0;
732
733		log_debug("Could not find interface using "
734		    "address %s", v6addr2str(&linkaddr));
735	}
736
737	return 0;
738}
739
740void
741rai_configure(struct packet_ctx *pc, struct interface_info *intf)
742{
743	if (remote_data != NULL) {
744		pc->pc_remote = remote_data;
745		pc->pc_remotelen = remote_datalen;
746		pc->pc_enterpriseno = htonl(enterpriseno);
747	}
748
749	/* Layer-2 must include Interface-ID (Option 18). */
750	if (drm == DRM_LAYER2)
751		goto select_rai;
752
753	/* User did not configure Interface-ID. */
754	if (oflag == 0)
755		return;
756
757 select_rai:
758	if (rai_data == NULL) {
759		pc->pc_raidata = intf->name;
760		pc->pc_raidatalen = strlen(intf->name);
761	} else {
762		pc->pc_raidata = rai_data;
763		pc->pc_raidatalen = rai_datalen;
764	}
765}
766
767void
768relay6_logsrcaddr(struct packet_ctx *pc, struct interface_info *intf,
769    uint8_t msgtype)
770{
771	const char		*type;
772
773	type = (msgtype == DHCP6_MT_RELAYREPL) ? "reply" : "forward";
774	if (drm == DRM_LAYER2)
775		log_info("forwarded relay-%s for %s to %s",
776		    type, print_hw_addr(pc->pc_htype, pc->pc_hlen,
777		    pc->pc_smac), intf->name);
778	else
779		log_info("forwarded relay-%s for %s to %s%%%s",
780		    type,
781		    v6addr2str(&ss2sin6(&pc->pc_srcorig)->sin6_addr),
782		    v6addr2str(&ss2sin6(&pc->pc_dst)->sin6_addr),
783		    intf->name);
784}
785
786void
787relay6(struct interface_info *intf, void *p, size_t plen,
788    struct packet_ctx *pc)
789{
790	struct dhcp6_packet		*ds = (struct dhcp6_packet *)p;
791	struct dhcp6_relay_packet	*dsr = (struct dhcp6_relay_packet *)p;
792	struct interface_info		*dstif = NULL;
793	struct server_list		*sp;
794	size_t				 buflen = plen;
795	int				 clientdir = (intf != interfaces);
796	uint8_t				 msgtype, hopcount = 0;
797
798	/* Sanity check: we have at least the DHCP header. */
799	if (plen < (int)sizeof(*ds)) {
800		log_debug("invalid packet size");
801		return;
802	}
803
804	/* Set Relay Agent Information fields. */
805	rai_configure(pc, intf);
806
807	/*
808	 * RFC 3315 section 20 relay messages:
809	 * For client messages prepend a new DHCP payload with the
810	 * relay-forward, otherwise update the DHCP relay header.
811	 */
812	msgtype = ds->ds_msgtype;
813
814	log_debug("%s: received %s from %s",
815	    intf->name, dhcp6type2str(msgtype),
816	    v6addr2str(&ss2sin6(&pc->pc_src)->sin6_addr));
817
818	switch (msgtype) {
819	case DHCP6_MT_ADVERTISE:
820	case DHCP6_MT_REPLY:
821	case DHCP6_MT_RECONFIGURE:
822		/*
823		 * Don't forward reply packets coming from the client
824		 * interface.
825		 *
826		 * RFC 6221 Section 6.1.1.
827		 */
828		if (clientdir == 0) {
829			log_debug("  dropped reply in opposite direction");
830			return;
831		}
832		/* FALLTHROUGH */
833
834	case DHCP6_MT_REQUEST:
835	case DHCP6_MT_RENEW:
836	case DHCP6_MT_REBIND:
837	case DHCP6_MT_RELEASE:
838	case DHCP6_MT_DECLINE:
839	case DHCP6_MT_INFORMATIONREQUEST:
840	case DHCP6_MT_SOLICIT:
841	case DHCP6_MT_CONFIRM:
842		/*
843		 * Encapsulate the client/server message with the
844		 * relay-message header.
845		 */
846		if (relay6_pushrelaymsg(pc, intf, (uint8_t *)p,
847		    &buflen, DHCP_MTU_MAX) == -1) {
848			log_debug("  message encapsulation failed");
849			return;
850		}
851		break;
852
853	case DHCP6_MT_RELAYREPL:
854		/*
855		 * Don't forward reply packets coming from the client
856		 * interface.
857		 *
858		 * RFC 6221 Section 6.1.1.
859		 */
860		if (clientdir == 0) {
861			log_debug("  dropped reply in opposite direction");
862			return;
863		}
864
865		if (relay6_poprelaymsg(pc, &dstif, (uint8_t *)p,
866		    &buflen) == -1) {
867			log_debug("  failed to pop relay-message");
868			return;
869		}
870
871		pc->pc_sd = clientsd;
872		break;
873
874	case DHCP6_MT_RELAYFORW:
875		/*
876		 * We can only have multiple hops when the destination
877		 * address is All_DHCP_Relay_Agents_and_Servers, otherwise
878		 * drop it.
879		 */
880		if (memcmp(&ss2sin6(&pc->pc_dst)->sin6_addr,
881		    &in6alldhcprelay, sizeof(in6alldhcprelay)) != 0) {
882			log_debug("  wrong destination");
883			return;
884		}
885
886		hopcount = dsr->dsr_hopcount + 1;
887		if (hopcount >= DHCP6_HOP_LIMIT) {
888			log_debug("  hop limit reached");
889			return;
890		}
891
892		/* Stack into another relay-message. */
893		if (relay6_pushrelaymsg(pc, intf, (uint8_t *)p,
894		    &buflen, DHCP_MTU_MAX) == -1) {
895			log_debug("  failed to push relay message");
896			return;
897		}
898
899		dsr = (struct dhcp6_relay_packet *)p;
900		dsr->dsr_msgtype = msgtype;
901		dsr->dsr_peer = ss2sin6(&pc->pc_src)->sin6_addr;
902		dsr->dsr_hopcount = hopcount;
903		break;
904
905	default:
906		log_debug("  unknown message type %d", ds->ds_msgtype);
907		return;
908	}
909
910	/* We received an packet with Interface-ID, use it. */
911	if (dstif != NULL) {
912		relay6_logsrcaddr(pc, dstif, msgtype);
913		send_packet(dstif, p, buflen, pc);
914		return;
915	}
916
917	/* Or send packet to the client. */
918	if (clientdir) {
919		relay6_logsrcaddr(pc, interfaces, msgtype);
920		send_packet(interfaces, p, buflen, pc);
921		return;
922	}
923
924	/* Otherwise broadcast it to other relays/servers. */
925	TAILQ_FOREACH(sp, &svlist, entry) {
926		/*
927		 * Don't send in the same interface it came in if we are
928		 * using multicast.
929		 */
930		if (sp->intf == intf &&
931		    sp->to.ss_family == 0)
932			continue;
933
934		/*
935		 * When forwarding a packet use the configured address
936		 * (if any) instead of multicasting.
937		 */
938		if (msgtype != DHCP6_MT_REPLY &&
939		    sp->to.ss_family == AF_INET6)
940			pc->pc_dst = sp->to;
941
942		relay6_logsrcaddr(pc, sp->intf, msgtype);
943		send_packet(sp->intf, p, buflen, pc);
944	}
945}
946
947void
948mcast6_recv(struct protocol *l)
949{
950	struct in6_pktinfo	*ipi6 = NULL;
951	struct cmsghdr		*cmsg;
952	struct interface_info	*intf;
953	int			 sd = *(int *)l->local;
954	ssize_t			 recvlen;
955	struct packet_ctx	 pc;
956	struct msghdr		 msg;
957	struct sockaddr_storage	 ss;
958	struct iovec		 iov[2];
959	uint8_t			 iovbuf[4096];
960	uint8_t			 cmsgbuf[
961	    CMSG_SPACE(sizeof(struct in6_pktinfo))
962	];
963
964	memset(&pc, 0, sizeof(pc));
965
966	iov[0].iov_base = iovbuf;
967	iov[0].iov_len = sizeof(iovbuf);
968
969	memset(&msg, 0, sizeof(msg));
970	msg.msg_iov = iov;
971	msg.msg_iovlen = 1;
972	msg.msg_control = cmsgbuf;
973	msg.msg_controllen = sizeof(cmsgbuf);
974	msg.msg_name = &ss;
975	msg.msg_namelen = sizeof(ss);
976	if ((recvlen = recvmsg(sd, &msg, 0)) == -1) {
977		log_warn("%s: recvmsg failed", __func__);
978		return;
979	}
980
981	/* Sanity check: this is an IPv6 packet. */
982	if (ss.ss_family != AF_INET6) {
983		log_debug("received non IPv6 packet");
984		return;
985	}
986
987	/* Drop packets that we sent. */
988	if (iflist_getbyaddr6(&ss2sin6(&ss)->sin6_addr) != NULL)
989		return;
990
991	/* Save the sender address. */
992	pc.pc_srcorig = pc.pc_src = ss;
993
994	/* Pre-configure destination to the default multicast address. */
995	ss2sin6(&pc.pc_dst)->sin6_family = AF_INET6;
996	ss2sin6(&pc.pc_dst)->sin6_len = sizeof(struct sockaddr_in6);
997	ss2sin6(&pc.pc_dst)->sin6_addr = in6alldhcprelay;
998	ss2sin6(&pc.pc_dst)->sin6_port = htons(DHCP6_SERVER_PORT);
999	pc.pc_sd = serversd;
1000
1001	/* Find out input interface. */
1002	for (cmsg = (struct cmsghdr *)CMSG_FIRSTHDR(&msg); cmsg;
1003	    cmsg = (struct cmsghdr *)CMSG_NXTHDR(&msg, cmsg)) {
1004		if (cmsg->cmsg_level != IPPROTO_IPV6)
1005			continue;
1006
1007		switch (cmsg->cmsg_type) {
1008		case IPV6_PKTINFO:
1009			ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
1010			break;
1011		}
1012	}
1013	if (ipi6 == NULL) {
1014		log_debug("failed to get packet interface");
1015		return;
1016	}
1017
1018	intf = iflist_getbyindex(ipi6->ipi6_ifindex);
1019	if (intf == NULL) {
1020		log_debug("failed to find packet interface: %u",
1021		    ipi6->ipi6_ifindex);
1022		return;
1023	}
1024
1025	/* Pass it to the relay routine. */
1026	if (bootp_packet_handler)
1027		(*bootp_packet_handler)(intf, iovbuf, recvlen, &pc);
1028}
1029