Bug Summary

File:src/usr.sbin/dhcrelay6/dhcrelay6.c
Warning:line 378, column 8
Access to field 'ipv6' results in a dereference of a null pointer (loaded from field 'intf')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name dhcrelay6.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/dhcrelay6/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/dhcrelay6/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/dhcrelay6/dhcrelay6.c
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(*__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__attribute__((__noreturn__)) 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_ENTERPRISENO30155;
114char *remote_data;
115size_t remote_datalen;
116enum dhcp_relay_mode drm = DRM_LAYER3;
117
118__dead__attribute__((__noreturn__)) void
119usage(void)
120{
121 extern char *__progname;
122
123 fprintf(stderr(&__sF[2]), "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((void *)0))
138 fatal("calloc");
139 TAILQ_INSERT_HEAD(&svlist, sp, entry)do { if (((sp)->entry.tqe_next = (&svlist)->tqh_first
) != ((void *)0)) (&svlist)->tqh_first->entry.tqe_prev
= &(sp)->entry.tqe_next; else (&svlist)->tqh_last
= &(sp)->entry.tqe_next; (&svlist)->tqh_first =
(sp); (sp)->entry.tqe_prev = &(&svlist)->tqh_first
; } while (0)
;
140
141 /* Detect interface only destinations. */
142 if ((sp->intf = iflist_getbyname(dest)) != NULL((void *)0))
143 return sp;
144
145 /* Split address from interface and save it. */
146 ifname = strchr(dest, '%');
147 if (ifname == NULL((void *)0))
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((void *)0))
156 fatalx("interface '%s' not found", ifname);
157 if (s6fromaddr(ss2sin6(&sp->to), buf,
158 DHCP6_SERVER_PORT_STR"547", 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)(((&ss2sin6(&sp->to)->sin6_addr)->__u6_addr.
__u6_addr8[0] == 0xfe) && (((&ss2sin6(&sp->
to)->sin6_addr)->__u6_addr.__u6_addr8[1] & 0xc0) ==
0x80))
)
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(3<<3)); /* 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) {
1
Assuming the condition is false
2
Loop condition is false. Execution continues on line 230
185 switch (ch) {
186 case 'd':
187 daemonize = 0;
188 break;
189 case 'E':
190 enterpriseno = strtonum(optarg, 1, UINT32_MAX0xffffffffU, &errp);
191 if (errp != NULL((void *)0))
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((void *)0))
202 usage();
203
204 interfaces = iflist_getbyname(optarg);
205 if (interfaces == NULL((void *)0))
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) {
3
Assuming 'argc' is <= 0
4
Loop condition is false. Execution continues on line 238
233 parse_destination(argv[0]);
234 argc--;
235 argv++;
236 }
237
238 if (daemonize
4.1
'daemonize' is 1
) {
5
Taking true branch
239 devnull = open(_PATH_DEVNULL"/dev/null", O_RDWR0x0002);
240 if (devnull == -1)
6
Assuming the condition is false
7
Taking false branch
241 fatal("open(%s)", _PATH_DEVNULL"/dev/null");
242 }
243 if (interfaces == NULL((void *)0))
8
Assuming 'interfaces' is not equal to NULL
9
Taking false branch
244 fatalx("no interface given");
245 if (TAILQ_EMPTY(&svlist)(((&svlist)->tqh_first) == ((void *)0)))
10
Assuming field 'tqh_first' is not equal to null
11
Taking false branch
246 fatalx("no destination selected");
247
248 relay6_setup();
12
Calling 'relay6_setup'
249 bootp_packet_handler = relay6;
250
251 tzset();
252 time(&cur_time);
253
254 if ((pw = getpwnam(DHCRELAY6_USER"_dhcp")) == NULL((void *)0))
255 fatalx("user \"%s\" not found", DHCRELAY6_USER"_dhcp");
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(3<<3)); /* stop loggoing to stderr */
270 log_setverbose(debug);
271
272 if (pledge("inet stdio route", NULL((void *)0)) == -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(*__errno()) = EBADF9;
286 return (-1);
287 }
288 if (fcntl(devnull, F_GETFL3) == -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_FILENO0);
304 (void)dup2(devnull, STDOUT_FILENO1);
305 (void)dup2(devnull, STDERR_FILENO2);
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_INET624;
323 ai.ai_socktype = SOCK_DGRAM2;
324 ai.ai_protocol = IPPROTO_UDP17;
325 ai.ai_flags = (passive) ? AI_PASSIVE1 : 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)for((sp) = ((&svlist)->tqh_first); (sp) != ((void *)0)
; (sp) = ((sp)->entry.tqe_next))
{
13
Loop condition is true. Entering loop body
17
Assuming 'sp' is equal to null
18
Loop condition is false. Execution continues on line 358
350 if (sp->intf == NULL((void *)0))
14
Assuming field 'intf' is equal to NULL
15
Taking true branch
351 continue;
16
Execution continues on line 349
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
18.1
'drm' is not equal to DRM_LAYER2
== DRM_LAYER2) {
19
Taking false branch
359 TAILQ_FOREACH(sp, &svlist, entry)for((sp) = ((&svlist)->tqh_first); (sp) != ((void *)0)
; (sp) = ((sp)->entry.tqe_next))
{
360 sp->intf = register_interface(sp->intf->name,
361 got_one);
362 if (sp->intf == NULL((void *)0))
363 fatalx("destination interface "
364 "registration failed");
365 }
366 interfaces = register_interface(interfaces->name, got_one);
367 if (interfaces == NULL((void *)0))
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)for((sp) = ((&svlist)->tqh_first); (sp) != ((void *)0)
; (sp) = ((sp)->entry.tqe_next))
{
20
Loop condition is true. Entering loop body
378 if (!sp->intf->ipv6)
21
Access to field 'ipv6' results in a dereference of a null pointer (loaded from field 'intf')
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_INET624, SOCK_DGRAM2, IPPROTO_UDP17);
391 if (clientsd == -1)
392 fatal("socket");
393
394 if (setsockopt(clientsd, SOL_SOCKET0xffff, SO_REUSEPORT0x0200, &flag,
395 sizeof(flag)) == -1)
396 fatal("setsockopt(SO_REUSEPORT)");
397
398 if (s6fromaddr(&sin6, NULL((void *)0), DHCP6_SERVER_PORT_STR"547", 1) == -1)
399 fatalx("s6fromaddr");
400 if (bind(clientsd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
401 fatal("bind");
402
403 if (setsockopt(clientsd, IPPROTO_IPV641, IPV6_RECVPKTINFO36, &flag,
404 sizeof(flag)) == -1)
405 fatal("setsockopt(IPV6_RECVPKTINFO)");
406
407 memset(&mreq6, 0, sizeof(mreq6));
408 if (s6fromaddr(&sin6, DHCP6_ADDR_RELAYSERVER"FF02::1:2", NULL((void *)0), 0) == -1)
409 fatalx("s6fromaddr");
410 memcpy(&mreq6.ipv6mr_multiaddr, &sin6.sin6_addr,
411 sizeof(mreq6.ipv6mr_multiaddr));
412 TAILQ_FOREACH(intf, &intflist, entry)for((intf) = ((&intflist)->tqh_first); (intf) != ((void
*)0); (intf) = ((intf)->entry.tqe_next))
{
413 /* Skip interfaces without IPv6. */
414 if (!intf->ipv6)
415 continue;
416
417 mreq6.ipv6mr_interface = intf->index;
418 if (setsockopt(clientsd, IPPROTO_IPV641, IPV6_JOIN_GROUP12,
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_INET624, SOCK_DGRAM2, IPPROTO_UDP17);
427 if (serversd == -1)
428 fatal("socket");
429
430 if (setsockopt(serversd, SOL_SOCKET0xffff, SO_REUSEPORT0x0200, &flag,
431 sizeof(flag)) == -1)
432 fatal("setsockopt(SO_REUSEPORT)");
433
434 if (s6fromaddr(&sin6, NULL((void *)0), DHCP6_SERVER_PORT_STR"547", 1) == -1)
435 fatalx("s6fromaddr");
436 if (bind(serversd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
437 fatal("bind");
438
439 if (setsockopt(serversd, IPPROTO_IPV641, IPV6_RECVPKTINFO36, &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_INET624, addr, &buf[bufpos][1],
481 sizeof(buf[bufpos])) == NULL((void *)0))
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_REQUEST3:
494 return "REQUEST";
495 case DHCP6_MT_RENEW5:
496 return "RENEW";
497 case DHCP6_MT_REBIND6:
498 return "REBIND";
499 case DHCP6_MT_RELEASE8:
500 return "RELEASE";
501 case DHCP6_MT_DECLINE9:
502 return "DECLINE";
503 case DHCP6_MT_INFORMATIONREQUEST11:
504 return "INFORMATION-REQUEST";
505 case DHCP6_MT_SOLICIT1:
506 return "SOLICIT";
507 case DHCP6_MT_ADVERTISE2:
508 return "ADVERTISE";
509 case DHCP6_MT_CONFIRM4:
510 return "CONFIRM";
511 case DHCP6_MT_REPLY7:
512 return "REPLY";
513 case DHCP6_MT_RECONFIGURE10:
514 return "RECONFIGURE";
515 case DHCP6_MT_RELAYREPL13:
516 return "RELAY-REPLY";
517 case DHCP6_MT_RELAYFORW12:
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((void *)0))
534 railen = sizeof(*dso) + pc->pc_raidatalen;
535 else
536 railen = 0;
537
538 if (pc->pc_remote)
539 remotelen = sizeof(*dso) + ENTERPRISENO_LENsizeof(uint32_t) +
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_RELAYFORW12;
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_LIMIT32;
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)(__uint16_t)(__builtin_constant_p(18) ? (__uint16_t)(((__uint16_t
)(18) & 0xffU) << 8 | ((__uint16_t)(18) & 0xff00U
) >> 8) : __swap16md(18))
;
590 dso->dso_length = htons(pc->pc_raidatalen)(__uint16_t)(__builtin_constant_p(pc->pc_raidatalen) ? (__uint16_t
)(((__uint16_t)(pc->pc_raidatalen) & 0xffU) << 8
| ((__uint16_t)(pc->pc_raidatalen) & 0xff00U) >>
8) : __swap16md(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)(__uint16_t)(__builtin_constant_p(37) ? (__uint16_t)(((__uint16_t
)(37) & 0xffU) << 8 | ((__uint16_t)(37) & 0xff00U
) >> 8) : __swap16md(37))
;
599 dso->dso_length =
600 htons(sizeof(pc->pc_enterpriseno) + pc->pc_remotelen)(__uint16_t)(__builtin_constant_p(sizeof(pc->pc_enterpriseno
) + pc->pc_remotelen) ? (__uint16_t)(((__uint16_t)(sizeof(
pc->pc_enterpriseno) + pc->pc_remotelen) & 0xffU) <<
8 | ((__uint16_t)(sizeof(pc->pc_enterpriseno) + pc->pc_remotelen
) & 0xff00U) >> 8) : __swap16md(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)(__uint16_t)(__builtin_constant_p(9) ? (__uint16_t)(((__uint16_t
)(9) & 0xffU) << 8 | ((__uint16_t)(9) & 0xff00U
) >> 8) : __swap16md(9))
;
611 dso->dso_length = htons(*plen)(__uint16_t)(__builtin_constant_p(*plen) ? (__uint16_t)(((__uint16_t
)(*plen) & 0xffU) << 8 | ((__uint16_t)(*plen) &
0xff00U) >> 8) : __swap16md(*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((void *)0);
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((void *)0);
633
634 /* Sanity check: this is a relay message of the right type. */
635 if (dsr->dsr_msgtype != DHCP6_MT_RELAYREPL13) {
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)(__uint16_t)(__builtin_constant_p(dso->dso_code) ? (__uint16_t
)(((__uint16_t)(dso->dso_code) & 0xffU) << 8 | (
(__uint16_t)(dso->dso_code) & 0xff00U) >> 8) : __swap16md
(dso->dso_code))
;
649 dsolen = sizeof(*dso) + ntohs(dso->dso_length)(__uint16_t)(__builtin_constant_p(dso->dso_length) ? (__uint16_t
)(((__uint16_t)(dso->dso_length) & 0xffU) << 8 |
((__uint16_t)(dso->dso_length) & 0xff00U) >> 8)
: __swap16md(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_INTERFACEID18) {
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_MSG9) {
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)(__uint16_t)(__builtin_constant_p(dso->dso_length) ? (__uint16_t
)(((__uint16_t)(dso->dso_length) & 0xffU) << 8 |
((__uint16_t)(dso->dso_length) & 0xff00U) >> 8)
: __swap16md(dso->dso_length))
;
689
690 dso = (struct dhcp6_option *)((uint8_t *)dso + dsolen);
691 pleft -= dsolen;
692 }
693 if (ds == NULL((void *)0) || 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_RELAYREPL13)
709 ss2sin6(&pc->pc_dst)->sin6_port =
710 htons(DHCP6_CLIENT_PORT)(__uint16_t)(__builtin_constant_p(546) ? (__uint16_t)(((__uint16_t
)(546) & 0xffU) << 8 | ((__uint16_t)(546) & 0xff00U
) >> 8) : __swap16md(546))
;
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((void *)0)) {
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((void *)0))
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((void *)0)) {
744 pc->pc_remote = remote_data;
745 pc->pc_remotelen = remote_datalen;
746 pc->pc_enterpriseno = htonl(enterpriseno)(__uint32_t)(__builtin_constant_p(enterpriseno) ? (__uint32_t
)(((__uint32_t)(enterpriseno) & 0xff) << 24 | ((__uint32_t
)(enterpriseno) & 0xff00) << 8 | ((__uint32_t)(enterpriseno
) & 0xff0000) >> 8 | ((__uint32_t)(enterpriseno) &
0xff000000) >> 24) : __swap32md(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((void *)0)) {
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_RELAYREPL13) ? "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((void *)0);
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_ADVERTISE2:
820 case DHCP6_MT_REPLY7:
821 case DHCP6_MT_RECONFIGURE10:
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_REQUEST3:
835 case DHCP6_MT_RENEW5:
836 case DHCP6_MT_REBIND6:
837 case DHCP6_MT_RELEASE8:
838 case DHCP6_MT_DECLINE9:
839 case DHCP6_MT_INFORMATIONREQUEST11:
840 case DHCP6_MT_SOLICIT1:
841 case DHCP6_MT_CONFIRM4:
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_MAX1500) == -1) {
848 log_debug(" message encapsulation failed");
849 return;
850 }
851 break;
852
853 case DHCP6_MT_RELAYREPL13:
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_RELAYFORW12:
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_LIMIT32) {
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_MAX1500) == -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((void *)0)) {
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)for((sp) = ((&svlist)->tqh_first); (sp) != ((void *)0)
; (sp) = ((sp)->entry.tqe_next))
{
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_REPLY7 &&
939 sp->to.ss_family == AF_INET624)
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((void *)0);
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))((((unsigned long)(sizeof(struct cmsghdr)) + (sizeof(long) - 1
)) &~(sizeof(long) - 1)) + (((unsigned long)(sizeof(struct
in6_pktinfo)) + (sizeof(long) - 1)) &~(sizeof(long) - 1)
))
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_INET624) {
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((void *)0))
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_INET624;
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)(__uint16_t)(__builtin_constant_p(547) ? (__uint16_t)(((__uint16_t
)(547) & 0xffU) << 8 | ((__uint16_t)(547) & 0xff00U
) >> 8) : __swap16md(547))
;
999 pc.pc_sd = serversd;
1000
1001 /* Find out input interface. */
1002 for (cmsg = (struct cmsghdr *)CMSG_FIRSTHDR(&msg)((&msg)->msg_controllen >= sizeof(struct cmsghdr) ?
(struct cmsghdr *)(&msg)->msg_control : (struct cmsghdr
*)((void *)0))
; cmsg;
1003 cmsg = (struct cmsghdr *)CMSG_NXTHDR(&msg, cmsg)(((char *)(cmsg) + (((unsigned long)((cmsg)->cmsg_len) + (
sizeof(long) - 1)) &~(sizeof(long) - 1)) + (((unsigned long
)(sizeof(struct cmsghdr)) + (sizeof(long) - 1)) &~(sizeof
(long) - 1)) > ((char *)(&msg)->msg_control) + (&
msg)->msg_controllen) ? (struct cmsghdr *)((void *)0) : (struct
cmsghdr *)((char *)(cmsg) + (((unsigned long)((cmsg)->cmsg_len
) + (sizeof(long) - 1)) &~(sizeof(long) - 1))))
) {
1004 if (cmsg->cmsg_level != IPPROTO_IPV641)
1005 continue;
1006
1007 switch (cmsg->cmsg_type) {
1008 case IPV6_PKTINFO46:
1009 ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg)((unsigned char *)(cmsg) + (((unsigned long)(sizeof(struct cmsghdr
)) + (sizeof(long) - 1)) &~(sizeof(long) - 1)))
;
1010 break;
1011 }
1012 }
1013 if (ipi6 == NULL((void *)0)) {
1014 log_debug("failed to get packet interface");
1015 return;
1016 }
1017
1018 intf = iflist_getbyindex(ipi6->ipi6_ifindex);
1019 if (intf == NULL((void *)0)) {
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}