xref: /openbsd/usr.sbin/dhcrelay6/dhcrelay6.c (revision b7041c07)
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  */
77 struct in6_addr		 in6alldhcprelay = {
78 	{{ 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0, 0x02 }}
79 };
80 struct 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);
85 struct server_list *parse_destination(const char *);
86 int	 rdaemon(int);
87 void	 relay6_setup(void);
88 int	 s6fromaddr(struct sockaddr_in6 *, const char *, const char *, int);
89 char	*print_hw_addr(int, int, unsigned char *);
90 const char *dhcp6type2str(uint8_t);
91 int	 relay6_pushrelaymsg(struct packet_ctx *, struct interface_info *,
92 	    uint8_t *, size_t *, size_t);
93 int	 relay6_poprelaymsg(struct packet_ctx *, struct interface_info **,
94 	    uint8_t *, size_t *);
95 void	 rai_configure(struct packet_ctx *, struct interface_info *);
96 void	 relay6_logsrcaddr(struct packet_ctx *, struct interface_info *,
97 	    uint8_t);
98 void	 relay6(struct interface_info *, void *, size_t,
99 	    struct packet_ctx *);
100 void	 mcast6_recv(struct protocol *);
101 
102 /* Shared variables */
103 int			 clientsd;
104 int			 serversd;
105 int			 oflag;
106 time_t			 cur_time;
107 
108 struct intfq		 intflist;
109 struct serverq		 svlist;
110 struct interface_info	*interfaces;
111 char			*rai_data;
112 size_t			 rai_datalen;
113 uint32_t		 enterpriseno = OPENBSD_ENTERPRISENO;
114 char			*remote_data;
115 size_t			 remote_datalen;
116 enum dhcp_relay_mode	 drm = DRM_LAYER3;
117 
118 __dead void
usage(void)119 usage(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 
130 struct server_list *
parse_destination(const char * dest)131 parse_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 
171 int
main(int argc,char * argv[])172 main(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 
281 int
rdaemon(int devnull)282 rdaemon(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 
312 int
s6fromaddr(struct sockaddr_in6 * sin6,const char * addr,const char * serv,int passive)313 s6fromaddr(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 
339 void
relay6_setup(void)340 relay6_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 
446 char *
print_hw_addr(int htype,int hlen,unsigned char * data)447 print_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) {
454 bad:
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 
472 const char *
v6addr2str(struct in6_addr * addr)473 v6addr2str(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 
489 const char *
dhcp6type2str(uint8_t msgtype)490 dhcp6type2str(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 
524 int
relay6_pushrelaymsg(struct packet_ctx * pc,struct interface_info * intf,uint8_t * p,size_t * plen,size_t ptotal)525 relay6_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 
619 int
relay6_poprelaymsg(struct packet_ctx * pc,struct interface_info ** intf,uint8_t * p,size_t * plen)620 relay6_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 
740 void
rai_configure(struct packet_ctx * pc,struct interface_info * intf)741 rai_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 
767 void
relay6_logsrcaddr(struct packet_ctx * pc,struct interface_info * intf,uint8_t msgtype)768 relay6_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 
786 void
relay6(struct interface_info * intf,void * p,size_t plen,struct packet_ctx * pc)787 relay6(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 
947 void
mcast6_recv(struct protocol * l)948 mcast6_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