xref: /openbsd-src/usr.sbin/ospf6d/ospf6d.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: ospf6d.c,v 1.12 2009/03/28 15:38:58 michele Exp $ */
2 
3 /*
4  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2004, 2007 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 <sys/time.h>
25 #include <sys/stat.h>
26 #include <sys/wait.h>
27 #include <sys/param.h>
28 #include <sys/sysctl.h>
29 
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 
33 #include <event.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <pwd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <unistd.h>
42 
43 #include "ospf6d.h"
44 #include "ospf6.h"
45 #include "ospfe.h"
46 #include "control.h"
47 #include "log.h"
48 #include "rde.h"
49 
50 void		main_sig_handler(int, short, void *);
51 __dead void	usage(void);
52 void		ospfd_shutdown(void);
53 int		check_child(pid_t, const char *);
54 
55 void	main_dispatch_ospfe(int, short, void *);
56 void	main_dispatch_rde(int, short, void *);
57 
58 void	ospf_redistribute_default(int);
59 
60 int	ospf_reload(void);
61 int	ospf_sendboth(enum imsg_type, void *, u_int16_t);
62 int	merge_interfaces(struct area *, struct area *);
63 struct iface *iface_lookup(struct area *, struct iface *);
64 
65 int	pipe_parent2ospfe[2];
66 int	pipe_parent2rde[2];
67 int	pipe_ospfe2rde[2];
68 
69 struct ospfd_conf	*ospfd_conf = NULL;
70 struct imsgbuf		*ibuf_ospfe;
71 struct imsgbuf		*ibuf_rde;
72 char			*conffile;
73 
74 pid_t			 ospfe_pid = 0;
75 pid_t			 rde_pid = 0;
76 
77 /* ARGSUSED */
78 void
79 main_sig_handler(int sig, short event, void *arg)
80 {
81 	/*
82 	 * signal handler rules don't apply, libevent decouples for us
83 	 */
84 
85 	int	die = 0;
86 
87 	switch (sig) {
88 	case SIGTERM:
89 	case SIGINT:
90 		die = 1;
91 		/* FALLTHROUGH */
92 	case SIGCHLD:
93 		if (check_child(ospfe_pid, "ospf engine")) {
94 			ospfe_pid = 0;
95 			die = 1;
96 		}
97 		if (check_child(rde_pid, "route decision engine")) {
98 			rde_pid = 0;
99 			die = 1;
100 		}
101 		if (die)
102 			ospfd_shutdown();
103 		break;
104 	case SIGHUP:
105 		if (ospf_reload() == -1)
106 			log_warnx("configuration reload failed");
107 		else
108 			log_debug("configuration reloaded");
109 		break;
110 	default:
111 		fatalx("unexpected signal");
112 		/* NOTREACHED */
113 	}
114 }
115 
116 __dead void
117 usage(void)
118 {
119 	extern char *__progname;
120 
121 	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
122 	    __progname);
123 	exit(1);
124 }
125 
126 int
127 main(int argc, char *argv[])
128 {
129 	struct event		 ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
130 	int			 ch, opts = 0;
131 	int			 debug = 0;
132 	int			 ipforwarding;
133 	int			 mib[4];
134 	size_t			 len;
135 
136 	conffile = CONF_FILE;
137 	ospfd_process = PROC_MAIN;
138 
139 	log_init(1);	/* log to stderr until daemonized */
140 
141 	while ((ch = getopt(argc, argv, "cdD:f:nv")) != -1) {
142 		switch (ch) {
143 		case 'c':
144 			opts |= OSPFD_OPT_FORCE_DEMOTE;
145 			break;
146 		case 'd':
147 			debug = 1;
148 			break;
149 		case 'D':
150 			if (cmdline_symset(optarg) < 0)
151 				log_warnx("could not parse macro definition %s",
152 				    optarg);
153 			break;
154 		case 'f':
155 			conffile = optarg;
156 			break;
157 		case 'n':
158 			opts |= OSPFD_OPT_NOACTION;
159 			break;
160 		case 'v':
161 			if (opts & OSPFD_OPT_VERBOSE)
162 				opts |= OSPFD_OPT_VERBOSE2;
163 			opts |= OSPFD_OPT_VERBOSE;
164 			break;
165 
166 		default:
167 			usage();
168 			/* NOTREACHED */
169 		}
170 	}
171 
172 	argc -= optind;
173 	argv += optind;
174 	if (argc > 0)
175 		usage();
176 
177 	mib[0] = CTL_NET;
178 	mib[1] = PF_INET6;
179 	mib[2] = IPPROTO_IPV6;
180 	mib[3] = IPCTL_FORWARDING;
181 	len = sizeof(ipforwarding);
182 	if (sysctl(mib, 4, &ipforwarding, &len, NULL, 0) == -1)
183 		err(1, "sysctl");
184 
185 	if (ipforwarding != 1) {
186 		log_warnx("WARNING: IPv6 forwarding NOT enabled, "
187 		    "running as stub router");
188 		opts |= OSPFD_OPT_STUB_ROUTER;
189 	}
190 
191 	/* prepare and fetch interfaces early */
192 	if_init();
193 
194 	/* parse config file */
195 	if ((ospfd_conf = parse_config(conffile, opts)) == NULL )
196 		exit(1);
197 
198 	if (ospfd_conf->opts & OSPFD_OPT_NOACTION) {
199 		if (ospfd_conf->opts & OSPFD_OPT_VERBOSE)
200 			print_config(ospfd_conf);
201 		else
202 			fprintf(stderr, "configuration OK\n");
203 		exit(0);
204 	}
205 
206 	/* check for root privileges  */
207 	if (geteuid())
208 		errx(1, "need root privileges");
209 
210 	/* check for ospfd user */
211 	if (getpwnam(OSPF6D_USER) == NULL)
212 		errx(1, "unknown user %s", OSPF6D_USER);
213 
214 	log_init(debug);
215 
216 	if (!debug)
217 		daemon(1, 0);
218 
219 	log_info("startup");
220 
221 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
222 	    pipe_parent2ospfe) == -1)
223 		fatal("socketpair");
224 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2rde) == -1)
225 		fatal("socketpair");
226 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_ospfe2rde) == -1)
227 		fatal("socketpair");
228 	session_socket_blockmode(pipe_parent2ospfe[0], BM_NONBLOCK);
229 	session_socket_blockmode(pipe_parent2ospfe[1], BM_NONBLOCK);
230 	session_socket_blockmode(pipe_parent2rde[0], BM_NONBLOCK);
231 	session_socket_blockmode(pipe_parent2rde[1], BM_NONBLOCK);
232 	session_socket_blockmode(pipe_ospfe2rde[0], BM_NONBLOCK);
233 	session_socket_blockmode(pipe_ospfe2rde[1], BM_NONBLOCK);
234 
235 	/* start children */
236 	rde_pid = rde(ospfd_conf, pipe_parent2rde, pipe_ospfe2rde,
237 	    pipe_parent2ospfe);
238 	ospfe_pid = ospfe(ospfd_conf, pipe_parent2ospfe, pipe_ospfe2rde,
239 	    pipe_parent2rde);
240 
241 	/* show who we are */
242 	setproctitle("parent");
243 
244 	event_init();
245 
246 	/* setup signal handler */
247 	signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
248 	signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
249 	signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, NULL);
250 	signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
251 	signal_add(&ev_sigint, NULL);
252 	signal_add(&ev_sigterm, NULL);
253 	signal_add(&ev_sigchld, NULL);
254 	signal_add(&ev_sighup, NULL);
255 	signal(SIGPIPE, SIG_IGN);
256 
257 	/* setup pipes to children */
258 	close(pipe_parent2ospfe[1]);
259 	close(pipe_parent2rde[1]);
260 	close(pipe_ospfe2rde[0]);
261 	close(pipe_ospfe2rde[1]);
262 
263 	if ((ibuf_ospfe = malloc(sizeof(struct imsgbuf))) == NULL ||
264 	    (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL)
265 		fatal(NULL);
266 	imsg_init(ibuf_ospfe, pipe_parent2ospfe[0], main_dispatch_ospfe);
267 	imsg_init(ibuf_rde, pipe_parent2rde[0], main_dispatch_rde);
268 
269 	/* setup event handler */
270 	ibuf_ospfe->events = EV_READ;
271 	event_set(&ibuf_ospfe->ev, ibuf_ospfe->fd, ibuf_ospfe->events,
272 	    ibuf_ospfe->handler, ibuf_ospfe);
273 	event_add(&ibuf_ospfe->ev, NULL);
274 
275 	ibuf_rde->events = EV_READ;
276 	event_set(&ibuf_rde->ev, ibuf_rde->fd, ibuf_rde->events,
277 	    ibuf_rde->handler, ibuf_rde);
278 	event_add(&ibuf_rde->ev, NULL);
279 
280 	if (kr_init(!(ospfd_conf->flags & OSPFD_FLAG_NO_FIB_UPDATE)) == -1)
281 		fatalx("kr_init failed");
282 
283 	/* redistribute default */
284 	ospf_redistribute_default(IMSG_NETWORK_ADD);
285 
286 	event_dispatch();
287 
288 	ospfd_shutdown();
289 	/* NOTREACHED */
290 	return (0);
291 }
292 
293 void
294 ospfd_shutdown(void)
295 {
296 	pid_t	pid;
297 
298 	if (ospfe_pid)
299 		kill(ospfe_pid, SIGTERM);
300 
301 	if (rde_pid)
302 		kill(rde_pid, SIGTERM);
303 
304 	control_cleanup();
305 	kr_shutdown();
306 	carp_demote_shutdown();
307 
308 	do {
309 		if ((pid = wait(NULL)) == -1 &&
310 		    errno != EINTR && errno != ECHILD)
311 			fatal("wait");
312 	} while (pid != -1 || (pid == -1 && errno == EINTR));
313 
314 	msgbuf_clear(&ibuf_ospfe->w);
315 	free(ibuf_ospfe);
316 	msgbuf_clear(&ibuf_rde->w);
317 	free(ibuf_rde);
318 	free(ospfd_conf);
319 
320 	log_info("terminating");
321 	exit(0);
322 }
323 
324 int
325 check_child(pid_t pid, const char *pname)
326 {
327 	int	status;
328 
329 	if (waitpid(pid, &status, WNOHANG) > 0) {
330 		if (WIFEXITED(status)) {
331 			log_warnx("lost child: %s exited", pname);
332 			return (1);
333 		}
334 		if (WIFSIGNALED(status)) {
335 			log_warnx("lost child: %s terminated; signal %d",
336 			    pname, WTERMSIG(status));
337 			return (1);
338 		}
339 	}
340 
341 	return (0);
342 }
343 
344 /* imsg handling */
345 /* ARGSUSED */
346 void
347 main_dispatch_ospfe(int fd, short event, void *bula)
348 {
349 	struct imsgbuf		*ibuf = bula;
350 	struct imsg		 imsg;
351 	struct demote_msg	 dmsg;
352 	ssize_t			 n;
353 	int			 shut = 0;
354 
355 	switch (event) {
356 	case EV_READ:
357 		if ((n = imsg_read(ibuf)) == -1)
358 			fatal("imsg_read error");
359 		if (n == 0)	/* connection closed */
360 			shut = 1;
361 		break;
362 	case EV_WRITE:
363 		if (msgbuf_write(&ibuf->w) == -1)
364 			fatal("msgbuf_write");
365 		imsg_event_add(ibuf);
366 		return;
367 	default:
368 		fatalx("unknown event");
369 	}
370 
371 	for (;;) {
372 		if ((n = imsg_get(ibuf, &imsg)) == -1)
373 			fatal("imsg_get");
374 
375 		if (n == 0)
376 			break;
377 
378 		switch (imsg.hdr.type) {
379 		case IMSG_CTL_RELOAD:
380 			if (ospf_reload() == -1)
381 				log_warnx("configuration reload failed");
382 			else
383 				log_debug("configuration reloaded");
384 			break;
385 		case IMSG_CTL_FIB_COUPLE:
386 			kr_fib_couple();
387 			break;
388 		case IMSG_CTL_FIB_DECOUPLE:
389 			kr_fib_decouple();
390 			break;
391 		case IMSG_CTL_KROUTE:
392 		case IMSG_CTL_KROUTE_ADDR:
393 			kr_show_route(&imsg);
394 			break;
395 		case IMSG_DEMOTE:
396 			if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(dmsg))
397 				fatalx("invalid size of OE request");
398 			memcpy(&dmsg, imsg.data, sizeof(dmsg));
399 			carp_demote_set(dmsg.demote_group, dmsg.level);
400 			break;
401 		default:
402 			log_debug("main_dispatch_ospfe: error handling imsg %d",
403 			    imsg.hdr.type);
404 			break;
405 		}
406 		imsg_free(&imsg);
407 	}
408 	if (!shut)
409 		imsg_event_add(ibuf);
410 	else {
411 		/* this pipe is dead, so remove the event handler */
412 		event_del(&ibuf->ev);
413 		event_loopexit(NULL);
414 	}
415 }
416 
417 /* ARGSUSED */
418 void
419 main_dispatch_rde(int fd, short event, void *bula)
420 {
421 	struct imsgbuf  *ibuf = bula;
422 	struct imsg	 imsg;
423 	ssize_t		 n;
424 	int		 shut = 0;
425 
426 	switch (event) {
427 	case EV_READ:
428 		if ((n = imsg_read(ibuf)) == -1)
429 			fatal("imsg_read error");
430 		if (n == 0)	/* connection closed */
431 			shut = 1;
432 		break;
433 	case EV_WRITE:
434 		if (msgbuf_write(&ibuf->w) == -1)
435 			fatal("msgbuf_write");
436 		imsg_event_add(ibuf);
437 		return;
438 	default:
439 		fatalx("unknown event");
440 	}
441 
442 	for (;;) {
443 		if ((n = imsg_get(ibuf, &imsg)) == -1)
444 			fatal("imsg_get");
445 
446 		if (n == 0)
447 			break;
448 
449 		switch (imsg.hdr.type) {
450 		case IMSG_KROUTE_CHANGE:
451 			if (kr_change(imsg.data))
452 				log_warn("main_dispatch_rde: error changing "
453 				    "route");
454 			break;
455 		case IMSG_KROUTE_DELETE:
456 			if (kr_delete(imsg.data))
457 				log_warn("main_dispatch_rde: error deleting "
458 				    "route");
459 			break;
460 		default:
461 			log_debug("main_dispatch_rde: error handling imsg %d",
462 			    imsg.hdr.type);
463 			break;
464 		}
465 		imsg_free(&imsg);
466 	}
467 	if (!shut)
468 		imsg_event_add(ibuf);
469 	else {
470 		/* this pipe is dead, so remove the event handler */
471 		event_del(&ibuf->ev);
472 		event_loopexit(NULL);
473 	}
474 }
475 
476 void
477 main_imsg_compose_ospfe(int type, pid_t pid, void *data, u_int16_t datalen)
478 {
479 	imsg_compose(ibuf_ospfe, type, 0, pid, data, datalen);
480 }
481 
482 void
483 main_imsg_compose_rde(int type, pid_t pid, void *data, u_int16_t datalen)
484 {
485 	imsg_compose(ibuf_rde, type, 0, pid, data, datalen);
486 }
487 
488 /* this needs to be added here so that ospfctl can be used without libevent */
489 void
490 imsg_event_add(struct imsgbuf *ibuf)
491 {
492 	ibuf->events = EV_READ;
493 	if (ibuf->w.queued)
494 		ibuf->events |= EV_WRITE;
495 
496 	event_del(&ibuf->ev);
497 	event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf);
498 	event_add(&ibuf->ev, NULL);
499 }
500 
501 int
502 ospf_redistribute(struct kroute *kr, u_int32_t *metric)
503 {
504 	struct redistribute	*r;
505 	struct in6_addr		 ina, inb;
506 
507 	/* only allow 0.0.0.0/0 via REDISTRIBUTE_DEFAULT */
508 	if (IN6_IS_ADDR_UNSPECIFIED(&kr->prefix) && kr->prefixlen == 0)
509 		return (0);
510 
511 	SIMPLEQ_FOREACH(r, &ospfd_conf->redist_list, entry) {
512 		switch (r->type & ~REDIST_NO) {
513 		case REDIST_LABEL:
514 			if (kr->rtlabel == r->label) {
515 				*metric = r->metric;
516 				return (r->type & REDIST_NO ? 0 : 1);
517 			}
518 			break;
519 		case REDIST_STATIC:
520 			/*
521 			 * Dynamic routes are not redistributable. Placed here
522 			 * so that link local addresses can be redistributed
523 			 * via a rtlabel.
524 			 */
525 			if (kr->flags & F_DYNAMIC)
526 				continue;
527 			if (kr->flags & F_STATIC) {
528 				*metric = r->metric;
529 				return (r->type & REDIST_NO ? 0 : 1);
530 			}
531 			break;
532 		case REDIST_CONNECTED:
533 			if (kr->flags & F_DYNAMIC)
534 				continue;
535 			if (kr->flags & F_CONNECTED) {
536 				*metric = r->metric;
537 				return (r->type & REDIST_NO ? 0 : 1);
538 			}
539 			break;
540 		case REDIST_ADDR:
541 			if (kr->flags & F_DYNAMIC)
542 				continue;
543 			inet6applymask(&ina, &kr->prefix, kr->prefixlen);
544 			inet6applymask(&inb, &r->addr, r->prefixlen);
545 			if (IN6_ARE_ADDR_EQUAL(&ina, &inb) &&
546 			    kr->prefixlen >= r->prefixlen) {
547 				*metric = r->metric;
548 				return (r->type & REDIST_NO ? 0 : 1);
549 			}
550 			break;
551 		}
552 	}
553 
554 	return (0);
555 }
556 
557 void
558 ospf_redistribute_default(int type)
559 {
560 	struct rroute	rr;
561 
562 	if (!(ospfd_conf->redistribute & REDISTRIBUTE_DEFAULT))
563 		return;
564 
565 	bzero(&rr, sizeof(rr));
566 	rr.metric = ospfd_conf->defaultmetric;
567 	main_imsg_compose_rde(type, 0, &rr, sizeof(struct rroute));
568 }
569 
570 int
571 ospf_reload(void)
572 {
573 	struct area		*area;
574 	struct ospfd_conf	*xconf;
575 
576 	if ((xconf = parse_config(conffile, ospfd_conf->opts)) == NULL)
577 		return (-1);
578 
579 	/* send config to childs */
580 	if (ospf_sendboth(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1)
581 		return (-1);
582 
583 	/* send areas, interfaces happen out of band */
584 	LIST_FOREACH(area, &xconf->area_list, entry) {
585 		if (ospf_sendboth(IMSG_RECONF_AREA, area, sizeof(*area)) == -1)
586 			return (-1);
587 	}
588 
589 	if (ospf_sendboth(IMSG_RECONF_END, NULL, 0) == -1)
590 		return (-1);
591 
592 	/* XXX send newly available interfaces to the childs */
593 
594 	merge_config(ospfd_conf, xconf);
595 	/* update redistribute lists */
596 	kr_reload();
597 	return (0);
598 }
599 
600 int
601 ospf_sendboth(enum imsg_type type, void *buf, u_int16_t len)
602 {
603 	if (imsg_compose(ibuf_ospfe, type, 0, 0, buf, len) == -1)
604 		return (-1);
605 	if (imsg_compose(ibuf_rde, type, 0, 0, buf, len) == -1)
606 		return (-1);
607 	return (0);
608 }
609 
610 void
611 merge_config(struct ospfd_conf *conf, struct ospfd_conf *xconf)
612 {
613 	struct area		*a, *xa, *na;
614 	struct iface		*iface;
615 	struct redistribute	*r;
616 
617 	/* change of rtr_id needs a restart */
618 	conf->flags = xconf->flags;
619 	conf->spf_delay = xconf->spf_delay;
620 	conf->spf_hold_time = xconf->spf_hold_time;
621 	conf->redistribute = xconf->redistribute;
622 
623 	if (ospfd_process == PROC_MAIN) {
624 		/* main process does neither use areas nor interfaces */
625 		while ((r = SIMPLEQ_FIRST(&conf->redist_list)) != NULL) {
626 			SIMPLEQ_REMOVE_HEAD(&conf->redist_list, entry);
627 			free(r);
628 		}
629 		while ((r = SIMPLEQ_FIRST(&xconf->redist_list)) != NULL) {
630 			SIMPLEQ_REMOVE_HEAD(&xconf->redist_list, entry);
631 			SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry);
632 		}
633 		goto done;
634 	}
635 
636 	/* merge areas and interfaces */
637 	for (a = LIST_FIRST(&conf->area_list); a != NULL; a = na) {
638 		na = LIST_NEXT(a, entry);
639 		/* find deleted areas */
640 		if ((xa = area_find(xconf, a->id)) == NULL) {
641 			if (ospfd_process == PROC_OSPF_ENGINE) {
642 				LIST_FOREACH(iface, &a->iface_list, entry)
643 					if_fsm(iface, IF_EVT_DOWN);
644 			}
645 			LIST_REMOVE(a, entry);
646 			area_del(a);
647 		}
648 	}
649 
650 	for (xa = LIST_FIRST(&xconf->area_list); xa != NULL; xa = na) {
651 		na = LIST_NEXT(xa, entry);
652 		if ((a = area_find(conf, xa->id)) == NULL) {
653 			LIST_REMOVE(xa, entry);
654 			LIST_INSERT_HEAD(&conf->area_list, xa, entry);
655 			if (ospfd_process == PROC_OSPF_ENGINE) {
656 				/* start interfaces */
657 				ospfe_demote_area(xa, 0);
658 				LIST_FOREACH(iface, &xa->iface_list, entry)
659 					if_start(conf, iface);
660 			}
661 			/* no need to merge interfaces */
662 			continue;
663 		}
664 		/*
665 		 * stub is not yet used but switching between stub and normal
666 		 * will be another painful job.
667 		 */
668 		a->stub = xa->stub;
669 		a->stub_default_cost = xa->stub_default_cost;
670 		if (ospfd_process == PROC_RDE_ENGINE)
671 			a->dirty = 1; /* force SPF tree recalculation */
672 
673 		/* merge interfaces */
674 		if (merge_interfaces(a, xa) &&
675 		    ospfd_process == PROC_OSPF_ENGINE)
676 			a->dirty = 1; /* force rtr LSA update */
677 	}
678 
679 	if (ospfd_process == PROC_OSPF_ENGINE) {
680 		LIST_FOREACH(a, &conf->area_list, entry) {
681 			LIST_FOREACH(iface, &a->iface_list, entry) {
682 				if (iface->state == IF_STA_NEW) {
683 					iface->state = IF_STA_DOWN;
684 					if_start(conf, iface);
685 				}
686 			}
687 			if (a->dirty) {
688 				a->dirty = 0;
689 				orig_rtr_lsa(LIST_FIRST(&a->iface_list));
690 			}
691 		}
692 	}
693 
694 done:
695 	while ((a = LIST_FIRST(&xconf->area_list)) != NULL) {
696 		LIST_REMOVE(a, entry);
697 		area_del(a);
698 	}
699 	free(xconf);
700 }
701 
702 int
703 merge_interfaces(struct area *a, struct area *xa)
704 {
705 	struct iface	*i, *xi, *ni;
706 	int		 dirty = 0;
707 
708 	/* problems:
709 	 * - new interfaces (easy)
710 	 * - deleted interfaces (needs to be done via fsm?)
711 	 * - changing passive (painful?)
712 	 */
713 	for (i = LIST_FIRST(&a->iface_list); i != NULL; i = ni) {
714 		ni = LIST_NEXT(i, entry);
715 		if (iface_lookup(xa, i) == NULL) {
716 			log_debug("merge_interfaces: proc %d area %s removing "
717 			    "interface %s", ospfd_process, inet_ntoa(a->id),
718 			    i->name);
719 			if (ospfd_process == PROC_OSPF_ENGINE)
720 				if_fsm(i, IF_EVT_DOWN);
721 			LIST_REMOVE(i, entry);
722 			if_del(i);
723 		}
724 	}
725 
726 	for (xi = LIST_FIRST(&xa->iface_list); xi != NULL; xi = ni) {
727 		ni = LIST_NEXT(xi, entry);
728 		if ((i = iface_lookup(a, xi)) == NULL) {
729 			/* new interface but delay initialisation */
730 			log_debug("merge_interfaces: proc %d area %s adding "
731 			    "interface %s", ospfd_process, inet_ntoa(a->id),
732 			    xi->name);
733 			LIST_REMOVE(xi, entry);
734 			LIST_INSERT_HEAD(&a->iface_list, xi, entry);
735 			if (ospfd_process == PROC_OSPF_ENGINE)
736 				xi->state = IF_STA_NEW;
737 			continue;
738 		}
739 		log_debug("merge_interfaces: proc %d area %s merging "
740 		    "interface %s", ospfd_process, inet_ntoa(a->id), i->name);
741 		i->addr = xi->addr;
742 		i->dst = xi->dst;
743 		i->abr_id = xi->abr_id;
744 		i->baudrate = xi->baudrate;
745 		i->dead_interval = xi->dead_interval;
746 		i->mtu = xi->mtu;
747 		i->transmit_delay = xi->transmit_delay;
748 		i->hello_interval = xi->hello_interval;
749 		i->rxmt_interval = xi->rxmt_interval;
750 		if (i->metric != xi->metric)
751 			dirty = 1;
752 		i->metric = xi->metric;
753 		i->priority = xi->priority;
754 		i->flags = xi->flags; /* needed? */
755 		i->type = xi->type; /* needed? */
756 		i->media_type = xi->media_type; /* needed? */
757 		i->linkstate = xi->linkstate; /* needed? */
758 
759 #if 0 /* XXX needs some kind of love */
760 		if (i->passive != xi->passive) {
761 			/* need to restart interface to cope with this change */
762 			if (ospfd_process == PROC_OSPF_ENGINE)
763 				if_fsm(i, IF_EVT_DOWN);
764 			i->passive = xi->passive;
765 			if (ospfd_process == PROC_OSPF_ENGINE)
766 				if_fsm(i, IF_EVT_UP);
767 		}
768 #endif
769 	}
770 	return (dirty);
771 }
772 
773 struct iface *
774 iface_lookup(struct area *area, struct iface *iface)
775 {
776 	struct iface	*i;
777 
778 	LIST_FOREACH(i, &area->iface_list, entry)
779 		if (i->ifindex == iface->ifindex)
780 			return (i);
781 	return (NULL);
782 }
783