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 |
|
|
} |