xref: /openbsd-src/usr.sbin/eigrpd/eigrpd.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1 /*	$OpenBSD: eigrpd.c,v 1.36 2024/11/21 13:38:14 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2015 Renato Westphal <renato@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 
22 #include <sys/types.h>
23 #include <sys/wait.h>
24 #include <sys/sysctl.h>
25 
26 #include <err.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <pwd.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "eigrpd.h"
37 #include "eigrpe.h"
38 #include "rde.h"
39 #include "log.h"
40 
41 static void		 main_sig_handler(int, short, void *);
42 static __dead void	 usage(void);
43 static __dead void	 eigrpd_shutdown(void);
44 static pid_t		 start_child(enum eigrpd_process, char *, int, int, int,
45 			    char *);
46 static void		 main_dispatch_eigrpe(int, short, void *);
47 static void		 main_dispatch_rde(int, short, void *);
48 static int		 main_imsg_send_ipc_sockets(struct imsgbuf *,
49 			    struct imsgbuf *);
50 static int		 main_imsg_send_config(struct eigrpd_conf *);
51 static int		 eigrp_reload(void);
52 static int		 eigrp_sendboth(enum imsg_type, void *, uint16_t);
53 static void		 merge_instances(struct eigrpd_conf *, struct eigrp *,
54 			    struct eigrp *);
55 
56 struct eigrpd_conf	*eigrpd_conf;
57 
58 static char		*conffile;
59 static struct imsgev	*iev_eigrpe;
60 static struct imsgev	*iev_rde;
61 static pid_t		 eigrpe_pid;
62 static pid_t		 rde_pid;
63 
64 static void
65 main_sig_handler(int sig, short event, void *arg)
66 {
67 	/* signal handler rules don't apply, libevent decouples for us */
68 	switch (sig) {
69 	case SIGTERM:
70 	case SIGINT:
71 		eigrpd_shutdown();
72 		/* NOTREACHED */
73 	case SIGHUP:
74 		if (eigrp_reload() == -1)
75 			log_warnx("configuration reload failed");
76 		else
77 			log_debug("configuration reloaded");
78 		break;
79 	default:
80 		fatalx("unexpected signal");
81 		/* NOTREACHED */
82 	}
83 }
84 
85 static __dead void
86 usage(void)
87 {
88 	extern char *__progname;
89 
90 	fprintf(stderr, "usage: %s [-dnv] [-D macro=value]"
91 	    " [-f file] [-s socket]\n",
92 	    __progname);
93 	exit(1);
94 }
95 
96 struct eigrpd_global global;
97 
98 int
99 main(int argc, char *argv[])
100 {
101 	struct event		 ev_sigint, ev_sigterm, ev_sighup;
102 	char			*saved_argv0;
103 	int			 ch;
104 	int			 debug = 0, rflag = 0, eflag = 0;
105 	int			 ipforwarding;
106 	int			 mib[4];
107 	size_t			 len;
108 	char			*sockname;
109 	int			 pipe_parent2eigrpe[2];
110 	int			 pipe_parent2rde[2];
111 
112 	conffile = CONF_FILE;
113 	log_procname = "parent";
114 	sockname = EIGRPD_SOCKET;
115 
116 	log_init(1);	/* log to stderr until daemonized */
117 	log_verbose(1);
118 
119 	saved_argv0 = argv[0];
120 	if (saved_argv0 == NULL)
121 		saved_argv0 = "eigrpd";
122 
123 	while ((ch = getopt(argc, argv, "dD:f:ns:vRE")) != -1) {
124 		switch (ch) {
125 		case 'd':
126 			debug = 1;
127 			break;
128 		case 'D':
129 			if (cmdline_symset(optarg) < 0)
130 				log_warnx("could not parse macro definition %s",
131 				    optarg);
132 			break;
133 		case 'f':
134 			conffile = optarg;
135 			break;
136 		case 'n':
137 			global.cmd_opts |= EIGRPD_OPT_NOACTION;
138 			break;
139 		case 's':
140 			sockname = optarg;
141 			break;
142 		case 'v':
143 			if (global.cmd_opts & EIGRPD_OPT_VERBOSE)
144 				global.cmd_opts |= EIGRPD_OPT_VERBOSE2;
145 			global.cmd_opts |= EIGRPD_OPT_VERBOSE;
146 			break;
147 		case 'R':
148 			rflag = 1;
149 			break;
150 		case 'E':
151 			eflag = 1;
152 			break;
153 		default:
154 			usage();
155 			/* NOTREACHED */
156 		}
157 	}
158 
159 	argc -= optind;
160 	argv += optind;
161 	if (argc > 0 || (rflag && eflag))
162 		usage();
163 
164 	if (rflag)
165 		rde(debug, global.cmd_opts & EIGRPD_OPT_VERBOSE);
166 	else if (eflag)
167 		eigrpe(debug, global.cmd_opts & EIGRPD_OPT_VERBOSE, sockname);
168 
169 	mib[0] = CTL_NET;
170 	mib[1] = PF_INET;
171 	mib[2] = IPPROTO_IP;
172 	mib[3] = IPCTL_FORWARDING;
173 	len = sizeof(ipforwarding);
174 	if (sysctl(mib, 4, &ipforwarding, &len, NULL, 0) == -1)
175 		log_warn("sysctl");
176 
177 	if (ipforwarding != 1)
178 		log_warnx("WARNING: IP forwarding NOT enabled");
179 
180 	/* fetch interfaces early */
181 	kif_init();
182 
183 	/* parse config file */
184 	if ((eigrpd_conf = parse_config(conffile)) == NULL) {
185 		kif_clear();
186 		exit(1);
187 	}
188 
189 	if (global.cmd_opts & EIGRPD_OPT_NOACTION) {
190 		if (global.cmd_opts & EIGRPD_OPT_VERBOSE)
191 			print_config(eigrpd_conf);
192 		else
193 			fprintf(stderr, "configuration OK\n");
194 		kif_clear();
195 		exit(0);
196 	}
197 
198 	/* check for root privileges  */
199 	if (geteuid())
200 		errx(1, "need root privileges");
201 
202 	/* check for eigrpd user */
203 	if (getpwnam(EIGRPD_USER) == NULL)
204 		errx(1, "unknown user %s", EIGRPD_USER);
205 
206 	log_init(debug);
207 	log_verbose(global.cmd_opts & EIGRPD_OPT_VERBOSE);
208 
209 	if (!debug)
210 		daemon(1, 0);
211 
212 	log_info("startup");
213 
214 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
215 	    PF_UNSPEC, pipe_parent2eigrpe) == -1)
216 		fatal("socketpair");
217 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
218 	    PF_UNSPEC, pipe_parent2rde) == -1)
219 		fatal("socketpair");
220 
221 	/* start children */
222 	rde_pid = start_child(PROC_RDE_ENGINE, saved_argv0, pipe_parent2rde[1],
223 	    debug, global.cmd_opts & EIGRPD_OPT_VERBOSE, NULL);
224 	eigrpe_pid = start_child(PROC_EIGRP_ENGINE, saved_argv0,
225 	    pipe_parent2eigrpe[1], debug, global.cmd_opts & EIGRPD_OPT_VERBOSE,
226 	    sockname);
227 
228 	event_init();
229 
230 	/* setup signal handler */
231 	signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
232 	signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
233 	signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
234 	signal_add(&ev_sigint, NULL);
235 	signal_add(&ev_sigterm, NULL);
236 	signal_add(&ev_sighup, NULL);
237 	signal(SIGPIPE, SIG_IGN);
238 
239 	/* setup pipes to children */
240 	if ((iev_eigrpe = malloc(sizeof(struct imsgev))) == NULL ||
241 	    (iev_rde = malloc(sizeof(struct imsgev))) == NULL)
242 		fatal(NULL);
243 	if (imsgbuf_init(&iev_eigrpe->ibuf, pipe_parent2eigrpe[0]) == -1)
244 		fatal(NULL);
245 	imsgbuf_allow_fdpass(&iev_eigrpe->ibuf);
246 	iev_eigrpe->handler = main_dispatch_eigrpe;
247 	if (imsgbuf_init(&iev_rde->ibuf, pipe_parent2rde[0]) == -1)
248 		fatal(NULL);
249 	imsgbuf_allow_fdpass(&iev_rde->ibuf);
250 	iev_rde->handler = main_dispatch_rde;
251 
252 	/* setup event handler */
253 	iev_eigrpe->events = EV_READ;
254 	event_set(&iev_eigrpe->ev, iev_eigrpe->ibuf.fd, iev_eigrpe->events,
255 	    iev_eigrpe->handler, iev_eigrpe);
256 	event_add(&iev_eigrpe->ev, NULL);
257 
258 	iev_rde->events = EV_READ;
259 	event_set(&iev_rde->ev, iev_rde->ibuf.fd, iev_rde->events,
260 	    iev_rde->handler, iev_rde);
261 	event_add(&iev_rde->ev, NULL);
262 
263 	if (main_imsg_send_ipc_sockets(&iev_eigrpe->ibuf, &iev_rde->ibuf))
264 		fatal("could not establish imsg links");
265 	main_imsg_send_config(eigrpd_conf);
266 
267 	/* notify eigrpe about existing interfaces and addresses */
268 	kif_redistribute();
269 
270 	if (kr_init(!(eigrpd_conf->flags & EIGRPD_FLAG_NO_FIB_UPDATE),
271 	    eigrpd_conf->rdomain) == -1)
272 		fatalx("kr_init failed");
273 
274 	if (pledge("stdio rpath inet sendfd", NULL) == -1)
275 		fatal("pledge");
276 
277 	event_dispatch();
278 
279 	eigrpd_shutdown();
280 	/* NOTREACHED */
281 	return (0);
282 }
283 
284 static __dead void
285 eigrpd_shutdown(void)
286 {
287 	pid_t		 pid;
288 	int		 status;
289 
290 	/* close pipes */
291 	imsgbuf_clear(&iev_eigrpe->ibuf);
292 	close(iev_eigrpe->ibuf.fd);
293 	imsgbuf_clear(&iev_rde->ibuf);
294 	close(iev_rde->ibuf.fd);
295 
296 	kr_shutdown();
297 	config_clear(eigrpd_conf, PROC_MAIN);
298 
299 	log_debug("waiting for children to terminate");
300 	do {
301 		pid = wait(&status);
302 		if (pid == -1) {
303 			if (errno != EINTR && errno != ECHILD)
304 				fatal("wait");
305 		} else if (WIFSIGNALED(status))
306 			log_warnx("%s terminated; signal %d",
307 			    (pid == rde_pid) ? "route decision engine" :
308 			    "eigrp engine", WTERMSIG(status));
309 	} while (pid != -1 || (pid == -1 && errno == EINTR));
310 
311 	free(iev_eigrpe);
312 	free(iev_rde);
313 
314 	log_info("terminating");
315 	exit(0);
316 }
317 
318 static pid_t
319 start_child(enum eigrpd_process p, char *argv0, int fd, int debug, int verbose,
320     char *sockname)
321 {
322 	char	*argv[7];
323 	int	 argc = 0;
324 	pid_t	 pid;
325 
326 	switch (pid = fork()) {
327 	case -1:
328 		fatal("cannot fork");
329 	case 0:
330 		break;
331 	default:
332 		close(fd);
333 		return (pid);
334 	}
335 
336 	if (fd != 3) {
337 		if (dup2(fd, 3) == -1)
338 			fatal("cannot setup imsg fd");
339 	} else if (fcntl(fd, F_SETFD, 0) == -1)
340 		fatal("cannot setup imsg fd");
341 
342 	argv[argc++] = argv0;
343 	switch (p) {
344 	case PROC_MAIN:
345 		fatalx("Can not start main process");
346 	case PROC_RDE_ENGINE:
347 		argv[argc++] = "-R";
348 		break;
349 	case PROC_EIGRP_ENGINE:
350 		argv[argc++] = "-E";
351 		break;
352 	}
353 	if (debug)
354 		argv[argc++] = "-d";
355 	if (verbose)
356 		argv[argc++] = "-v";
357 	if (sockname) {
358 		argv[argc++] = "-s";
359 		argv[argc++] = sockname;
360 	}
361 	argv[argc++] = NULL;
362 
363 	execvp(argv0, argv);
364 	fatal("execvp");
365 }
366 
367 /* imsg handling */
368 static void
369 main_dispatch_eigrpe(int fd, short event, void *bula)
370 {
371 	struct imsgev		*iev = bula;
372 	struct imsgbuf		*ibuf;
373 	struct imsg		 imsg;
374 	ssize_t			 n;
375 	int			 shut = 0, verbose;
376 
377 	ibuf = &iev->ibuf;
378 
379 	if (event & EV_READ) {
380 		if ((n = imsgbuf_read(ibuf)) == -1)
381 			fatal("imsgbuf_read error");
382 		if (n == 0)	/* connection closed */
383 			shut = 1;
384 	}
385 	if (event & EV_WRITE) {
386 		if (imsgbuf_write(ibuf) == -1) {
387 			if (errno == EPIPE)	/* connection closed */
388 				shut = 1;
389 			else
390 				fatal("imsgbuf_write");
391 		}
392 	}
393 
394 	for (;;) {
395 		if ((n = imsg_get(ibuf, &imsg)) == -1)
396 			fatal("imsg_get");
397 
398 		if (n == 0)
399 			break;
400 
401 		switch (imsg.hdr.type) {
402 		case IMSG_CTL_RELOAD:
403 			if (eigrp_reload() == -1)
404 				log_warnx("configuration reload failed");
405 			else
406 				log_debug("configuration reloaded");
407 			break;
408 		case IMSG_CTL_FIB_COUPLE:
409 			kr_fib_couple();
410 			break;
411 		case IMSG_CTL_FIB_DECOUPLE:
412 			kr_fib_decouple();
413 			break;
414 		case IMSG_CTL_KROUTE:
415 			kr_show_route(&imsg);
416 			break;
417 		case IMSG_CTL_IFINFO:
418 			if (imsg.hdr.len == IMSG_HEADER_SIZE)
419 				kr_ifinfo(NULL, imsg.hdr.pid);
420 			else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ)
421 				kr_ifinfo(imsg.data, imsg.hdr.pid);
422 			else
423 				log_warnx("IFINFO request with wrong len");
424 			break;
425 		case IMSG_CTL_LOG_VERBOSE:
426 			/* already checked by eigrpe */
427 			memcpy(&verbose, imsg.data, sizeof(verbose));
428 			log_verbose(verbose);
429 			break;
430 		default:
431 			log_debug("%s: error handling imsg %d", __func__,
432 			    imsg.hdr.type);
433 			break;
434 		}
435 		imsg_free(&imsg);
436 	}
437 	if (!shut)
438 		imsg_event_add(iev);
439 	else {
440 		/* this pipe is dead, so remove the event handler */
441 		event_del(&iev->ev);
442 		event_loopexit(NULL);
443 	}
444 }
445 
446 static void
447 main_dispatch_rde(int fd, short event, void *bula)
448 {
449 	struct imsgev	*iev = bula;
450 	struct imsgbuf  *ibuf;
451 	struct imsg	 imsg;
452 	ssize_t		 n;
453 	int		 shut = 0;
454 
455 	ibuf = &iev->ibuf;
456 
457 	if (event & EV_READ) {
458 		if ((n = imsgbuf_read(ibuf)) == -1)
459 			fatal("imsgbuf_read error");
460 		if (n == 0)	/* connection closed */
461 			shut = 1;
462 	}
463 	if (event & EV_WRITE) {
464 		if (imsgbuf_write(ibuf) == -1) {
465 			if (errno == EPIPE)	/* connection closed */
466 				shut = 1;
467 			else
468 				fatal("imsgbuf_write");
469 		}
470 	}
471 
472 	for (;;) {
473 		if ((n = imsg_get(ibuf, &imsg)) == -1)
474 			fatal("imsg_get");
475 
476 		if (n == 0)
477 			break;
478 
479 		switch (imsg.hdr.type) {
480 		case IMSG_KROUTE_CHANGE:
481 			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
482 			    sizeof(struct kroute))
483 				fatalx("invalid size of IMSG_KROUTE_CHANGE");
484 			if (kr_change(imsg.data))
485 				log_warnx("%s: error changing route", __func__);
486 			break;
487 		case IMSG_KROUTE_DELETE:
488 			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
489 			    sizeof(struct kroute))
490 				fatalx("invalid size of IMSG_KROUTE_DELETE");
491 			if (kr_delete(imsg.data))
492 				log_warnx("%s: error deleting route", __func__);
493 			break;
494 
495 		default:
496 			log_debug("%s: error handling imsg %d", __func__,
497 			    imsg.hdr.type);
498 			break;
499 		}
500 		imsg_free(&imsg);
501 	}
502 	if (!shut)
503 		imsg_event_add(iev);
504 	else {
505 		/* this pipe is dead, so remove the event handler */
506 		event_del(&iev->ev);
507 		event_loopexit(NULL);
508 	}
509 }
510 
511 int
512 main_imsg_compose_eigrpe(int type, pid_t pid, void *data, uint16_t datalen)
513 {
514 	if (iev_eigrpe == NULL)
515 		return (-1);
516 	return (imsg_compose_event(iev_eigrpe, type, 0, pid, -1, data, datalen));
517 }
518 
519 int
520 main_imsg_compose_rde(int type, pid_t pid, void *data, uint16_t datalen)
521 {
522 	if (iev_rde == NULL)
523 		return (-1);
524 	return (imsg_compose_event(iev_rde, type, 0, pid, -1, data, datalen));
525 }
526 
527 void
528 imsg_event_add(struct imsgev *iev)
529 {
530 	iev->events = EV_READ;
531 	if (imsgbuf_queuelen(&iev->ibuf) > 0)
532 		iev->events |= EV_WRITE;
533 
534 	event_del(&iev->ev);
535 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
536 	event_add(&iev->ev, NULL);
537 }
538 
539 int
540 imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
541     pid_t pid, int fd, void *data, uint16_t datalen)
542 {
543 	int	ret;
544 
545 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
546 	    pid, fd, data, datalen)) != -1)
547 		imsg_event_add(iev);
548 	return (ret);
549 }
550 
551 static int
552 main_imsg_send_ipc_sockets(struct imsgbuf *eigrpe_buf, struct imsgbuf *rde_buf)
553 {
554 	int pipe_eigrpe2rde[2];
555 
556 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
557 	    PF_UNSPEC, pipe_eigrpe2rde) == -1)
558 		return (-1);
559 
560 	if (imsg_compose(eigrpe_buf, IMSG_SOCKET_IPC, 0, 0, pipe_eigrpe2rde[0],
561 	    NULL, 0) == -1)
562 		return (-1);
563 	if (imsg_compose(rde_buf, IMSG_SOCKET_IPC, 0, 0, pipe_eigrpe2rde[1],
564 	    NULL, 0) == -1)
565 		return (-1);
566 
567 	return (0);
568 }
569 
570 struct eigrp *
571 eigrp_find(struct eigrpd_conf *xconf, int af, uint16_t as)
572 {
573 	struct eigrp	*eigrp;
574 
575 	TAILQ_FOREACH(eigrp, &xconf->instances, entry)
576 		if (eigrp->af == af && eigrp->as == as)
577 			return (eigrp);
578 
579 	return (NULL);
580 }
581 
582 static int
583 main_imsg_send_config(struct eigrpd_conf *xconf)
584 {
585 	struct eigrp		*eigrp;
586 	struct eigrp_iface	*ei;
587 
588 	if (eigrp_sendboth(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1)
589 		return (-1);
590 
591 	TAILQ_FOREACH(eigrp, &xconf->instances, entry) {
592 		if (eigrp_sendboth(IMSG_RECONF_INSTANCE, eigrp,
593 		    sizeof(*eigrp)) == -1)
594 			return (-1);
595 
596 		TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) {
597 			if (eigrp_sendboth(IMSG_RECONF_IFACE, ei->iface,
598 			    sizeof(struct iface)) == -1)
599 				return (-1);
600 
601 			if (eigrp_sendboth(IMSG_RECONF_EIGRP_IFACE, ei,
602 			    sizeof(*ei)) == -1)
603 				return (-1);
604 		}
605 	}
606 
607 	if (eigrp_sendboth(IMSG_RECONF_END, NULL, 0) == -1)
608 		return (-1);
609 
610 	return (0);
611 }
612 
613 static int
614 eigrp_reload(void)
615 {
616 	struct eigrpd_conf	*xconf;
617 
618 	if ((xconf = parse_config(conffile)) == NULL)
619 		return (-1);
620 
621 	if (main_imsg_send_config(xconf) == -1)
622 		return (-1);
623 
624 	merge_config(eigrpd_conf, xconf, PROC_MAIN);
625 
626 	return (0);
627 }
628 
629 static int
630 eigrp_sendboth(enum imsg_type type, void *buf, uint16_t len)
631 {
632 	if (main_imsg_compose_eigrpe(type, 0, buf, len) == -1)
633 		return (-1);
634 	if (main_imsg_compose_rde(type, 0, buf, len) == -1)
635 		return (-1);
636 	return (0);
637 }
638 
639 void
640 merge_config(struct eigrpd_conf *conf, struct eigrpd_conf *xconf,
641     enum eigrpd_process proc)
642 {
643 	struct iface		*iface, *itmp, *xi;
644 	struct eigrp		*eigrp, *etmp, *xe;
645 
646 	conf->rtr_id = xconf->rtr_id;
647 	conf->flags = xconf->flags;
648 	conf->rdomain= xconf->rdomain;
649 	conf->fib_priority_internal = xconf->fib_priority_internal;
650 	conf->fib_priority_external = xconf->fib_priority_external;
651 	conf->fib_priority_summary = xconf->fib_priority_summary;
652 
653 	/* merge instances */
654 	TAILQ_FOREACH_SAFE(eigrp, &conf->instances, entry, etmp) {
655 		/* find deleted instances */
656 		if ((xe = eigrp_find(xconf, eigrp->af, eigrp->as)) == NULL) {
657 			TAILQ_REMOVE(&conf->instances, eigrp, entry);
658 
659 			switch (proc) {
660 			case PROC_RDE_ENGINE:
661 				rde_instance_del(eigrp);
662 				break;
663 			case PROC_EIGRP_ENGINE:
664 				eigrpe_instance_del(eigrp);
665 				break;
666 			case PROC_MAIN:
667 				free(eigrp);
668 				break;
669 			}
670 		}
671 	}
672 	TAILQ_FOREACH_SAFE(xe, &xconf->instances, entry, etmp) {
673 		/* find new instances */
674 		if ((eigrp = eigrp_find(conf, xe->af, xe->as)) == NULL) {
675 			TAILQ_REMOVE(&xconf->instances, xe, entry);
676 			TAILQ_INSERT_TAIL(&conf->instances, xe, entry);
677 
678 			switch (proc) {
679 			case PROC_RDE_ENGINE:
680 				rde_instance_init(xe);
681 				break;
682 			case PROC_EIGRP_ENGINE:
683 				eigrpe_instance_init(xe);
684 				break;
685 			case PROC_MAIN:
686 				break;
687 			}
688 			continue;
689 		}
690 
691 		/* update existing instances */
692 		merge_instances(conf, eigrp, xe);
693 	}
694 
695 	/* merge interfaces */
696 	TAILQ_FOREACH_SAFE(iface, &conf->iface_list, entry, itmp) {
697 		/* find deleted ifaces */
698 		if ((xi = if_lookup(xconf, iface->ifindex)) == NULL) {
699 			TAILQ_REMOVE(&conf->iface_list, iface, entry);
700 			free(iface);
701 		}
702 	}
703 	TAILQ_FOREACH_SAFE(xi, &xconf->iface_list, entry, itmp) {
704 		/* find new ifaces */
705 		if ((iface = if_lookup(conf, xi->ifindex)) == NULL) {
706 			TAILQ_REMOVE(&xconf->iface_list, xi, entry);
707 			TAILQ_INSERT_TAIL(&conf->iface_list, xi, entry);
708 			continue;
709 		}
710 
711 		/* TODO update existing ifaces */
712 	}
713 
714 	/* resend addresses to activate new interfaces */
715 	if (proc == PROC_MAIN)
716 		kif_redistribute();
717 
718 	free(xconf);
719 }
720 
721 static void
722 merge_instances(struct eigrpd_conf *xconf, struct eigrp *eigrp, struct eigrp *xe)
723 {
724 	/* TODO */
725 }
726 
727 struct eigrpd_conf *
728 config_new_empty(void)
729 {
730 	struct eigrpd_conf	*xconf;
731 
732 	xconf = calloc(1, sizeof(*xconf));
733 	if (xconf == NULL)
734 		fatal(NULL);
735 
736 	TAILQ_INIT(&xconf->instances);
737 	TAILQ_INIT(&xconf->iface_list);
738 
739 	return (xconf);
740 }
741 
742 void
743 config_clear(struct eigrpd_conf *conf, enum eigrpd_process proc)
744 {
745 	struct eigrpd_conf	*xconf;
746 
747 	/* merge current config with an empty config */
748 	xconf = config_new_empty();
749 	merge_config(conf, xconf, proc);
750 
751 	free(conf);
752 }
753