GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcrelay6/dhcrelay6.c Lines: 0 440 0.0 %
Date: 2017-11-07 Branches: 0 260 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: dhcrelay6.c,v 1.2 2017/03/17 16:45:27 jmc 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
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 *
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
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, 0);
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(_PATH_VAREMPTY) == -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 flock rpath cpath wpath", NULL) == -1)
273
		fatalx("pledge");
274
275
	dispatch();
276
	/* not reached */
277
278
	exit(0);
279
}
280
281
int
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
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
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 *
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 *
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 *
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
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
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
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
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
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
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
}