1 /* $OpenBSD: dvmrpe.c,v 1.30 2024/11/21 13:38:14 claudio Exp $ */
2
3 /*
4 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5 * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
6 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/queue.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <net/if_types.h>
27 #include <stdlib.h>
28 #include <signal.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <pwd.h>
32 #include <unistd.h>
33 #include <event.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <stdio.h>
37
38 #include "igmp.h"
39 #include "dvmrp.h"
40 #include "dvmrpd.h"
41 #include "dvmrpe.h"
42 #include "control.h"
43 #include "log.h"
44
45 void dvmrpe_sig_handler(int, short, void *);
46 __dead void dvmrpe_shutdown(void);
47
48 volatile sig_atomic_t dvmrpe_quit = 0;
49 struct dvmrpd_conf *deconf = NULL;
50 static struct imsgev *iev_main;
51 static struct imsgev *iev_rde;
52
53 void
dvmrpe_sig_handler(int sig,short event,void * bula)54 dvmrpe_sig_handler(int sig, short event, void *bula)
55 {
56 switch (sig) {
57 case SIGINT:
58 case SIGTERM:
59 dvmrpe_shutdown();
60 /* NOTREACHED */
61 default:
62 fatalx("unexpected signal");
63 }
64 }
65
66 /* dvmrp engine */
67 pid_t
dvmrpe(struct dvmrpd_conf * xconf,int pipe_parent2dvmrpe[2],int pipe_dvmrpe2rde[2],int pipe_parent2rde[2])68 dvmrpe(struct dvmrpd_conf *xconf, int pipe_parent2dvmrpe[2],
69 int pipe_dvmrpe2rde[2], int pipe_parent2rde[2])
70 {
71 struct iface *iface = NULL;
72 struct passwd *pw;
73 struct event ev_sigint, ev_sigterm;
74 pid_t pid;
75
76 switch (pid = fork()) {
77 case -1:
78 fatal("cannot fork");
79 case 0:
80 break;
81 default:
82
83 return (pid);
84 }
85
86 /* create the raw ip socket */
87 if ((xconf->dvmrp_socket = socket(AF_INET,
88 SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
89 IPPROTO_IGMP)) == -1)
90 fatal("error creating raw socket");
91
92 /* create dvmrpd control socket outside chroot */
93 if (control_init() == -1)
94 fatalx("control socket setup failed");
95
96 /* set some defaults */
97 if (if_set_mcast_ttl(xconf->dvmrp_socket,
98 IP_DEFAULT_MULTICAST_TTL) == -1)
99 fatal("if_set_mcast_ttl");
100
101 if (if_set_mcast_loop(xconf->dvmrp_socket) == -1)
102 fatal("if_set_mcast_loop");
103
104 if (if_set_tos(xconf->dvmrp_socket, IPTOS_PREC_INTERNETCONTROL) == -1)
105 fatal("if_set_tos");
106
107 if_set_recvbuf(xconf->dvmrp_socket);
108
109 deconf = xconf;
110
111 if ((pw = getpwnam(DVMRPD_USER)) == NULL)
112 fatal("getpwnam");
113
114 if (chroot(pw->pw_dir) == -1)
115 fatal("chroot");
116 if (chdir("/") == -1)
117 fatal("chdir(\"/\")");
118
119 setproctitle("dvmrp engine");
120 log_procname = "dvmrpe";
121
122 if (setgroups(1, &pw->pw_gid) ||
123 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
124 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
125 fatal("can't drop privileges");
126
127 event_init();
128 nbr_init(NBR_HASHSIZE);
129
130 /* setup signal handler */
131 signal_set(&ev_sigint, SIGINT, dvmrpe_sig_handler, NULL);
132 signal_set(&ev_sigterm, SIGTERM, dvmrpe_sig_handler, NULL);
133 signal_add(&ev_sigint, NULL);
134 signal_add(&ev_sigterm, NULL);
135 signal(SIGPIPE, SIG_IGN);
136
137 /* setup pipes */
138 close(pipe_parent2dvmrpe[0]);
139 close(pipe_dvmrpe2rde[1]);
140 close(pipe_parent2rde[0]);
141 close(pipe_parent2rde[1]);
142
143 if ((iev_rde = malloc(sizeof(struct imsgev))) == NULL ||
144 (iev_main = malloc(sizeof(struct imsgev))) == NULL)
145 fatal(NULL);
146 if (imsgbuf_init(&iev_rde->ibuf, pipe_dvmrpe2rde[0]) == -1)
147 fatal(NULL);
148 iev_rde->handler = dvmrpe_dispatch_rde;
149
150 if (imsgbuf_init(&iev_main->ibuf, pipe_parent2dvmrpe[1]) == -1)
151 fatal(NULL);
152 iev_main->handler = dvmrpe_dispatch_main;
153
154 /* setup event handler */
155 iev_rde->events = EV_READ;
156 event_set(&iev_rde->ev, iev_rde->ibuf.fd, iev_rde->events,
157 iev_rde->handler, iev_rde);
158 event_add(&iev_rde->ev, NULL);
159
160 iev_main->events = EV_READ;
161 event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
162 iev_main->handler, iev_main);
163 event_add(&iev_main->ev, NULL);
164
165 event_set(&deconf->ev, deconf->dvmrp_socket, EV_READ|EV_PERSIST,
166 recv_packet, deconf);
167 event_add(&deconf->ev, NULL);
168
169 /* listen on dvmrpd control socket */
170 control_listen();
171
172 /* start interfaces */
173 LIST_FOREACH(iface, &deconf->iface_list, entry) {
174 if_init(xconf, iface);
175 if (if_fsm(iface, IF_EVT_UP)) {
176 log_debug("error starting interface %s", iface->name);
177 }
178 }
179
180 evtimer_set(&deconf->report_timer, report_timer, deconf);
181 start_report_timer();
182
183 event_dispatch();
184
185 dvmrpe_shutdown();
186 /* NOTREACHED */
187 return (0);
188 }
189
190 __dead void
dvmrpe_shutdown(void)191 dvmrpe_shutdown(void)
192 {
193 struct iface *iface;
194
195 /* close pipes */
196 imsgbuf_write(&iev_rde->ibuf);
197 imsgbuf_clear(&iev_rde->ibuf);
198 close(iev_rde->ibuf.fd);
199 imsgbuf_write(&iev_main->ibuf);
200 imsgbuf_clear(&iev_main->ibuf);
201 close(iev_main->ibuf.fd);
202
203 /* stop all interfaces and delete them */
204 LIST_FOREACH(iface, &deconf->iface_list, entry) {
205 if (if_fsm(iface, IF_EVT_DOWN)) {
206 log_debug("error stopping interface %s",
207 iface->name);
208 }
209 if_del(iface);
210 }
211
212 /* clean up */
213 free(iev_rde);
214 free(iev_main);
215
216 log_info("dvmrp engine exiting");
217 _exit(0);
218 }
219
220 int
dvmrpe_imsg_compose_parent(int type,pid_t pid,void * data,u_int16_t datalen)221 dvmrpe_imsg_compose_parent(int type, pid_t pid, void *data, u_int16_t datalen)
222 {
223 return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen));
224 }
225
226 int
dvmrpe_imsg_compose_rde(int type,u_int32_t peerid,pid_t pid,void * data,u_int16_t datalen)227 dvmrpe_imsg_compose_rde(int type, u_int32_t peerid, pid_t pid,
228 void *data, u_int16_t datalen)
229 {
230 return (imsg_compose_event(iev_rde, type, peerid, pid,
231 -1, data, datalen));
232 }
233
234 void
dvmrpe_dispatch_main(int fd,short event,void * bula)235 dvmrpe_dispatch_main(int fd, short event, void *bula)
236 {
237 struct imsg imsg;
238 struct imsgev *iev = bula;
239 struct imsgbuf *ibuf = &iev->ibuf;
240 struct kif *kif;
241 struct iface *iface;
242 ssize_t n;
243 int shut = 0, link_ok;
244
245 if (event & EV_READ) {
246 if ((n = imsgbuf_read(ibuf)) == -1)
247 fatal("imsgbuf_read error");
248 if (n == 0) /* connection closed */
249 shut = 1;
250 }
251 if (event & EV_WRITE) {
252 if (imsgbuf_write(ibuf) == -1) {
253 if (errno == EPIPE) /* connection closed */
254 shut = 1;
255 else
256 fatal("imsgbuf_write");
257 }
258 }
259
260 for (;;) {
261 if ((n = imsg_get(ibuf, &imsg)) == -1)
262 fatal("dvmrpe_dispatch_main: imsg_get error");
263 if (n == 0)
264 break;
265
266 switch (imsg.hdr.type) {
267 case IMSG_IFINFO:
268 if (imsg.hdr.len - IMSG_HEADER_SIZE !=
269 sizeof(struct kif))
270 fatalx("IFINFO imsg with wrong len");
271 kif = imsg.data;
272 link_ok = (kif->flags & IFF_UP) &&
273 LINK_STATE_IS_UP(kif->link_state);
274
275 LIST_FOREACH(iface, &deconf->iface_list, entry) {
276 if (kif->ifindex == iface->ifindex) {
277 iface->flags = kif->flags;
278 iface->linkstate = kif->link_state;
279
280 if (link_ok) {
281 if_fsm(iface, IF_EVT_UP);
282 log_warnx("interface %s up",
283 iface->name);
284 } else {
285 if_fsm(iface, IF_EVT_DOWN);
286 log_warnx("interface %s down",
287 iface->name);
288 }
289 }
290 }
291 break;
292 default:
293 log_debug("dvmrpe_dispatch_main: error handling "
294 "imsg %d", imsg.hdr.type);
295 break;
296 }
297 imsg_free(&imsg);
298 }
299 if (!shut)
300 imsg_event_add(iev);
301 else {
302 /* this pipe is dead, so remove the event handler */
303 event_del(&iev->ev);
304 event_loopexit(NULL);
305 }
306 }
307
308 void
dvmrpe_dispatch_rde(int fd,short event,void * bula)309 dvmrpe_dispatch_rde(int fd, short event, void *bula)
310 {
311 struct imsgev *iev = bula;
312 struct imsgbuf *ibuf = &iev->ibuf;
313 struct imsg imsg;
314 struct nbr *nbr;
315 struct prune p;
316 struct iface *iface;
317 struct route_report *rr;
318 ssize_t n;
319 int shut = 0;
320
321 if (event & EV_READ) {
322 if ((n = imsgbuf_read(ibuf)) == -1)
323 fatal("imsgbuf_read error");
324 if (n == 0) /* connection closed */
325 shut = 1;
326 }
327 if (event & EV_WRITE) {
328 if (imsgbuf_write(ibuf) == -1) {
329 if (errno == EPIPE) /* connection closed */
330 shut = 1;
331 else
332 fatal("imsgbuf_write");
333 }
334 }
335
336 for (;;) {
337 if ((n = imsg_get(ibuf, &imsg)) == -1)
338 fatal("dvmrpe_dispatch_rde: imsgbuf_get error");
339 if (n == 0)
340 break;
341
342 switch (imsg.hdr.type) {
343 case IMSG_CTL_SHOW_RIB:
344 case IMSG_CTL_SHOW_SUM:
345 case IMSG_CTL_SHOW_MFC:
346 case IMSG_CTL_END:
347 control_imsg_relay(&imsg);
348 break;
349 case IMSG_FULL_ROUTE_REPORT:
350 /* add route reports to list */
351 if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr))
352 fatalx("invalid size of RDE request");
353
354 if ((rr = calloc(1, sizeof(*rr))) == NULL)
355 fatal("dvmrpe_dispatch_rde");
356
357 memcpy(rr, imsg.data, sizeof(*rr));
358
359 /* general update, per interface */
360 if (imsg.hdr.peerid == 0) {
361 /* add to interface list */
362 LIST_FOREACH(iface, &deconf->iface_list,
363 entry) {
364 if (!if_nbr_list_empty(iface))
365 rr_list_add(&iface->rr_list,
366 rr);
367 }
368 break;
369 }
370
371 /* add to neighbor list */
372 nbr = nbr_find_peerid(imsg.hdr.peerid);
373 rr_list_add(&nbr->rr_list, rr);
374 break;
375 case IMSG_FULL_ROUTE_REPORT_END:
376 /* transmit route report */
377 if (imsg.hdr.peerid == 0) {
378 /*
379 * send general route report on all
380 * interfaces with neighbors.
381 */
382 LIST_FOREACH(iface, &deconf->iface_list,
383 entry) {
384 rr_list_send(&iface->rr_list,
385 iface, NULL);
386 }
387 break;
388 }
389
390 nbr = nbr_find_peerid(imsg.hdr.peerid);
391 rr_list_send(&nbr->rr_list, NULL, nbr);
392 break;
393 case IMSG_SEND_PRUNE:
394 if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(p))
395 fatalx("invalid size of RDE request");
396
397 memcpy(&p, imsg.data, sizeof(p));
398
399 LIST_FOREACH(iface, &deconf->iface_list, entry)
400 if (p.ifindex == iface->ifindex)
401 break;
402
403 if (iface == NULL)
404 fatalx("invalid interface in mfc");
405
406 nbr = nbr_find_ip(iface, p.nexthop.s_addr);
407 if (nbr == NULL)
408 fatalx("unknown neighbor to send prune");
409
410 send_prune(nbr, &p);
411
412 break;
413 case IMSG_FLASH_UPDATE:
414 if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr))
415 fatalx("invalid size of RDE request");
416
417 if ((rr = calloc(1, sizeof(*rr))) == NULL)
418 fatal("dvmrpe_dispatch_rde");
419
420 memcpy(rr, imsg.data, sizeof(*rr));
421
422 LIST_FOREACH(iface, &deconf->iface_list, entry) {
423 if (!if_nbr_list_empty(iface)) {
424 rr_list_add(&iface->rr_list, rr);
425 rr_list_send(&iface->rr_list, iface,
426 NULL);
427 }
428 }
429 break;
430 case IMSG_FLASH_UPDATE_DS:
431 if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr))
432 fatalx("invalid size of RDE request");
433
434 if ((rr = calloc(1, sizeof(*rr))) == NULL)
435 fatal("dvmrpe_dispatch_rde");
436
437 memcpy(rr, imsg.data, sizeof(*rr));
438
439 LIST_FOREACH(iface, &deconf->iface_list, entry) {
440 if (iface->ifindex == rr->ifindex)
441 continue;
442 if (!if_nbr_list_empty(iface)) {
443 rr_list_add(&iface->rr_list, rr);
444 rr_list_send(&iface->rr_list, iface,
445 NULL);
446 }
447 }
448 break;
449 default:
450 log_debug("dvmrpe_dispatch_rde: error handling imsg %d",
451 imsg.hdr.type);
452 break;
453 }
454 imsg_free(&imsg);
455 }
456 if (!shut)
457 imsg_event_add(iev);
458 else {
459 /* this pipe is dead, so remove the event handler */
460 event_del(&iev->ev);
461 event_loopexit(NULL);
462 }
463 }
464
465 void
dvmrpe_iface_ctl(struct ctl_conn * c,unsigned int idx)466 dvmrpe_iface_ctl(struct ctl_conn *c, unsigned int idx)
467 {
468 struct iface *iface;
469 struct ctl_iface *ictl;
470
471 LIST_FOREACH(iface, &deconf->iface_list, entry)
472 if (idx == 0 || idx == iface->ifindex) {
473 ictl = if_to_ctl(iface);
474 imsg_compose_event(&c->iev, IMSG_CTL_SHOW_IFACE,
475 0, 0, -1, ictl, sizeof(struct ctl_iface));
476 }
477 }
478
479 void
dvmrpe_iface_igmp_ctl(struct ctl_conn * c,unsigned int idx)480 dvmrpe_iface_igmp_ctl(struct ctl_conn *c, unsigned int idx)
481 {
482 struct iface *iface;
483 struct ctl_iface *ictl;
484
485 LIST_FOREACH(iface, &deconf->iface_list, entry)
486 if (idx == 0 || idx == iface->ifindex) {
487 ictl = if_to_ctl(iface);
488 imsg_compose_event(&c->iev, IMSG_CTL_SHOW_IFACE,
489 0, 0, -1, ictl, sizeof(struct ctl_iface));
490 group_list_dump(iface, c);
491
492 }
493 }
494
495 void
dvmrpe_nbr_ctl(struct ctl_conn * c)496 dvmrpe_nbr_ctl(struct ctl_conn *c)
497 {
498 struct iface *iface;
499 struct nbr *nbr;
500 struct ctl_nbr *nctl;
501
502 LIST_FOREACH(iface, &deconf->iface_list, entry)
503 LIST_FOREACH(nbr, &iface->nbr_list, entry) {
504 nctl = nbr_to_ctl(nbr);
505 imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR,
506 0, 0, -1, nctl, sizeof(struct ctl_nbr));
507 }
508
509 imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
510 }
511