1 |
|
|
/* $OpenBSD: frontend.c,v 1.8 2017/08/23 10:48:01 florian Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2017 Florian Obser <florian@openbsd.org> |
5 |
|
|
* Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> |
6 |
|
|
* Copyright (c) 2004 Esben Norby <norby@openbsd.org> |
7 |
|
|
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> |
8 |
|
|
* |
9 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
10 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
11 |
|
|
* copyright notice and this permission notice appear in all copies. |
12 |
|
|
* |
13 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
14 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
15 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
16 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
17 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
18 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
19 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
20 |
|
|
*/ |
21 |
|
|
#include <sys/types.h> |
22 |
|
|
#include <sys/ioctl.h> |
23 |
|
|
#include <sys/queue.h> |
24 |
|
|
#include <sys/socket.h> |
25 |
|
|
#include <sys/syslog.h> |
26 |
|
|
#include <sys/uio.h> |
27 |
|
|
|
28 |
|
|
#include <net/if.h> |
29 |
|
|
#include <net/if_dl.h> |
30 |
|
|
#include <net/if_types.h> |
31 |
|
|
#include <net/route.h> |
32 |
|
|
|
33 |
|
|
#include <arpa/inet.h> |
34 |
|
|
|
35 |
|
|
#include <netinet/in.h> |
36 |
|
|
#include <netinet/if_ether.h> |
37 |
|
|
#include <netinet6/nd6.h> |
38 |
|
|
#include <netinet6/in6_var.h> |
39 |
|
|
#include <netinet/ip6.h> |
40 |
|
|
#include <netinet6/ip6_var.h> |
41 |
|
|
#include <netinet/icmp6.h> |
42 |
|
|
|
43 |
|
|
#include <errno.h> |
44 |
|
|
#include <event.h> |
45 |
|
|
#include <ifaddrs.h> |
46 |
|
|
#include <imsg.h> |
47 |
|
|
#include <pwd.h> |
48 |
|
|
#include <signal.h> |
49 |
|
|
#include <stdio.h> |
50 |
|
|
#include <stdlib.h> |
51 |
|
|
#include <string.h> |
52 |
|
|
#include <unistd.h> |
53 |
|
|
|
54 |
|
|
#include "log.h" |
55 |
|
|
#include "slaacd.h" |
56 |
|
|
#include "frontend.h" |
57 |
|
|
#include "control.h" |
58 |
|
|
|
59 |
|
|
#define ROUTE_SOCKET_BUF_SIZE 16384 |
60 |
|
|
#define ALLROUTER "ff02::2" |
61 |
|
|
|
62 |
|
|
__dead void frontend_shutdown(void); |
63 |
|
|
void frontend_sig_handler(int, short, void *); |
64 |
|
|
void update_iface(uint32_t, char*); |
65 |
|
|
void frontend_startup(void); |
66 |
|
|
void route_receive(int, short, void *); |
67 |
|
|
void handle_route_message(struct rt_msghdr *, struct sockaddr **); |
68 |
|
|
void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); |
69 |
|
|
void icmp6_receive(int, short, void *); |
70 |
|
|
int get_flags(char *); |
71 |
|
|
int get_xflags(char *); |
72 |
|
|
void get_lladdr(char *, struct ether_addr *, struct sockaddr_in6 *); |
73 |
|
|
void send_solicitation(uint32_t); |
74 |
|
|
#ifndef SMALL |
75 |
|
|
void update_autoconf_addresses(uint32_t, char*); |
76 |
|
|
#endif /* SMALL */ |
77 |
|
|
|
78 |
|
|
struct imsgev *iev_main; |
79 |
|
|
struct imsgev *iev_engine; |
80 |
|
|
struct event ev_route; |
81 |
|
|
struct msghdr sndmhdr; |
82 |
|
|
struct iovec sndiov[4]; |
83 |
|
|
struct nd_router_solicit rs; |
84 |
|
|
struct nd_opt_hdr nd_opt_hdr; |
85 |
|
|
struct ether_addr nd_opt_source_link_addr; |
86 |
|
|
struct sockaddr_in6 dst; |
87 |
|
|
int icmp6sock, routesock, ioctlsock; |
88 |
|
|
|
89 |
|
|
struct icmp6_ev { |
90 |
|
|
struct event ev; |
91 |
|
|
uint8_t answer[1500]; |
92 |
|
|
struct msghdr rcvmhdr; |
93 |
|
|
struct iovec rcviov[1]; |
94 |
|
|
struct sockaddr_in6 from; |
95 |
|
|
} icmp6ev; |
96 |
|
|
|
97 |
|
|
void |
98 |
|
|
frontend_sig_handler(int sig, short event, void *bula) |
99 |
|
|
{ |
100 |
|
|
/* |
101 |
|
|
* Normal signal handler rules don't apply because libevent |
102 |
|
|
* decouples for us. |
103 |
|
|
*/ |
104 |
|
|
|
105 |
|
|
switch (sig) { |
106 |
|
|
case SIGINT: |
107 |
|
|
case SIGTERM: |
108 |
|
|
frontend_shutdown(); |
109 |
|
|
default: |
110 |
|
|
fatalx("unexpected signal"); |
111 |
|
|
} |
112 |
|
|
} |
113 |
|
|
|
114 |
|
|
void |
115 |
|
|
frontend(int debug, int verbose, char *sockname) |
116 |
|
|
{ |
117 |
|
|
struct event ev_sigint, ev_sigterm; |
118 |
|
|
struct passwd *pw; |
119 |
|
|
struct icmp6_filter filt; |
120 |
|
|
struct in6_pktinfo *pi; |
121 |
|
|
struct cmsghdr *cm; |
122 |
|
|
size_t rcvcmsglen, sndcmsglen; |
123 |
|
|
int hoplimit = 255, on = 1, rtfilter; |
124 |
|
|
uint8_t *rcvcmsgbuf, *sndcmsgbuf; |
125 |
|
|
|
126 |
|
|
log_init(debug, LOG_DAEMON); |
127 |
|
|
log_setverbose(verbose); |
128 |
|
|
|
129 |
|
|
#ifndef SMALL |
130 |
|
|
/* Create slaacd control socket outside chroot. */ |
131 |
|
|
if (control_init(sockname) == -1) |
132 |
|
|
fatalx("control socket setup failed"); |
133 |
|
|
#endif /* SMALL */ |
134 |
|
|
|
135 |
|
|
if ((pw = getpwnam(SLAACD_USER)) == NULL) |
136 |
|
|
fatal("getpwnam"); |
137 |
|
|
|
138 |
|
|
if (chroot(pw->pw_dir) == -1) |
139 |
|
|
fatal("chroot"); |
140 |
|
|
if (chdir("/") == -1) |
141 |
|
|
fatal("chdir(\"/\")"); |
142 |
|
|
|
143 |
|
|
slaacd_process = PROC_FRONTEND; |
144 |
|
|
setproctitle("%s", log_procnames[slaacd_process]); |
145 |
|
|
log_procinit(log_procnames[slaacd_process]); |
146 |
|
|
|
147 |
|
|
if ((icmp6sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) |
148 |
|
|
fatal("ICMPv6 socket"); |
149 |
|
|
|
150 |
|
|
if ((routesock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) |
151 |
|
|
fatal("route socket"); |
152 |
|
|
|
153 |
|
|
if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) |
154 |
|
|
fatal("socket"); |
155 |
|
|
|
156 |
|
|
if (setgroups(1, &pw->pw_gid) || |
157 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || |
158 |
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) |
159 |
|
|
fatal("can't drop privileges"); |
160 |
|
|
|
161 |
|
|
if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, |
162 |
|
|
sizeof(on)) < 0) |
163 |
|
|
fatal("IPV6_RECVPKTINFO"); |
164 |
|
|
|
165 |
|
|
if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, |
166 |
|
|
sizeof(on)) < 0) |
167 |
|
|
fatal("IPV6_RECVHOPLIMIT"); |
168 |
|
|
|
169 |
|
|
/* only router advertisements */ |
170 |
|
|
ICMP6_FILTER_SETBLOCKALL(&filt); |
171 |
|
|
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); |
172 |
|
|
if (setsockopt(icmp6sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, |
173 |
|
|
sizeof(filt)) == -1) |
174 |
|
|
fatal("ICMP6_FILTER"); |
175 |
|
|
|
176 |
|
|
rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_NEWADDR) | |
177 |
|
|
ROUTE_FILTER(RTM_DELADDR) | ROUTE_FILTER(RTM_PROPOSAL); |
178 |
|
|
if (setsockopt(routesock, PF_ROUTE, ROUTE_MSGFILTER, |
179 |
|
|
&rtfilter, sizeof(rtfilter)) < 0) |
180 |
|
|
fatal("setsockopt(ROUTE_MSGFILTER)"); |
181 |
|
|
|
182 |
|
|
if (pledge("stdio inet recvfd route flock rpath cpath wpath", NULL) == -1) |
183 |
|
|
fatal("pledge"); |
184 |
|
|
|
185 |
|
|
event_init(); |
186 |
|
|
|
187 |
|
|
/* Setup signal handler. */ |
188 |
|
|
signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL); |
189 |
|
|
signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL); |
190 |
|
|
signal_add(&ev_sigint, NULL); |
191 |
|
|
signal_add(&ev_sigterm, NULL); |
192 |
|
|
signal(SIGPIPE, SIG_IGN); |
193 |
|
|
signal(SIGHUP, SIG_IGN); |
194 |
|
|
|
195 |
|
|
/* Setup pipe and event handler to the parent process. */ |
196 |
|
|
if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) |
197 |
|
|
fatal(NULL); |
198 |
|
|
imsg_init(&iev_main->ibuf, 3); |
199 |
|
|
iev_main->handler = frontend_dispatch_main; |
200 |
|
|
iev_main->events = EV_READ; |
201 |
|
|
event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, |
202 |
|
|
iev_main->handler, iev_main); |
203 |
|
|
event_add(&iev_main->ev, NULL); |
204 |
|
|
|
205 |
|
|
#ifndef SMALL |
206 |
|
|
/* Listen on control socket. */ |
207 |
|
|
TAILQ_INIT(&ctl_conns); |
208 |
|
|
control_listen(); |
209 |
|
|
#endif /* SMALL */ |
210 |
|
|
|
211 |
|
|
event_set(&ev_route, routesock, EV_READ | EV_PERSIST, route_receive, |
212 |
|
|
NULL); |
213 |
|
|
|
214 |
|
|
event_set(&icmp6ev.ev, icmp6sock, EV_READ | EV_PERSIST, icmp6_receive, |
215 |
|
|
NULL); |
216 |
|
|
|
217 |
|
|
rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + |
218 |
|
|
CMSG_SPACE(sizeof(int)); |
219 |
|
|
if((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) |
220 |
|
|
fatal("malloc"); |
221 |
|
|
|
222 |
|
|
icmp6ev.rcviov[0].iov_base = (caddr_t)icmp6ev.answer; |
223 |
|
|
icmp6ev.rcviov[0].iov_len = sizeof(icmp6ev.answer); |
224 |
|
|
icmp6ev.rcvmhdr.msg_name = (caddr_t)&icmp6ev.from; |
225 |
|
|
icmp6ev.rcvmhdr.msg_namelen = sizeof(icmp6ev.from); |
226 |
|
|
icmp6ev.rcvmhdr.msg_iov = icmp6ev.rcviov; |
227 |
|
|
icmp6ev.rcvmhdr.msg_iovlen = 1; |
228 |
|
|
icmp6ev.rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; |
229 |
|
|
icmp6ev.rcvmhdr.msg_controllen = rcvcmsglen; |
230 |
|
|
|
231 |
|
|
sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + |
232 |
|
|
CMSG_SPACE(sizeof(int)); |
233 |
|
|
|
234 |
|
|
if ((sndcmsgbuf = malloc(sndcmsglen)) == NULL) |
235 |
|
|
fatal("malloc"); |
236 |
|
|
|
237 |
|
|
rs.nd_rs_type = ND_ROUTER_SOLICIT; |
238 |
|
|
rs.nd_rs_code = 0; |
239 |
|
|
rs.nd_rs_cksum = 0; |
240 |
|
|
rs.nd_rs_reserved = 0; |
241 |
|
|
|
242 |
|
|
nd_opt_hdr.nd_opt_type = ND_OPT_SOURCE_LINKADDR; |
243 |
|
|
nd_opt_hdr.nd_opt_len = 1; |
244 |
|
|
|
245 |
|
|
memset(&dst, 0, sizeof(dst)); |
246 |
|
|
dst.sin6_family = AF_INET6; |
247 |
|
|
if (inet_pton(AF_INET6, ALLROUTER, &dst.sin6_addr.s6_addr) != 1) |
248 |
|
|
fatal("inet_pton"); |
249 |
|
|
|
250 |
|
|
sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); |
251 |
|
|
sndmhdr.msg_iov = sndiov; |
252 |
|
|
sndmhdr.msg_iovlen = 3; |
253 |
|
|
sndmhdr.msg_control = (caddr_t)sndcmsgbuf; |
254 |
|
|
sndmhdr.msg_controllen = sndcmsglen; |
255 |
|
|
|
256 |
|
|
sndmhdr.msg_name = (caddr_t)&dst; |
257 |
|
|
sndmhdr.msg_iov[0].iov_base = (caddr_t)&rs; |
258 |
|
|
sndmhdr.msg_iov[0].iov_len = sizeof(rs); |
259 |
|
|
sndmhdr.msg_iov[1].iov_base = (caddr_t)&nd_opt_hdr; |
260 |
|
|
sndmhdr.msg_iov[1].iov_len = sizeof(nd_opt_hdr); |
261 |
|
|
sndmhdr.msg_iov[2].iov_base = (caddr_t)&nd_opt_source_link_addr; |
262 |
|
|
sndmhdr.msg_iov[2].iov_len = sizeof(nd_opt_source_link_addr); |
263 |
|
|
|
264 |
|
|
cm = CMSG_FIRSTHDR(&sndmhdr); |
265 |
|
|
|
266 |
|
|
cm->cmsg_level = IPPROTO_IPV6; |
267 |
|
|
cm->cmsg_type = IPV6_PKTINFO; |
268 |
|
|
cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
269 |
|
|
pi = (struct in6_pktinfo *)CMSG_DATA(cm); |
270 |
|
|
memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); |
271 |
|
|
pi->ipi6_ifindex = 0; |
272 |
|
|
|
273 |
|
|
cm = CMSG_NXTHDR(&sndmhdr, cm); |
274 |
|
|
cm->cmsg_level = IPPROTO_IPV6; |
275 |
|
|
cm->cmsg_type = IPV6_HOPLIMIT; |
276 |
|
|
cm->cmsg_len = CMSG_LEN(sizeof(int)); |
277 |
|
|
memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); |
278 |
|
|
|
279 |
|
|
event_dispatch(); |
280 |
|
|
|
281 |
|
|
frontend_shutdown(); |
282 |
|
|
} |
283 |
|
|
|
284 |
|
|
__dead void |
285 |
|
|
frontend_shutdown(void) |
286 |
|
|
{ |
287 |
|
|
/* Close pipes. */ |
288 |
|
|
msgbuf_write(&iev_engine->ibuf.w); |
289 |
|
|
msgbuf_clear(&iev_engine->ibuf.w); |
290 |
|
|
close(iev_engine->ibuf.fd); |
291 |
|
|
msgbuf_write(&iev_main->ibuf.w); |
292 |
|
|
msgbuf_clear(&iev_main->ibuf.w); |
293 |
|
|
close(iev_main->ibuf.fd); |
294 |
|
|
|
295 |
|
|
free(iev_engine); |
296 |
|
|
free(iev_main); |
297 |
|
|
|
298 |
|
|
log_info("frontend exiting"); |
299 |
|
|
exit(0); |
300 |
|
|
} |
301 |
|
|
|
302 |
|
|
int |
303 |
|
|
frontend_imsg_compose_main(int type, pid_t pid, void *data, |
304 |
|
|
uint16_t datalen) |
305 |
|
|
{ |
306 |
|
|
return (imsg_compose_event(iev_main, type, 0, pid, -1, data, |
307 |
|
|
datalen)); |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
int |
311 |
|
|
frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid, |
312 |
|
|
void *data, uint16_t datalen) |
313 |
|
|
{ |
314 |
|
|
return (imsg_compose_event(iev_engine, type, peerid, pid, -1, |
315 |
|
|
data, datalen)); |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
void |
319 |
|
|
frontend_dispatch_main(int fd, short event, void *bula) |
320 |
|
|
{ |
321 |
|
|
struct imsg imsg; |
322 |
|
|
struct imsgev *iev = bula; |
323 |
|
|
struct imsgbuf *ibuf = &iev->ibuf; |
324 |
|
|
ssize_t n; |
325 |
|
|
int shut = 0; |
326 |
|
|
|
327 |
|
|
if (event & EV_READ) { |
328 |
|
|
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) |
329 |
|
|
fatal("imsg_read error"); |
330 |
|
|
if (n == 0) /* Connection closed. */ |
331 |
|
|
shut = 1; |
332 |
|
|
} |
333 |
|
|
if (event & EV_WRITE) { |
334 |
|
|
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) |
335 |
|
|
fatal("msgbuf_write"); |
336 |
|
|
if (n == 0) /* Connection closed. */ |
337 |
|
|
shut = 1; |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
for (;;) { |
341 |
|
|
if ((n = imsg_get(ibuf, &imsg)) == -1) |
342 |
|
|
fatal("%s: imsg_get error", __func__); |
343 |
|
|
if (n == 0) /* No more messages. */ |
344 |
|
|
break; |
345 |
|
|
|
346 |
|
|
switch (imsg.hdr.type) { |
347 |
|
|
case IMSG_SOCKET_IPC: |
348 |
|
|
/* |
349 |
|
|
* Setup pipe and event handler to the engine |
350 |
|
|
* process. |
351 |
|
|
*/ |
352 |
|
|
if (iev_engine) { |
353 |
|
|
log_warnx("%s: received unexpected imsg fd " |
354 |
|
|
"to frontend", __func__); |
355 |
|
|
break; |
356 |
|
|
} |
357 |
|
|
if ((fd = imsg.fd) == -1) { |
358 |
|
|
log_warnx("%s: expected to receive imsg fd to " |
359 |
|
|
"frontend but didn't receive any", |
360 |
|
|
__func__); |
361 |
|
|
break; |
362 |
|
|
} |
363 |
|
|
|
364 |
|
|
iev_engine = malloc(sizeof(struct imsgev)); |
365 |
|
|
if (iev_engine == NULL) |
366 |
|
|
fatal(NULL); |
367 |
|
|
|
368 |
|
|
imsg_init(&iev_engine->ibuf, fd); |
369 |
|
|
iev_engine->handler = frontend_dispatch_engine; |
370 |
|
|
iev_engine->events = EV_READ; |
371 |
|
|
|
372 |
|
|
event_set(&iev_engine->ev, iev_engine->ibuf.fd, |
373 |
|
|
iev_engine->events, iev_engine->handler, iev_engine); |
374 |
|
|
event_add(&iev_engine->ev, NULL); |
375 |
|
|
|
376 |
|
|
if (pledge("stdio inet route flock rpath cpath wpath", NULL) == -1) |
377 |
|
|
fatal("pledge"); |
378 |
|
|
|
379 |
|
|
break; |
380 |
|
|
case IMSG_STARTUP: |
381 |
|
|
frontend_startup(); |
382 |
|
|
break; |
383 |
|
|
#ifndef SMALL |
384 |
|
|
case IMSG_CTL_END: |
385 |
|
|
control_imsg_relay(&imsg); |
386 |
|
|
break; |
387 |
|
|
#endif /* SMALL */ |
388 |
|
|
default: |
389 |
|
|
log_debug("%s: error handling imsg %d", __func__, |
390 |
|
|
imsg.hdr.type); |
391 |
|
|
break; |
392 |
|
|
} |
393 |
|
|
imsg_free(&imsg); |
394 |
|
|
} |
395 |
|
|
if (!shut) |
396 |
|
|
imsg_event_add(iev); |
397 |
|
|
else { |
398 |
|
|
/* This pipe is dead. Remove its event handler. */ |
399 |
|
|
event_del(&iev->ev); |
400 |
|
|
event_loopexit(NULL); |
401 |
|
|
} |
402 |
|
|
} |
403 |
|
|
|
404 |
|
|
void |
405 |
|
|
frontend_dispatch_engine(int fd, short event, void *bula) |
406 |
|
|
{ |
407 |
|
|
struct imsgev *iev = bula; |
408 |
|
|
struct imsgbuf *ibuf = &iev->ibuf; |
409 |
|
|
struct imsg imsg; |
410 |
|
|
ssize_t n; |
411 |
|
|
int shut = 0; |
412 |
|
|
uint32_t if_index; |
413 |
|
|
|
414 |
|
|
if (event & EV_READ) { |
415 |
|
|
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) |
416 |
|
|
fatal("imsg_read error"); |
417 |
|
|
if (n == 0) /* Connection closed. */ |
418 |
|
|
shut = 1; |
419 |
|
|
} |
420 |
|
|
if (event & EV_WRITE) { |
421 |
|
|
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) |
422 |
|
|
fatal("msgbuf_write"); |
423 |
|
|
if (n == 0) /* Connection closed. */ |
424 |
|
|
shut = 1; |
425 |
|
|
} |
426 |
|
|
|
427 |
|
|
for (;;) { |
428 |
|
|
if ((n = imsg_get(ibuf, &imsg)) == -1) |
429 |
|
|
fatal("%s: imsg_get error", __func__); |
430 |
|
|
if (n == 0) /* No more messages. */ |
431 |
|
|
break; |
432 |
|
|
|
433 |
|
|
switch (imsg.hdr.type) { |
434 |
|
|
#ifndef SMALL |
435 |
|
|
case IMSG_CTL_END: |
436 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO: |
437 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO_RA: |
438 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX: |
439 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS: |
440 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL: |
441 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS: |
442 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL: |
443 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS: |
444 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL: |
445 |
|
|
control_imsg_relay(&imsg); |
446 |
|
|
break; |
447 |
|
|
#endif /* SMALL */ |
448 |
|
|
case IMSG_CTL_SEND_SOLICITATION: |
449 |
|
|
if_index = *((uint32_t *)imsg.data); |
450 |
|
|
send_solicitation(if_index); |
451 |
|
|
break; |
452 |
|
|
case IMSG_FAKE_ACK: |
453 |
|
|
frontend_imsg_compose_engine(IMSG_PROPOSAL_ACK, |
454 |
|
|
0, 0, imsg.data, sizeof(struct imsg_proposal_ack)); |
455 |
|
|
break; |
456 |
|
|
default: |
457 |
|
|
log_debug("%s: error handling imsg %d", __func__, |
458 |
|
|
imsg.hdr.type); |
459 |
|
|
break; |
460 |
|
|
} |
461 |
|
|
imsg_free(&imsg); |
462 |
|
|
} |
463 |
|
|
if (!shut) |
464 |
|
|
imsg_event_add(iev); |
465 |
|
|
else { |
466 |
|
|
/* This pipe is dead. Remove its event handler. */ |
467 |
|
|
event_del(&iev->ev); |
468 |
|
|
event_loopexit(NULL); |
469 |
|
|
} |
470 |
|
|
} |
471 |
|
|
|
472 |
|
|
int |
473 |
|
|
get_flags(char *if_name) |
474 |
|
|
{ |
475 |
|
|
struct ifreq ifr; |
476 |
|
|
|
477 |
|
|
(void) strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); |
478 |
|
|
if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) |
479 |
|
|
fatal("SIOCGIFFLAGS"); |
480 |
|
|
return ifr.ifr_flags; |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
int |
484 |
|
|
get_xflags(char *if_name) |
485 |
|
|
{ |
486 |
|
|
struct ifreq ifr; |
487 |
|
|
|
488 |
|
|
(void) strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); |
489 |
|
|
if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) < 0) |
490 |
|
|
fatal("SIOCGIFXFLAGS"); |
491 |
|
|
return ifr.ifr_flags; |
492 |
|
|
} |
493 |
|
|
|
494 |
|
|
void |
495 |
|
|
update_iface(uint32_t if_index, char* if_name) |
496 |
|
|
{ |
497 |
|
|
struct imsg_ifinfo imsg_ifinfo; |
498 |
|
|
int flags, xflags; |
499 |
|
|
|
500 |
|
|
flags = get_flags(if_name); |
501 |
|
|
xflags = get_xflags(if_name); |
502 |
|
|
|
503 |
|
|
if (!(xflags & IFXF_AUTOCONF6)) |
504 |
|
|
return; |
505 |
|
|
|
506 |
|
|
memset(&imsg_ifinfo, 0, sizeof(imsg_ifinfo)); |
507 |
|
|
|
508 |
|
|
imsg_ifinfo.if_index = if_index; |
509 |
|
|
imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | |
510 |
|
|
IFF_RUNNING); |
511 |
|
|
imsg_ifinfo.autoconfprivacy = !(xflags & IFXF_INET6_NOPRIVACY); |
512 |
|
|
get_lladdr(if_name, &imsg_ifinfo.hw_address, &imsg_ifinfo.ll_address); |
513 |
|
|
|
514 |
|
|
memcpy(&nd_opt_source_link_addr, &imsg_ifinfo.hw_address, |
515 |
|
|
sizeof(nd_opt_source_link_addr)); |
516 |
|
|
|
517 |
|
|
frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &imsg_ifinfo, |
518 |
|
|
sizeof(imsg_ifinfo)); |
519 |
|
|
} |
520 |
|
|
|
521 |
|
|
#ifndef SMALL |
522 |
|
|
void |
523 |
|
|
update_autoconf_addresses(uint32_t if_index, char* if_name) |
524 |
|
|
{ |
525 |
|
|
struct in6_ifreq ifr6; |
526 |
|
|
struct imsg_addrinfo imsg_addrinfo; |
527 |
|
|
struct ifaddrs *ifap, *ifa; |
528 |
|
|
struct in6_addrlifetime *lifetime; |
529 |
|
|
struct sockaddr_in6 *sin6; |
530 |
|
|
time_t t; |
531 |
|
|
int xflags; |
532 |
|
|
|
533 |
|
|
xflags = get_xflags(if_name); |
534 |
|
|
|
535 |
|
|
if (!(xflags & IFXF_AUTOCONF6)) |
536 |
|
|
return; |
537 |
|
|
|
538 |
|
|
memset(&imsg_addrinfo, 0, sizeof(imsg_addrinfo)); |
539 |
|
|
imsg_addrinfo.if_index = if_index; |
540 |
|
|
get_lladdr(if_name, &imsg_addrinfo.hw_address, |
541 |
|
|
&imsg_addrinfo.ll_address); |
542 |
|
|
|
543 |
|
|
if (getifaddrs(&ifap) != 0) |
544 |
|
|
fatal("getifaddrs"); |
545 |
|
|
|
546 |
|
|
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { |
547 |
|
|
if (strcmp(if_name, ifa->ifa_name) != 0) |
548 |
|
|
continue; |
549 |
|
|
if (ifa->ifa_addr->sa_family != AF_INET6) |
550 |
|
|
continue; |
551 |
|
|
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; |
552 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) |
553 |
|
|
continue; |
554 |
|
|
|
555 |
|
|
log_debug("%s: IP: %s", __func__, sin6_to_str(sin6)); |
556 |
|
|
imsg_addrinfo.addr = *sin6; |
557 |
|
|
|
558 |
|
|
memset(&ifr6, 0, sizeof(ifr6)); |
559 |
|
|
(void) strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name)); |
560 |
|
|
memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); |
561 |
|
|
|
562 |
|
|
if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6) < 0) { |
563 |
|
|
log_warn("SIOCGIFAFLAG_IN6"); |
564 |
|
|
continue; |
565 |
|
|
} |
566 |
|
|
|
567 |
|
|
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | |
568 |
|
|
IN6_IFF_PRIVACY))) |
569 |
|
|
continue; |
570 |
|
|
|
571 |
|
|
imsg_addrinfo.privacy = ifr6.ifr_ifru.ifru_flags6 & |
572 |
|
|
IN6_IFF_PRIVACY ? 1 : 0; |
573 |
|
|
|
574 |
|
|
memset(&ifr6, 0, sizeof(ifr6)); |
575 |
|
|
(void) strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name)); |
576 |
|
|
memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); |
577 |
|
|
|
578 |
|
|
if (ioctl(ioctlsock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) < 0) { |
579 |
|
|
log_warn("SIOCGIFNETMASK_IN6"); |
580 |
|
|
continue; |
581 |
|
|
} |
582 |
|
|
|
583 |
|
|
imsg_addrinfo.mask = ((struct sockaddr_in6 *)&ifr6.ifr_addr) |
584 |
|
|
->sin6_addr; |
585 |
|
|
|
586 |
|
|
memset(&ifr6, 0, sizeof(ifr6)); |
587 |
|
|
(void) strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name)); |
588 |
|
|
memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); |
589 |
|
|
lifetime = &ifr6.ifr_ifru.ifru_lifetime; |
590 |
|
|
|
591 |
|
|
if (ioctl(ioctlsock, SIOCGIFALIFETIME_IN6, (caddr_t)&ifr6) < |
592 |
|
|
0) { |
593 |
|
|
log_warn("SIOCGIFALIFETIME_IN6"); |
594 |
|
|
continue; |
595 |
|
|
} |
596 |
|
|
|
597 |
|
|
imsg_addrinfo.vltime = ND6_INFINITE_LIFETIME; |
598 |
|
|
imsg_addrinfo.pltime = ND6_INFINITE_LIFETIME; |
599 |
|
|
t = time(NULL); |
600 |
|
|
|
601 |
|
|
if (lifetime->ia6t_preferred) |
602 |
|
|
imsg_addrinfo.pltime = lifetime->ia6t_preferred < t ? 0 |
603 |
|
|
: lifetime->ia6t_preferred - t; |
604 |
|
|
|
605 |
|
|
if (lifetime->ia6t_expire) |
606 |
|
|
imsg_addrinfo.vltime = lifetime->ia6t_expire < t ? 0 : |
607 |
|
|
lifetime->ia6t_expire - t; |
608 |
|
|
|
609 |
|
|
frontend_imsg_compose_main(IMSG_UPDATE_ADDRESS, 0, |
610 |
|
|
&imsg_addrinfo, sizeof(imsg_addrinfo)); |
611 |
|
|
|
612 |
|
|
} |
613 |
|
|
freeifaddrs(ifap); |
614 |
|
|
} |
615 |
|
|
#endif /* SMALL */ |
616 |
|
|
|
617 |
|
|
void |
618 |
|
|
frontend_startup(void) |
619 |
|
|
{ |
620 |
|
|
struct if_nameindex *ifnidxp, *ifnidx; |
621 |
|
|
|
622 |
|
|
event_add(&ev_route, NULL); |
623 |
|
|
event_add(&icmp6ev.ev, NULL); |
624 |
|
|
|
625 |
|
|
if ((ifnidxp = if_nameindex()) == NULL) |
626 |
|
|
fatalx("if_nameindex"); |
627 |
|
|
|
628 |
|
|
for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL; |
629 |
|
|
ifnidx++) { |
630 |
|
|
update_iface(ifnidx->if_index, ifnidx->if_name); |
631 |
|
|
#ifndef SMALL |
632 |
|
|
update_autoconf_addresses(ifnidx->if_index, ifnidx->if_name); |
633 |
|
|
#endif /* SMALL */ |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
if_freenameindex(ifnidxp); |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
void |
640 |
|
|
route_receive(int fd, short events, void *arg) |
641 |
|
|
{ |
642 |
|
|
static uint8_t buf[ROUTE_SOCKET_BUF_SIZE]; |
643 |
|
|
|
644 |
|
|
struct rt_msghdr *rtm = (struct rt_msghdr *)buf; |
645 |
|
|
struct sockaddr *sa, *rti_info[RTAX_MAX]; |
646 |
|
|
ssize_t n; |
647 |
|
|
|
648 |
|
|
if ((n = read(fd, &buf, sizeof(buf))) == -1) { |
649 |
|
|
if (errno == EAGAIN || errno == EINTR) |
650 |
|
|
return; |
651 |
|
|
log_warn("dispatch_rtmsg: read error"); |
652 |
|
|
return; |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
if (n == 0) |
656 |
|
|
fatal("routing socket closed"); |
657 |
|
|
|
658 |
|
|
if (n < rtm->rtm_msglen) { |
659 |
|
|
log_warnx("partial rtm in buffer"); |
660 |
|
|
return; |
661 |
|
|
} |
662 |
|
|
|
663 |
|
|
if (rtm->rtm_version != RTM_VERSION) |
664 |
|
|
return; |
665 |
|
|
|
666 |
|
|
sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen); |
667 |
|
|
get_rtaddrs(rtm->rtm_addrs, sa, rti_info); |
668 |
|
|
|
669 |
|
|
handle_route_message(rtm, rti_info); |
670 |
|
|
} |
671 |
|
|
|
672 |
|
|
void |
673 |
|
|
handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) |
674 |
|
|
{ |
675 |
|
|
struct if_msghdr *ifm; |
676 |
|
|
struct imsg_proposal_ack proposal_ack; |
677 |
|
|
struct imsg_del_addr del_addr; |
678 |
|
|
struct sockaddr_rtlabel *rl; |
679 |
|
|
int64_t id, pid; |
680 |
|
|
int flags, xflags, if_index; |
681 |
|
|
char ifnamebuf[IFNAMSIZ]; |
682 |
|
|
char *if_name; |
683 |
|
|
char **ap, *argv[4], *p; |
684 |
|
|
const char *errstr; |
685 |
|
|
|
686 |
|
|
switch (rtm->rtm_type) { |
687 |
|
|
case RTM_IFINFO: |
688 |
|
|
ifm = (struct if_msghdr *)rtm; |
689 |
|
|
if_name = if_indextoname(ifm->ifm_index, ifnamebuf); |
690 |
|
|
if (if_name == NULL) { |
691 |
|
|
log_debug("RTM_IFINFO: lost if %d", ifm->ifm_index); |
692 |
|
|
if_index = ifm->ifm_index; |
693 |
|
|
frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, |
694 |
|
|
&if_index, sizeof(if_index)); |
695 |
|
|
} else { |
696 |
|
|
xflags = get_xflags(if_name); |
697 |
|
|
flags = get_flags(if_name); |
698 |
|
|
if (!(xflags & IFXF_AUTOCONF6)) { |
699 |
|
|
log_debug("RTM_IFINFO: %s(%d) no(longer) " |
700 |
|
|
"autoconf6", if_name, ifm->ifm_index); |
701 |
|
|
if_index = ifm->ifm_index; |
702 |
|
|
frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, |
703 |
|
|
0, &if_index, sizeof(if_index)); |
704 |
|
|
} else { |
705 |
|
|
update_iface(ifm->ifm_index, if_name); |
706 |
|
|
#ifndef SMALL |
707 |
|
|
update_autoconf_addresses(ifm->ifm_index, |
708 |
|
|
if_name); |
709 |
|
|
#endif /* SMALL */ |
710 |
|
|
} |
711 |
|
|
} |
712 |
|
|
break; |
713 |
|
|
case RTM_NEWADDR: |
714 |
|
|
ifm = (struct if_msghdr *)rtm; |
715 |
|
|
if_name = if_indextoname(ifm->ifm_index, ifnamebuf); |
716 |
|
|
log_debug("RTM_NEWADDR: %s[%u]", if_name, ifm->ifm_index); |
717 |
|
|
update_iface(ifm->ifm_index, if_name); |
718 |
|
|
break; |
719 |
|
|
case RTM_DELADDR: |
720 |
|
|
ifm = (struct if_msghdr *)rtm; |
721 |
|
|
if_name = if_indextoname(ifm->ifm_index, ifnamebuf); |
722 |
|
|
if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family |
723 |
|
|
== AF_INET6) { |
724 |
|
|
del_addr.if_index = ifm->ifm_index; |
725 |
|
|
memcpy(&del_addr.addr, rti_info[RTAX_IFA], sizeof( |
726 |
|
|
del_addr.addr)); |
727 |
|
|
frontend_imsg_compose_engine(IMSG_DEL_ADDRESS, |
728 |
|
|
0, 0, &del_addr, sizeof(del_addr)); |
729 |
|
|
log_debug("RTM_DELADDR: %s[%u]", if_name, |
730 |
|
|
ifm->ifm_index); |
731 |
|
|
} |
732 |
|
|
break; |
733 |
|
|
case RTM_PROPOSAL: |
734 |
|
|
ifm = (struct if_msghdr *)rtm; |
735 |
|
|
if_name = if_indextoname(ifm->ifm_index, ifnamebuf); |
736 |
|
|
|
737 |
|
|
if ((rtm->rtm_flags & (RTF_DONE | RTF_PROTO1)) == |
738 |
|
|
(RTF_DONE | RTF_PROTO1) && rtm->rtm_addrs == RTA_LABEL) { |
739 |
|
|
rl = (struct sockaddr_rtlabel *)rti_info[RTAX_LABEL]; |
740 |
|
|
/* XXX validate rl */ |
741 |
|
|
|
742 |
|
|
p = rl->sr_label; |
743 |
|
|
|
744 |
|
|
for (ap = argv; ap < &argv[3] && (*ap = |
745 |
|
|
strsep(&p, " ")) != NULL;) { |
746 |
|
|
if (**ap != '\0') |
747 |
|
|
ap++; |
748 |
|
|
} |
749 |
|
|
*ap = NULL; |
750 |
|
|
|
751 |
|
|
if (argv[0] != NULL && strncmp(argv[0], "slaacd:", |
752 |
|
|
strlen("slaacd:")) == 0 && argv[1] != NULL && |
753 |
|
|
argv[2] != NULL && argv[3] == NULL) { |
754 |
|
|
id = strtonum(argv[1], 0, INT64_MAX, &errstr); |
755 |
|
|
if (errstr != NULL) { |
756 |
|
|
log_warn("%s: proposal seq is %s: %s", |
757 |
|
|
__func__, errstr, argv[1]); |
758 |
|
|
break; |
759 |
|
|
} |
760 |
|
|
pid = strtonum(argv[2], 0, INT32_MAX, &errstr); |
761 |
|
|
if (errstr != NULL) { |
762 |
|
|
log_warn("%s: pid is %s: %s", |
763 |
|
|
__func__, errstr, argv[2]); |
764 |
|
|
break; |
765 |
|
|
} |
766 |
|
|
proposal_ack.id = id; |
767 |
|
|
proposal_ack.pid = pid; |
768 |
|
|
proposal_ack.if_index = ifm->ifm_index; |
769 |
|
|
|
770 |
|
|
frontend_imsg_compose_engine(IMSG_PROPOSAL_ACK, |
771 |
|
|
0, 0, &proposal_ack, sizeof(proposal_ack)); |
772 |
|
|
} else { |
773 |
|
|
log_debug("cannot parse: %s", rl->sr_label); |
774 |
|
|
} |
775 |
|
|
} else { |
776 |
|
|
#if 0 |
777 |
|
|
log_debug("%s: got flags %x, expcted %x", __func__, |
778 |
|
|
rtm->rtm_flags, (RTF_DONE | RTF_PROTO1)); |
779 |
|
|
#endif |
780 |
|
|
} |
781 |
|
|
|
782 |
|
|
break; |
783 |
|
|
default: |
784 |
|
|
log_debug("unexpected RTM: %d", rtm->rtm_type); |
785 |
|
|
break; |
786 |
|
|
} |
787 |
|
|
|
788 |
|
|
} |
789 |
|
|
|
790 |
|
|
#define ROUNDUP(a) \ |
791 |
|
|
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) |
792 |
|
|
|
793 |
|
|
void |
794 |
|
|
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) |
795 |
|
|
{ |
796 |
|
|
int i; |
797 |
|
|
|
798 |
|
|
for (i = 0; i < RTAX_MAX; i++) { |
799 |
|
|
if (addrs & (1 << i)) { |
800 |
|
|
rti_info[i] = sa; |
801 |
|
|
sa = (struct sockaddr *)((char *)(sa) + |
802 |
|
|
ROUNDUP(sa->sa_len)); |
803 |
|
|
} else |
804 |
|
|
rti_info[i] = NULL; |
805 |
|
|
} |
806 |
|
|
} |
807 |
|
|
|
808 |
|
|
void |
809 |
|
|
get_lladdr(char *if_name, struct ether_addr *mac, struct sockaddr_in6 *ll) |
810 |
|
|
{ |
811 |
|
|
struct ifaddrs *ifap, *ifa; |
812 |
|
|
struct sockaddr_dl *sdl; |
813 |
|
|
struct sockaddr_in6 *sin6; |
814 |
|
|
|
815 |
|
|
if (getifaddrs(&ifap) != 0) |
816 |
|
|
fatal("getifaddrs"); |
817 |
|
|
|
818 |
|
|
memset(mac, 0, sizeof(*mac)); |
819 |
|
|
memset(ll, 0, sizeof(*ll)); |
820 |
|
|
|
821 |
|
|
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { |
822 |
|
|
if (strcmp(if_name, ifa->ifa_name) != 0) |
823 |
|
|
continue; |
824 |
|
|
|
825 |
|
|
switch(ifa->ifa_addr->sa_family) { |
826 |
|
|
case AF_LINK: |
827 |
|
|
sdl = (struct sockaddr_dl *)ifa->ifa_addr; |
828 |
|
|
if (sdl->sdl_type != IFT_ETHER || |
829 |
|
|
sdl->sdl_alen != ETHER_ADDR_LEN) |
830 |
|
|
continue; |
831 |
|
|
|
832 |
|
|
memcpy(mac->ether_addr_octet, LLADDR(sdl), |
833 |
|
|
ETHER_ADDR_LEN); |
834 |
|
|
break; |
835 |
|
|
case AF_INET6: |
836 |
|
|
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; |
837 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { |
838 |
|
|
sin6->sin6_scope_id = ntohs(*(u_int16_t *) |
839 |
|
|
&sin6->sin6_addr.s6_addr[2]); |
840 |
|
|
sin6->sin6_addr.s6_addr[2] = |
841 |
|
|
sin6->sin6_addr.s6_addr[3] = 0; |
842 |
|
|
memcpy(ll, sin6, sizeof(*ll)); |
843 |
|
|
} |
844 |
|
|
break; |
845 |
|
|
default: |
846 |
|
|
break; |
847 |
|
|
} |
848 |
|
|
} |
849 |
|
|
freeifaddrs(ifap); |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
void |
853 |
|
|
icmp6_receive(int fd, short events, void *arg) |
854 |
|
|
{ |
855 |
|
|
struct imsg_ra ra; |
856 |
|
|
|
857 |
|
|
struct in6_pktinfo *pi = NULL; |
858 |
|
|
struct cmsghdr *cm; |
859 |
|
|
ssize_t len; |
860 |
|
|
int if_index = 0, *hlimp = NULL; |
861 |
|
|
char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; |
862 |
|
|
uint8_t *p; |
863 |
|
|
|
864 |
|
|
p = icmp6ev.answer; |
865 |
|
|
|
866 |
|
|
if ((len = recvmsg(fd, &icmp6ev.rcvmhdr, 0)) < 0) { |
867 |
|
|
log_warn("recvmsg"); |
868 |
|
|
return; |
869 |
|
|
} |
870 |
|
|
|
871 |
|
|
/* extract optional information via Advanced API */ |
872 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&icmp6ev.rcvmhdr); cm; |
873 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(&icmp6ev.rcvmhdr, cm)) { |
874 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
875 |
|
|
cm->cmsg_type == IPV6_PKTINFO && |
876 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { |
877 |
|
|
pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); |
878 |
|
|
if_index = pi->ipi6_ifindex; |
879 |
|
|
} |
880 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
881 |
|
|
cm->cmsg_type == IPV6_HOPLIMIT && |
882 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(int))) |
883 |
|
|
hlimp = (int *)CMSG_DATA(cm); |
884 |
|
|
} |
885 |
|
|
|
886 |
|
|
if (if_index == 0) { |
887 |
|
|
log_warnx("failed to get receiving interface"); |
888 |
|
|
return; |
889 |
|
|
} |
890 |
|
|
|
891 |
|
|
if (hlimp == NULL) { |
892 |
|
|
log_warnx("failed to get receiving hop limit"); |
893 |
|
|
return; |
894 |
|
|
} |
895 |
|
|
|
896 |
|
|
if (*hlimp != 255) { |
897 |
|
|
log_warnx("invalid RA with hop limit of %d from %s on %s", |
898 |
|
|
*hlimp, inet_ntop(AF_INET6, &icmp6ev.from.sin6_addr, |
899 |
|
|
ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index, |
900 |
|
|
ifnamebuf)); |
901 |
|
|
return; |
902 |
|
|
} |
903 |
|
|
|
904 |
|
|
if ((size_t)len > sizeof(ra.packet)) { |
905 |
|
|
log_warnx("invalid RA with size %ld from %s on %s", |
906 |
|
|
len, inet_ntop(AF_INET6, &icmp6ev.from.sin6_addr, |
907 |
|
|
ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index, |
908 |
|
|
ifnamebuf)); |
909 |
|
|
return; |
910 |
|
|
} |
911 |
|
|
|
912 |
|
|
ra.if_index = if_index; |
913 |
|
|
memcpy(&ra.from, &icmp6ev.from, sizeof(ra.from)); |
914 |
|
|
ra.len = len; |
915 |
|
|
memcpy(ra.packet, icmp6ev.answer, len); |
916 |
|
|
|
917 |
|
|
frontend_imsg_compose_engine(IMSG_RA, 0, 0, &ra, sizeof(ra)); |
918 |
|
|
} |
919 |
|
|
|
920 |
|
|
void |
921 |
|
|
send_solicitation(uint32_t if_index) |
922 |
|
|
{ |
923 |
|
|
struct in6_pktinfo *pi; |
924 |
|
|
struct cmsghdr *cm; |
925 |
|
|
|
926 |
|
|
log_debug("%s(%u)", __func__, if_index); |
927 |
|
|
|
928 |
|
|
dst.sin6_scope_id = if_index; |
929 |
|
|
|
930 |
|
|
cm = CMSG_FIRSTHDR(&sndmhdr); |
931 |
|
|
pi = (struct in6_pktinfo *)CMSG_DATA(cm); |
932 |
|
|
pi->ipi6_ifindex = if_index; |
933 |
|
|
|
934 |
|
|
if (sendmsg(icmp6sock, &sndmhdr, 0) != sizeof(rs) + |
935 |
|
|
sizeof(nd_opt_hdr) + sizeof(nd_opt_source_link_addr)) |
936 |
|
|
log_warn("sendmsg"); |
937 |
|
|
} |