xref: /openbsd-src/usr.sbin/ospfd/kroute.c (revision 95d3cd23b2d0e6a67e49f429e41f58010e853126)
1*95d3cd23Santon /*	$OpenBSD: kroute.c,v 1.118 2025/01/01 13:44:52 anton Exp $ */
2204df0f8Sclaudio 
3204df0f8Sclaudio /*
4367f601bSnorby  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
5204df0f8Sclaudio  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6204df0f8Sclaudio  *
7204df0f8Sclaudio  * Permission to use, copy, modify, and distribute this software for any
8204df0f8Sclaudio  * purpose with or without fee is hereby granted, provided that the above
9204df0f8Sclaudio  * copyright notice and this permission notice appear in all copies.
10204df0f8Sclaudio  *
11204df0f8Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12204df0f8Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13204df0f8Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14204df0f8Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15204df0f8Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16204df0f8Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17204df0f8Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18204df0f8Sclaudio  */
19204df0f8Sclaudio 
20204df0f8Sclaudio #include <sys/types.h>
21204df0f8Sclaudio #include <sys/socket.h>
22204df0f8Sclaudio #include <sys/sysctl.h>
23204df0f8Sclaudio #include <sys/tree.h>
242dbdedf7Sclaudio #include <sys/uio.h>
25204df0f8Sclaudio #include <netinet/in.h>
26204df0f8Sclaudio #include <arpa/inet.h>
27204df0f8Sclaudio #include <net/if.h>
28204df0f8Sclaudio #include <net/if_dl.h>
29d7a91d7eSclaudio #include <net/if_types.h>
30204df0f8Sclaudio #include <net/route.h>
31204df0f8Sclaudio #include <err.h>
32204df0f8Sclaudio #include <errno.h>
33204df0f8Sclaudio #include <fcntl.h>
34204df0f8Sclaudio #include <stdio.h>
35204df0f8Sclaudio #include <stdlib.h>
36204df0f8Sclaudio #include <string.h>
37204df0f8Sclaudio #include <unistd.h>
38b9fc9a72Sderaadt #include <limits.h>
39204df0f8Sclaudio 
40204df0f8Sclaudio #include "ospfd.h"
41204df0f8Sclaudio #include "log.h"
42204df0f8Sclaudio 
43204df0f8Sclaudio struct {
44204df0f8Sclaudio 	u_int32_t		rtseq;
45204df0f8Sclaudio 	pid_t			pid;
46204df0f8Sclaudio 	int			fib_sync;
47119f0f1dSdlg 	int			fib_serial;
484c260f66Sremi 	u_int8_t		fib_prio;
49204df0f8Sclaudio 	int			fd;
50204df0f8Sclaudio 	struct event		ev;
51fddf39b9Ssthen 	struct event		reload;
52e4caa3d9Sclaudio 	u_int			rdomain;
53fddf39b9Ssthen #define KR_RELOAD_IDLE	0
54fddf39b9Ssthen #define KR_RELOAD_FETCH	1
55fddf39b9Ssthen #define KR_RELOAD_HOLD	2
56fddf39b9Ssthen 	int			reload_state;
57204df0f8Sclaudio } kr_state;
58204df0f8Sclaudio 
59204df0f8Sclaudio struct kroute_node {
60204df0f8Sclaudio 	RB_ENTRY(kroute_node)	 entry;
6129b0bbeeSclaudio 	struct kroute_node	*next;
62afcfb5e2Sclaudio 	struct kroute		 r;
63119f0f1dSdlg 	int			 serial;
64204df0f8Sclaudio };
65204df0f8Sclaudio 
66204df0f8Sclaudio struct kif_node {
67204df0f8Sclaudio 	RB_ENTRY(kif_node)	 entry;
6866dd3991Sclaudio 	TAILQ_HEAD(, kif_addr)	 addrs;
69204df0f8Sclaudio 	struct kif		 k;
70204df0f8Sclaudio };
71204df0f8Sclaudio 
7229b0bbeeSclaudio void	kr_redist_remove(struct kroute_node *, struct kroute_node *);
73afcfb5e2Sclaudio int	kr_redist_eval(struct kroute *, struct kroute *);
7429b0bbeeSclaudio void	kr_redistribute(struct kroute_node *);
75204df0f8Sclaudio int	kroute_compare(struct kroute_node *, struct kroute_node *);
76204df0f8Sclaudio int	kif_compare(struct kif_node *, struct kif_node *);
77d5218eb1Sclaudio int	kr_change_fib(struct kroute_node *, struct kroute *, int, int);
78d5218eb1Sclaudio int	kr_delete_fib(struct kroute_node *);
79204df0f8Sclaudio 
801795d796Sclaudio struct kroute_node	*kroute_find(in_addr_t, u_int8_t, u_int8_t);
8129b0bbeeSclaudio struct kroute_node	*kroute_matchgw(struct kroute_node *, struct in_addr);
82204df0f8Sclaudio int			 kroute_insert(struct kroute_node *);
83204df0f8Sclaudio int			 kroute_remove(struct kroute_node *);
84204df0f8Sclaudio void			 kroute_clear(void);
85204df0f8Sclaudio 
8673e34765Sclaudio struct kif_node		*kif_find(u_short);
8773e34765Sclaudio struct kif_node		*kif_insert(u_short);
88204df0f8Sclaudio int			 kif_remove(struct kif_node *);
8973e34765Sclaudio struct kif		*kif_update(u_short, int, struct if_data *,
9073e34765Sclaudio 			    struct sockaddr_dl *);
9173e34765Sclaudio int			 kif_validate(u_short);
92204df0f8Sclaudio 
93204df0f8Sclaudio struct kroute_node	*kroute_match(in_addr_t);
94204df0f8Sclaudio 
95204df0f8Sclaudio int		protect_lo(void);
96204df0f8Sclaudio u_int8_t	prefixlen_classful(in_addr_t);
97204df0f8Sclaudio void		get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
981c282279Sclaudio void		if_change(u_short, int, struct if_data *, struct sockaddr_dl *);
9973e34765Sclaudio void		if_newaddr(u_short, struct sockaddr_in *, struct sockaddr_in *,
10073e34765Sclaudio 		    struct sockaddr_in *);
101a60d5a8aSclaudio void		if_deladdr(u_short, struct sockaddr_in *, struct sockaddr_in *,
102a60d5a8aSclaudio 		    struct sockaddr_in *);
103204df0f8Sclaudio void		if_announce(void *);
104204df0f8Sclaudio 
105204df0f8Sclaudio int		send_rtmsg(int, int, struct kroute *);
106204df0f8Sclaudio int		dispatch_rtmsg(void);
107204df0f8Sclaudio int		fetchtable(void);
10873e34765Sclaudio int		fetchifs(u_short);
10958ef7452Sclaudio int		rtmsg_process(char *, size_t);
110fddf39b9Ssthen void		kr_fib_reload_timer(int, short, void *);
111fddf39b9Ssthen void		kr_fib_reload_arm_timer(int);
112204df0f8Sclaudio 
113358269bbSclaudio RB_HEAD(kroute_tree, kroute_node)	krt = RB_INITIALIZER(&krt);
114204df0f8Sclaudio RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare)
115204df0f8Sclaudio RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare)
116204df0f8Sclaudio 
117358269bbSclaudio RB_HEAD(kif_tree, kif_node)		kit = RB_INITIALIZER(&kit);
118204df0f8Sclaudio RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
119204df0f8Sclaudio RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
120204df0f8Sclaudio 
121204df0f8Sclaudio int
122e9fa2173Sclaudio kif_init(void)
123e9fa2173Sclaudio {
124e9fa2173Sclaudio 	if (fetchifs(0) == -1)
125e9fa2173Sclaudio 		return (-1);
126e9fa2173Sclaudio 
127e9fa2173Sclaudio 	return (0);
128e9fa2173Sclaudio }
129e9fa2173Sclaudio 
130e9fa2173Sclaudio int
1314c260f66Sremi kr_init(int fs, u_int rdomain, int redis_label_or_prefix, u_int8_t fib_prio)
132204df0f8Sclaudio {
1331dcb316cShenning 	int		opt = 0, rcvbuf, default_rcvbuf;
1341dcb316cShenning 	socklen_t	optlen;
1354c260f66Sremi 	int		filter_prio = fib_prio;
1365e36d456Sjmatthew 	int		filter_flags = RTF_LLINFO | RTF_BROADCAST;
137204df0f8Sclaudio 
138204df0f8Sclaudio 	kr_state.fib_sync = fs;
139e4caa3d9Sclaudio 	kr_state.rdomain = rdomain;
1404c260f66Sremi 	kr_state.fib_prio = fib_prio;
141204df0f8Sclaudio 
14258ef7452Sclaudio 	if ((kr_state.fd = socket(AF_ROUTE,
14358ef7452Sclaudio 	    SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, AF_INET)) == -1) {
144204df0f8Sclaudio 		log_warn("kr_init: socket");
145204df0f8Sclaudio 		return (-1);
146204df0f8Sclaudio 	}
147204df0f8Sclaudio 
148204df0f8Sclaudio 	/* not interested in my own messages */
149204df0f8Sclaudio 	if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK,
150204df0f8Sclaudio 	    &opt, sizeof(opt)) == -1)
151204df0f8Sclaudio 		log_warn("kr_init: setsockopt");	/* not fatal */
152204df0f8Sclaudio 
153ad4152efSbenno 	if (redis_label_or_prefix) {
154ad4152efSbenno 		filter_prio = 0;
155ad4152efSbenno 		log_info("%s: priority filter disabled", __func__);
156ad4152efSbenno 	} else
157ad4152efSbenno 		log_debug("%s: priority filter enabled", __func__);
158ad4152efSbenno 
159ad4152efSbenno 	if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_PRIOFILTER, &filter_prio,
160ad4152efSbenno 	    sizeof(filter_prio)) == -1) {
161ad4152efSbenno 		log_warn("%s: setsockopt AF_ROUTE ROUTE_PRIOFILTER", __func__);
162ad4152efSbenno 		/* not fatal */
163ad4152efSbenno 	}
1645e36d456Sjmatthew 	if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_FLAGFILTER, &filter_flags,
1655e36d456Sjmatthew 	    sizeof(filter_flags)) == -1) {
1665e36d456Sjmatthew 		log_warn("%s: setsockopt AF_ROUTE ROUTE_FLAGFILTER", __func__);
1675e36d456Sjmatthew 		/* not fatal */
1685e36d456Sjmatthew 	}
169ad4152efSbenno 
1701dcb316cShenning 	/* grow receive buffer, don't wanna miss messages */
1711dcb316cShenning 	optlen = sizeof(default_rcvbuf);
1721dcb316cShenning 	if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF,
1731dcb316cShenning 	    &default_rcvbuf, &optlen) == -1)
1741dcb316cShenning 		log_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF");
1751dcb316cShenning 	else
1761dcb316cShenning 		for (rcvbuf = MAX_RTSOCK_BUF;
1771dcb316cShenning 		    rcvbuf > default_rcvbuf &&
1781dcb316cShenning 		    setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF,
1791dcb316cShenning 		    &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS;
1801dcb316cShenning 		    rcvbuf /= 2)
1811dcb316cShenning 			;	/* nothing */
1821dcb316cShenning 
183204df0f8Sclaudio 	kr_state.pid = getpid();
184204df0f8Sclaudio 	kr_state.rtseq = 1;
185204df0f8Sclaudio 
186204df0f8Sclaudio 	if (fetchtable() == -1)
187204df0f8Sclaudio 		return (-1);
188204df0f8Sclaudio 
189204df0f8Sclaudio 	if (protect_lo() == -1)
190204df0f8Sclaudio 		return (-1);
191204df0f8Sclaudio 
192e9fa2173Sclaudio 	event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST,
193e9fa2173Sclaudio 	    kr_dispatch_msg, NULL);
194204df0f8Sclaudio 	event_add(&kr_state.ev, NULL);
195204df0f8Sclaudio 
196fddf39b9Ssthen 	kr_state.reload_state = KR_RELOAD_IDLE;
197fddf39b9Ssthen 	evtimer_set(&kr_state.reload, kr_fib_reload_timer, NULL);
198fddf39b9Ssthen 
199204df0f8Sclaudio 	return (0);
200204df0f8Sclaudio }
201204df0f8Sclaudio 
202204df0f8Sclaudio int
203d5218eb1Sclaudio kr_change_fib(struct kroute_node *kr, struct kroute *kroute, int krcount,
204d5218eb1Sclaudio     int action)
205d5218eb1Sclaudio {
206d5218eb1Sclaudio 	int			 i;
207d5218eb1Sclaudio 	struct kroute_node	*kn, *nkn;
208d5218eb1Sclaudio 
209d5218eb1Sclaudio 	if (action == RTM_ADD) {
210d5218eb1Sclaudio 		/*
211d5218eb1Sclaudio 		 * First remove all stale multipath routes.
212d5218eb1Sclaudio 		 * This step must be skipped when the action is RTM_CHANGE
213d5218eb1Sclaudio 		 * because it is already a single path route that will be
214d5218eb1Sclaudio 		 * changed.
215d5218eb1Sclaudio 		 */
216d5218eb1Sclaudio 		for (kn = kr; kn != NULL; kn = nkn) {
217d5218eb1Sclaudio 			for (i = 0; i < krcount; i++) {
218d5218eb1Sclaudio 				if (kn->r.nexthop.s_addr ==
219d5218eb1Sclaudio 				    kroute[i].nexthop.s_addr)
220d5218eb1Sclaudio 					break;
221d5218eb1Sclaudio 			}
222d5218eb1Sclaudio 			nkn = kn->next;
223e938c347Sgollo 			if (i == krcount) {
224d5218eb1Sclaudio 				/* stale route */
225d5218eb1Sclaudio 				if (kr_delete_fib(kn) == -1)
226d5218eb1Sclaudio 					log_warnx("kr_delete_fib failed");
2278cee13bcSclaudio 				/*
2288cee13bcSclaudio 				 * if head element was removed we need to adjust
2298cee13bcSclaudio 				 * the head
2308cee13bcSclaudio 				 */
2318cee13bcSclaudio 				if (kr == kn)
2328cee13bcSclaudio 					kr = nkn;
233d5218eb1Sclaudio 			}
234d5218eb1Sclaudio 		}
235e938c347Sgollo 	}
236d5218eb1Sclaudio 
237d5218eb1Sclaudio 	/*
238d5218eb1Sclaudio 	 * now add or change the route
239d5218eb1Sclaudio 	 */
240d5218eb1Sclaudio 	for (i = 0; i < krcount; i++) {
241d5218eb1Sclaudio 		/* nexthop within 127/8 -> ignore silently */
242d5218eb1Sclaudio 		if ((kroute[i].nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
243d5218eb1Sclaudio 		    htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
244d5218eb1Sclaudio 			continue;
245d5218eb1Sclaudio 
246d5218eb1Sclaudio 		if (action == RTM_ADD && kr) {
247d5218eb1Sclaudio 			for (kn = kr; kn != NULL; kn = kn->next) {
248d5218eb1Sclaudio 				if (kn->r.nexthop.s_addr ==
249d5218eb1Sclaudio 				    kroute[i].nexthop.s_addr)
250d5218eb1Sclaudio 					break;
251d5218eb1Sclaudio 			}
252d5218eb1Sclaudio 
253d5218eb1Sclaudio 			if (kn != NULL)
254d5218eb1Sclaudio 				/* nexthop already present, skip it */
255d5218eb1Sclaudio 				continue;
256d5218eb1Sclaudio 		} else
257d5218eb1Sclaudio 			/* modify first entry */
258d5218eb1Sclaudio 			kn = kr;
259d5218eb1Sclaudio 
260d5218eb1Sclaudio 		/* send update */
261d5218eb1Sclaudio 		if (send_rtmsg(kr_state.fd, action, &kroute[i]) == -1)
262d5218eb1Sclaudio 			return (-1);
263d5218eb1Sclaudio 
264d5218eb1Sclaudio 		/* create new entry unless we are changing the first entry */
265d5218eb1Sclaudio 		if (action == RTM_ADD)
266d5218eb1Sclaudio 			if ((kn = calloc(1, sizeof(*kn))) == NULL)
267d5218eb1Sclaudio 				fatal(NULL);
268d5218eb1Sclaudio 
269d5218eb1Sclaudio 		kn->r.prefix.s_addr = kroute[i].prefix.s_addr;
270d5218eb1Sclaudio 		kn->r.prefixlen = kroute[i].prefixlen;
271d5218eb1Sclaudio 		kn->r.nexthop.s_addr = kroute[i].nexthop.s_addr;
272d5218eb1Sclaudio 		kn->r.flags = kroute[i].flags | F_OSPFD_INSERTED;
2734c260f66Sremi 		kn->r.priority = kr_state.fib_prio;
274d5218eb1Sclaudio 		kn->r.ext_tag = kroute[i].ext_tag;
275d5218eb1Sclaudio 		rtlabel_unref(kn->r.rtlabel);	/* for RTM_CHANGE */
276d5218eb1Sclaudio 		kn->r.rtlabel = kroute[i].rtlabel;
277d5218eb1Sclaudio 
278d5218eb1Sclaudio 		if (action == RTM_ADD)
279d5218eb1Sclaudio 			if (kroute_insert(kn) == -1) {
280d5218eb1Sclaudio 				log_debug("kr_update_fib: cannot insert %s",
281d5218eb1Sclaudio 				    inet_ntoa(kn->r.nexthop));
282d5218eb1Sclaudio 				free(kn);
283d5218eb1Sclaudio 			}
284d5218eb1Sclaudio 		action = RTM_ADD;
285d5218eb1Sclaudio 	}
286d5218eb1Sclaudio 	return  (0);
287d5218eb1Sclaudio }
288d5218eb1Sclaudio 
289d5218eb1Sclaudio int
290d5218eb1Sclaudio kr_change(struct kroute *kroute, int krcount)
291204df0f8Sclaudio {
292204df0f8Sclaudio 	struct kroute_node	*kr;
293204df0f8Sclaudio 	int			 action = RTM_ADD;
294204df0f8Sclaudio 
295fcb4545bSreyk 	kroute->rtlabel = rtlabel_tag2id(kroute->ext_tag);
296fcb4545bSreyk 
2974c260f66Sremi 	kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen,
2984c260f66Sremi 	    kr_state.fib_prio);
2991795d796Sclaudio 	if (kr != NULL && kr->next == NULL && krcount == 1)
3001795d796Sclaudio 		/* single path OSPF route */
3011446e0e7Sclaudio 		action = RTM_CHANGE;
302204df0f8Sclaudio 
303d5218eb1Sclaudio 	return (kr_change_fib(kr, kroute, krcount, action));
304d5218eb1Sclaudio }
305d5218eb1Sclaudio 
306d5218eb1Sclaudio int
307d5218eb1Sclaudio kr_delete_fib(struct kroute_node *kr)
308d5218eb1Sclaudio {
3094c260f66Sremi 	if (kr->r.priority != kr_state.fib_prio)
3101795d796Sclaudio 		log_warn("kr_delete_fib: %s/%d has wrong priority %d",
3111795d796Sclaudio 		    inet_ntoa(kr->r.prefix), kr->r.prefixlen, kr->r.priority);
312204df0f8Sclaudio 
313d5218eb1Sclaudio 	if (send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r) == -1)
314d5218eb1Sclaudio 		return (-1);
315d5218eb1Sclaudio 
316d5218eb1Sclaudio 	if (kroute_remove(kr) == -1)
317d5218eb1Sclaudio 		return (-1);
318204df0f8Sclaudio 
319204df0f8Sclaudio 	return (0);
320204df0f8Sclaudio }
321204df0f8Sclaudio 
322204df0f8Sclaudio int
323204df0f8Sclaudio kr_delete(struct kroute *kroute)
324204df0f8Sclaudio {
325d5218eb1Sclaudio 	struct kroute_node	*kr, *nkr;
326204df0f8Sclaudio 
3271795d796Sclaudio 	if ((kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen,
3284c260f66Sremi 	    kr_state.fib_prio)) == NULL)
329204df0f8Sclaudio 		return (0);
330204df0f8Sclaudio 
331d5218eb1Sclaudio 	while (kr != NULL) {
332d5218eb1Sclaudio 		nkr = kr->next;
333d5218eb1Sclaudio 		if (kr_delete_fib(kr) == -1)
334d5218eb1Sclaudio 			return (-1);
335d5218eb1Sclaudio 		kr = nkr;
3361446e0e7Sclaudio 	}
337204df0f8Sclaudio 	return (0);
338204df0f8Sclaudio }
339204df0f8Sclaudio 
340204df0f8Sclaudio void
341204df0f8Sclaudio kr_shutdown(void)
342204df0f8Sclaudio {
343204df0f8Sclaudio 	kr_fib_decouple();
344204df0f8Sclaudio 	kroute_clear();
345204df0f8Sclaudio 	kif_clear();
346204df0f8Sclaudio }
347204df0f8Sclaudio 
348204df0f8Sclaudio void
349204df0f8Sclaudio kr_fib_couple(void)
350204df0f8Sclaudio {
351204df0f8Sclaudio 	struct kroute_node	*kr;
352d5218eb1Sclaudio 	struct kroute_node	*kn;
353204df0f8Sclaudio 
354204df0f8Sclaudio 	if (kr_state.fib_sync == 1)	/* already coupled */
355204df0f8Sclaudio 		return;
356204df0f8Sclaudio 
357204df0f8Sclaudio 	kr_state.fib_sync = 1;
358204df0f8Sclaudio 
359204df0f8Sclaudio 	RB_FOREACH(kr, kroute_tree, &krt)
3604c260f66Sremi 		if (kr->r.priority == kr_state.fib_prio)
3611795d796Sclaudio 			for (kn = kr; kn != NULL; kn = kn->next)
362d5218eb1Sclaudio 				send_rtmsg(kr_state.fd, RTM_ADD, &kn->r);
363204df0f8Sclaudio 
364204df0f8Sclaudio 	log_info("kernel routing table coupled");
365204df0f8Sclaudio }
366204df0f8Sclaudio 
367204df0f8Sclaudio void
368204df0f8Sclaudio kr_fib_decouple(void)
369204df0f8Sclaudio {
370204df0f8Sclaudio 	struct kroute_node	*kr;
371d5218eb1Sclaudio 	struct kroute_node	*kn;
372204df0f8Sclaudio 
373204df0f8Sclaudio 	if (kr_state.fib_sync == 0)	/* already decoupled */
374204df0f8Sclaudio 		return;
375204df0f8Sclaudio 
3761795d796Sclaudio 	RB_FOREACH(kr, kroute_tree, &krt)
3774c260f66Sremi 		if (kr->r.priority == kr_state.fib_prio)
3781795d796Sclaudio 			for (kn = kr; kn != NULL; kn = kn->next)
379d5218eb1Sclaudio 				send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r);
380204df0f8Sclaudio 
381204df0f8Sclaudio 	kr_state.fib_sync = 0;
382204df0f8Sclaudio 
383204df0f8Sclaudio 	log_info("kernel routing table decoupled");
384204df0f8Sclaudio }
385204df0f8Sclaudio 
386119f0f1dSdlg void
387fddf39b9Ssthen kr_fib_reload_timer(int fd, short event, void *bula)
388fddf39b9Ssthen {
389fddf39b9Ssthen 	if (kr_state.reload_state == KR_RELOAD_FETCH) {
390fddf39b9Ssthen 		kr_fib_reload();
391fddf39b9Ssthen 		kr_state.reload_state = KR_RELOAD_HOLD;
392fddf39b9Ssthen 		kr_fib_reload_arm_timer(KR_RELOAD_HOLD_TIMER);
393fddf39b9Ssthen 	} else {
394fddf39b9Ssthen 		kr_state.reload_state = KR_RELOAD_IDLE;
395fddf39b9Ssthen 	}
396fddf39b9Ssthen }
397fddf39b9Ssthen 
398fddf39b9Ssthen void
399fddf39b9Ssthen kr_fib_reload_arm_timer(int delay)
400fddf39b9Ssthen {
401fddf39b9Ssthen 	struct timeval		 tv;
402fddf39b9Ssthen 
403fddf39b9Ssthen 	timerclear(&tv);
404fddf39b9Ssthen 	tv.tv_sec = delay / 1000;
405fddf39b9Ssthen 	tv.tv_usec = (delay % 1000) * 1000;
406fddf39b9Ssthen 
407fddf39b9Ssthen 	if (evtimer_add(&kr_state.reload, &tv) == -1)
408fddf39b9Ssthen 		fatal("add_reload_timer");
409fddf39b9Ssthen }
410fddf39b9Ssthen 
411fddf39b9Ssthen void
41204fee684Stb kr_fib_reload(void)
413119f0f1dSdlg {
414119f0f1dSdlg 	struct kroute_node	*krn, *kr, *kn;
415119f0f1dSdlg 
416fa19e37cSdlg 	log_info("reloading interface list and routing table");
417fa19e37cSdlg 
418119f0f1dSdlg 	kr_state.fib_serial++;
419119f0f1dSdlg 
420d11a1a20Sdlg 	if (fetchifs(0) == -1 || fetchtable() == -1)
421119f0f1dSdlg 		return;
422119f0f1dSdlg 
423119f0f1dSdlg 	for (kr = RB_MIN(kroute_tree, &krt); kr != NULL; kr = krn) {
424119f0f1dSdlg 		krn = RB_NEXT(kroute_tree, &krt, kr);
425119f0f1dSdlg 
426119f0f1dSdlg 		do {
427119f0f1dSdlg 			kn = kr->next;
428119f0f1dSdlg 
429119f0f1dSdlg 			if (kr->serial != kr_state.fib_serial) {
4304c260f66Sremi 				if (kr->r.priority == kr_state.fib_prio) {
431119f0f1dSdlg 					kr->serial = kr_state.fib_serial;
432119f0f1dSdlg 					if (send_rtmsg(kr_state.fd,
433119f0f1dSdlg 					    RTM_ADD, &kr->r) != 0)
434119f0f1dSdlg 						break;
435119f0f1dSdlg 				} else
436119f0f1dSdlg 					kroute_remove(kr);
437119f0f1dSdlg 			}
438119f0f1dSdlg 
439119f0f1dSdlg 		} while ((kr = kn) != NULL);
440119f0f1dSdlg 	}
441119f0f1dSdlg }
442119f0f1dSdlg 
4434c260f66Sremi void
4444c260f66Sremi kr_fib_update_prio(u_int8_t fib_prio)
4454c260f66Sremi {
4464c260f66Sremi 	struct kroute_node      *kr;
4474c260f66Sremi 
4484c260f66Sremi 	RB_FOREACH(kr, kroute_tree, &krt)
4494c260f66Sremi 		if ((kr->r.flags & F_OSPFD_INSERTED))
4504c260f66Sremi 			kr->r.priority = fib_prio;
4514c260f66Sremi 
4524c260f66Sremi 	log_info("fib priority changed from %hhu to %hhu",
4534c260f66Sremi 	    kr_state.fib_prio, fib_prio);
4544c260f66Sremi 
4554c260f66Sremi 	kr_state.fib_prio = fib_prio;
4564c260f66Sremi  }
4574c260f66Sremi 
458204df0f8Sclaudio void
459204df0f8Sclaudio kr_dispatch_msg(int fd, short event, void *bula)
460204df0f8Sclaudio {
4611795d796Sclaudio 	/* XXX this is stupid */
462412a8077Sdlg 	if (dispatch_rtmsg() == -1)
463412a8077Sdlg 		event_loopexit(NULL);
464204df0f8Sclaudio }
465204df0f8Sclaudio 
466204df0f8Sclaudio void
467204df0f8Sclaudio kr_show_route(struct imsg *imsg)
468204df0f8Sclaudio {
469b6b3a59aSclaudio 	struct kroute_node	*kr;
4704ee6d016Spyr 	struct kroute_node	*kn;
471b6b3a59aSclaudio 	int			 flags;
472b6b3a59aSclaudio 	struct in_addr		 addr;
473b6b3a59aSclaudio 
474204df0f8Sclaudio 	switch (imsg->hdr.type) {
475b6b3a59aSclaudio 	case IMSG_CTL_KROUTE:
476b6b3a59aSclaudio 		if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) {
477b6b3a59aSclaudio 			log_warnx("kr_show_route: wrong imsg len");
478b6b3a59aSclaudio 			return;
479b6b3a59aSclaudio 		}
480b6b3a59aSclaudio 		memcpy(&flags, imsg->data, sizeof(flags));
481b6b3a59aSclaudio 		RB_FOREACH(kr, kroute_tree, &krt)
482b6b3a59aSclaudio 			if (!flags || kr->r.flags & flags) {
4834ee6d016Spyr 				kn = kr;
4844ee6d016Spyr 				do {
485b6b3a59aSclaudio 					main_imsg_compose_ospfe(IMSG_CTL_KROUTE,
4864ee6d016Spyr 					    imsg->hdr.pid,
4874ee6d016Spyr 					    &kn->r, sizeof(kn->r));
4884ee6d016Spyr 				} while ((kn = kn->next) != NULL);
489b6b3a59aSclaudio 			}
490b6b3a59aSclaudio 		break;
491b6b3a59aSclaudio 	case IMSG_CTL_KROUTE_ADDR:
492b6b3a59aSclaudio 		if (imsg->hdr.len != IMSG_HEADER_SIZE +
493b6b3a59aSclaudio 		    sizeof(struct in_addr)) {
494b6b3a59aSclaudio 			log_warnx("kr_show_route: wrong imsg len");
495b6b3a59aSclaudio 			return;
496b6b3a59aSclaudio 		}
497b6b3a59aSclaudio 		memcpy(&addr, imsg->data, sizeof(addr));
498b6b3a59aSclaudio 		kr = NULL;
499b6b3a59aSclaudio 		kr = kroute_match(addr.s_addr);
500b6b3a59aSclaudio 		if (kr != NULL)
501b6b3a59aSclaudio 			main_imsg_compose_ospfe(IMSG_CTL_KROUTE, imsg->hdr.pid,
502b6b3a59aSclaudio 			    &kr->r, sizeof(kr->r));
503b6b3a59aSclaudio 		break;
504204df0f8Sclaudio 	default:
505204df0f8Sclaudio 		log_debug("kr_show_route: error handling imsg");
506204df0f8Sclaudio 		break;
507204df0f8Sclaudio 	}
508204df0f8Sclaudio 
509204df0f8Sclaudio 	main_imsg_compose_ospfe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0);
510204df0f8Sclaudio }
511204df0f8Sclaudio 
512204df0f8Sclaudio void
51370955e50Sclaudio kr_ifinfo(char *ifname, pid_t pid)
514204df0f8Sclaudio {
515204df0f8Sclaudio 	struct kif_node	*kif;
516204df0f8Sclaudio 
517204df0f8Sclaudio 	RB_FOREACH(kif, kif_tree, &kit)
51870955e50Sclaudio 		if (ifname == NULL || !strcmp(ifname, kif->k.ifname)) {
51970955e50Sclaudio 			main_imsg_compose_ospfe(IMSG_CTL_IFINFO,
52070955e50Sclaudio 			    pid, &kif->k, sizeof(kif->k));
521204df0f8Sclaudio 		}
52270955e50Sclaudio 
52370955e50Sclaudio 	main_imsg_compose_ospfe(IMSG_CTL_END, pid, NULL, 0);
524204df0f8Sclaudio }
525204df0f8Sclaudio 
526e2993955Sclaudio void
52729b0bbeeSclaudio kr_redist_remove(struct kroute_node *kh, struct kroute_node *kn)
528e2993955Sclaudio {
529b1c8b220Sjca 	struct kroute	*kr;
53044cb7d8eSclaudio 
53144cb7d8eSclaudio 	/* was the route redistributed? */
53229b0bbeeSclaudio 	if ((kn->r.flags & F_REDISTRIBUTED) == 0)
53329b0bbeeSclaudio 		return;
53429b0bbeeSclaudio 
53529b0bbeeSclaudio 	/* remove redistributed flag */
53629b0bbeeSclaudio 	kn->r.flags &= ~F_REDISTRIBUTED;
537b1c8b220Sjca 	kr = &kn->r;
53829b0bbeeSclaudio 
53929b0bbeeSclaudio 	/* probably inform the RDE (check if no other path is redistributed) */
54029b0bbeeSclaudio 	for (kn = kh; kn; kn = kn->next)
54129b0bbeeSclaudio 		if (kn->r.flags & F_REDISTRIBUTED)
54229b0bbeeSclaudio 			break;
54329b0bbeeSclaudio 
54429b0bbeeSclaudio 	if (kn == NULL)
545b1c8b220Sjca 		main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, kr,
546afcfb5e2Sclaudio 		    sizeof(struct kroute));
54744cb7d8eSclaudio }
54829b0bbeeSclaudio 
54929b0bbeeSclaudio int
550b1c8b220Sjca kr_redist_eval(struct kroute *kr, struct kroute *new_kr)
55129b0bbeeSclaudio {
55229b0bbeeSclaudio 	u_int32_t	 a, metric = 0;
55344cb7d8eSclaudio 
55444cb7d8eSclaudio 	/* Only non-ospfd routes are considered for redistribution. */
5551446e0e7Sclaudio 	if (!(kr->flags & F_KERNEL))
556bbb232d2Sclaudio 		goto dont_redistribute;
55744cb7d8eSclaudio 
55840df8bcdSclaudio 	/* Dynamic routes are not redistributable. */
55940df8bcdSclaudio 	if (kr->flags & F_DYNAMIC)
560bbb232d2Sclaudio 		goto dont_redistribute;
56140df8bcdSclaudio 
56244cb7d8eSclaudio 	/* interface is not up and running so don't announce */
56344cb7d8eSclaudio 	if (kr->flags & F_DOWN)
564bbb232d2Sclaudio 		goto dont_redistribute;
56544cb7d8eSclaudio 
566e2993955Sclaudio 	/*
567ef8f8f93Sclaudio 	 * We consider the loopback net and multicast addresses
568e2993955Sclaudio 	 * as not redistributable.
569e2993955Sclaudio 	 */
570e2993955Sclaudio 	a = ntohl(kr->prefix.s_addr);
571ef8f8f93Sclaudio 	if (IN_MULTICAST(a) || (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
572bbb232d2Sclaudio 		goto dont_redistribute;
573e2993955Sclaudio 	/*
574d9eec83cSclaudio 	 * Consider networks with nexthop loopback as not redistributable
575d9eec83cSclaudio 	 * unless it is a reject or blackhole route.
576e2993955Sclaudio 	 */
577d9eec83cSclaudio 	if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK) &&
578d9eec83cSclaudio 	    !(kr->flags & (F_BLACKHOLE|F_REJECT)))
579bbb232d2Sclaudio 		goto dont_redistribute;
580e2993955Sclaudio 
581cc63418dSsthen 	/* Should we redistribute this route? */
582f373ed5aSclaudio 	if (!ospf_redistribute(kr, &metric))
583bbb232d2Sclaudio 		goto dont_redistribute;
58444cb7d8eSclaudio 
58529b0bbeeSclaudio 	/* prefix should be redistributed */
58644cb7d8eSclaudio 	kr->flags |= F_REDISTRIBUTED;
58729b0bbeeSclaudio 	/*
58832da7e04Sclaudio 	 * only one of all multipath routes can be redistributed so
58929b0bbeeSclaudio 	 * redistribute the best one.
59029b0bbeeSclaudio 	 */
591b1c8b220Sjca 	if (new_kr->metric > metric) {
592b1c8b220Sjca 		*new_kr = *kr;
593b1c8b220Sjca 		new_kr->metric = metric;
59429b0bbeeSclaudio 	}
595afcfb5e2Sclaudio 
59629b0bbeeSclaudio 	return (1);
597f373ed5aSclaudio 
59829b0bbeeSclaudio dont_redistribute:
59929b0bbeeSclaudio 	/* was the route redistributed? */
60029b0bbeeSclaudio 	if ((kr->flags & F_REDISTRIBUTED) == 0)
60129b0bbeeSclaudio 		return (0);
60229b0bbeeSclaudio 
60329b0bbeeSclaudio 	kr->flags &= ~F_REDISTRIBUTED;
60429b0bbeeSclaudio 	return (1);
60529b0bbeeSclaudio }
60629b0bbeeSclaudio 
60729b0bbeeSclaudio void
60829b0bbeeSclaudio kr_redistribute(struct kroute_node *kh)
60929b0bbeeSclaudio {
61029b0bbeeSclaudio 	struct kroute_node	*kn;
611b1c8b220Sjca 	struct kroute		 kr;
61229b0bbeeSclaudio 	int			 redistribute = 0;
61329b0bbeeSclaudio 
6141795d796Sclaudio 	/* only the highest prio route can be redistributed */
6151795d796Sclaudio 	if (kroute_find(kh->r.prefix.s_addr, kh->r.prefixlen, RTP_ANY) != kh)
6161795d796Sclaudio 		return;
6171795d796Sclaudio 
618b1c8b220Sjca 	bzero(&kr, sizeof(kr));
619b1c8b220Sjca 	kr.metric = UINT_MAX;
62029b0bbeeSclaudio 	for (kn = kh; kn; kn = kn->next)
621b1c8b220Sjca 		if (kr_redist_eval(&kn->r, &kr))
62229b0bbeeSclaudio 			redistribute = 1;
62329b0bbeeSclaudio 
62429b0bbeeSclaudio 	if (!redistribute)
62529b0bbeeSclaudio 		return;
62629b0bbeeSclaudio 
627b1c8b220Sjca 	if (kr.flags & F_REDISTRIBUTED) {
628b1c8b220Sjca 		main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, &kr,
629afcfb5e2Sclaudio 		    sizeof(struct kroute));
63029b0bbeeSclaudio 	} else {
631b1c8b220Sjca 		kr = kh->r;
632b1c8b220Sjca 		main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &kr,
633afcfb5e2Sclaudio 		    sizeof(struct kroute));
63429b0bbeeSclaudio 	}
635e2993955Sclaudio }
636e2993955Sclaudio 
637afefb162Sclaudio void
638ad4152efSbenno kr_reload(int redis_label_or_prefix)
639afefb162Sclaudio {
64029b0bbeeSclaudio 	struct kroute_node	*kr, *kn;
641afefb162Sclaudio 	u_int32_t		 dummy;
642afefb162Sclaudio 	int			 r;
6434c260f66Sremi 	int			 filter_prio = kr_state.fib_prio;
644afefb162Sclaudio 
645ad4152efSbenno 	/* update the priority filter */
646ad4152efSbenno 	if (redis_label_or_prefix) {
647ad4152efSbenno 		filter_prio = 0;
648ad4152efSbenno 		log_info("%s: priority filter disabled", __func__);
649ad4152efSbenno 	} else
650ad4152efSbenno 		log_debug("%s: priority filter enabled", __func__);
651ad4152efSbenno 
652ad4152efSbenno 	if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_PRIOFILTER, &filter_prio,
653ad4152efSbenno 	    sizeof(filter_prio)) == -1) {
654ad4152efSbenno 		log_warn("%s: setsockopt AF_ROUTE ROUTE_PRIOFILTER", __func__);
655ad4152efSbenno 		/* not fatal */
656ad4152efSbenno 	}
657ad4152efSbenno 
658ad4152efSbenno 	/* update redistribute lists */
659afefb162Sclaudio 	RB_FOREACH(kr, kroute_tree, &krt) {
66029b0bbeeSclaudio 		for (kn = kr; kn; kn = kn->next) {
66129b0bbeeSclaudio 			r = ospf_redistribute(&kn->r, &dummy);
66229b0bbeeSclaudio 			/*
66329b0bbeeSclaudio 			 * if it is redistributed, redistribute again metric
66429b0bbeeSclaudio 			 * may have changed.
66529b0bbeeSclaudio 			 */
66629b0bbeeSclaudio 			if ((kn->r.flags & F_REDISTRIBUTED && !r) || r)
66729b0bbeeSclaudio 				break;
66829b0bbeeSclaudio 		}
66929b0bbeeSclaudio 		if (kn) {
67029b0bbeeSclaudio 			/*
67129b0bbeeSclaudio 			 * kr_redistribute copes with removes and RDE with
67229b0bbeeSclaudio 			 * duplicates
67329b0bbeeSclaudio 			 */
67429b0bbeeSclaudio 			kr_redistribute(kr);
675afefb162Sclaudio 		}
676afefb162Sclaudio 	}
677afefb162Sclaudio }
678afefb162Sclaudio 
679204df0f8Sclaudio /* rb-tree compare */
680204df0f8Sclaudio int
681204df0f8Sclaudio kroute_compare(struct kroute_node *a, struct kroute_node *b)
682204df0f8Sclaudio {
683204df0f8Sclaudio 	if (ntohl(a->r.prefix.s_addr) < ntohl(b->r.prefix.s_addr))
684204df0f8Sclaudio 		return (-1);
685204df0f8Sclaudio 	if (ntohl(a->r.prefix.s_addr) > ntohl(b->r.prefix.s_addr))
686204df0f8Sclaudio 		return (1);
687204df0f8Sclaudio 	if (a->r.prefixlen < b->r.prefixlen)
688204df0f8Sclaudio 		return (-1);
689204df0f8Sclaudio 	if (a->r.prefixlen > b->r.prefixlen)
690204df0f8Sclaudio 		return (1);
6911795d796Sclaudio 
6921795d796Sclaudio 	/* if the priority is RTP_ANY finish on the first address hit */
6931795d796Sclaudio 	if (a->r.priority == RTP_ANY || b->r.priority == RTP_ANY)
6941795d796Sclaudio 		return (0);
6951795d796Sclaudio 	if (a->r.priority < b->r.priority)
6961795d796Sclaudio 		return (-1);
6971795d796Sclaudio 	if (a->r.priority > b->r.priority)
6981795d796Sclaudio 		return (1);
699204df0f8Sclaudio 	return (0);
700204df0f8Sclaudio }
701204df0f8Sclaudio 
702204df0f8Sclaudio int
703204df0f8Sclaudio kif_compare(struct kif_node *a, struct kif_node *b)
704204df0f8Sclaudio {
705204df0f8Sclaudio 	return (b->k.ifindex - a->k.ifindex);
706204df0f8Sclaudio }
707204df0f8Sclaudio 
708204df0f8Sclaudio /* tree management */
709204df0f8Sclaudio struct kroute_node *
7101795d796Sclaudio kroute_find(in_addr_t prefix, u_int8_t prefixlen, u_int8_t prio)
711204df0f8Sclaudio {
712204df0f8Sclaudio 	struct kroute_node	s;
7131795d796Sclaudio 	struct kroute_node	*kn, *tmp;
714204df0f8Sclaudio 
715204df0f8Sclaudio 	s.r.prefix.s_addr = prefix;
716204df0f8Sclaudio 	s.r.prefixlen = prefixlen;
7171795d796Sclaudio 	s.r.priority = prio;
718204df0f8Sclaudio 
7191795d796Sclaudio 	kn = RB_FIND(kroute_tree, &krt, &s);
7201795d796Sclaudio 	if (kn && prio == RTP_ANY) {
7211795d796Sclaudio 		tmp = RB_PREV(kroute_tree, &krt, kn);
7221795d796Sclaudio 		while (tmp) {
7231795d796Sclaudio 			if (kroute_compare(&s, tmp) == 0)
7241795d796Sclaudio 				kn = tmp;
7251795d796Sclaudio 			else
7261795d796Sclaudio 				break;
7271795d796Sclaudio 			tmp = RB_PREV(kroute_tree, &krt, kn);
7281795d796Sclaudio 		}
7291795d796Sclaudio 	}
7301795d796Sclaudio 	return (kn);
731204df0f8Sclaudio }
732204df0f8Sclaudio 
73329b0bbeeSclaudio struct kroute_node *
73429b0bbeeSclaudio kroute_matchgw(struct kroute_node *kr, struct in_addr nh)
73529b0bbeeSclaudio {
73629b0bbeeSclaudio 	in_addr_t	nexthop;
73729b0bbeeSclaudio 
73829b0bbeeSclaudio 	nexthop = nh.s_addr;
73929b0bbeeSclaudio 
74029b0bbeeSclaudio 	while (kr) {
74129b0bbeeSclaudio 		if (kr->r.nexthop.s_addr == nexthop)
74229b0bbeeSclaudio 			return (kr);
74329b0bbeeSclaudio 		kr = kr->next;
74429b0bbeeSclaudio 	}
74529b0bbeeSclaudio 
74629b0bbeeSclaudio 	return (NULL);
74729b0bbeeSclaudio }
74829b0bbeeSclaudio 
749204df0f8Sclaudio int
750204df0f8Sclaudio kroute_insert(struct kroute_node *kr)
751204df0f8Sclaudio {
7529ae468ceSclaudio 	struct kroute_node	*krm, *krh;
75329b0bbeeSclaudio 
754119f0f1dSdlg 	kr->serial = kr_state.fib_serial;
755119f0f1dSdlg 
7569ae468ceSclaudio 	if ((krh = RB_INSERT(kroute_tree, &krt, kr)) != NULL) {
75729b0bbeeSclaudio 		/*
75832da7e04Sclaudio 		 * Multipath route, add at end of list.
75929b0bbeeSclaudio 		 */
7609ae468ceSclaudio 		krm = krh;
76129b0bbeeSclaudio 		while (krm->next != NULL)
76229b0bbeeSclaudio 			krm = krm->next;
76329b0bbeeSclaudio 		krm->next = kr;
76429b0bbeeSclaudio 		kr->next = NULL; /* to be sure */
76529b0bbeeSclaudio 	} else
7669ae468ceSclaudio 		krh = kr;
767204df0f8Sclaudio 
7681446e0e7Sclaudio 	if (!(kr->r.flags & F_KERNEL)) {
76944cb7d8eSclaudio 		/* don't validate or redistribute ospf route */
77044cb7d8eSclaudio 		kr->r.flags &= ~F_DOWN;
77144cb7d8eSclaudio 		return (0);
77244cb7d8eSclaudio 	}
77344cb7d8eSclaudio 
77444cb7d8eSclaudio 	if (kif_validate(kr->r.ifindex))
77544cb7d8eSclaudio 		kr->r.flags &= ~F_DOWN;
77644cb7d8eSclaudio 	else
77744cb7d8eSclaudio 		kr->r.flags |= F_DOWN;
77844cb7d8eSclaudio 
7799ae468ceSclaudio 	kr_redistribute(krh);
780204df0f8Sclaudio 	return (0);
781204df0f8Sclaudio }
782204df0f8Sclaudio 
783204df0f8Sclaudio int
784204df0f8Sclaudio kroute_remove(struct kroute_node *kr)
785204df0f8Sclaudio {
78629b0bbeeSclaudio 	struct kroute_node	*krm;
78729b0bbeeSclaudio 
78829b0bbeeSclaudio 	if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) {
78929b0bbeeSclaudio 		log_warnx("kroute_remove failed to find %s/%u",
79029b0bbeeSclaudio 		    inet_ntoa(kr->r.prefix), kr->r.prefixlen);
79129b0bbeeSclaudio 		return (-1);
79229b0bbeeSclaudio 	}
79329b0bbeeSclaudio 
79429b0bbeeSclaudio 	if (krm == kr) {
79529b0bbeeSclaudio 		/* head element */
796204df0f8Sclaudio 		if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) {
797204df0f8Sclaudio 			log_warnx("kroute_remove failed for %s/%u",
798204df0f8Sclaudio 			    inet_ntoa(kr->r.prefix), kr->r.prefixlen);
799204df0f8Sclaudio 			return (-1);
800204df0f8Sclaudio 		}
80129b0bbeeSclaudio 		if (kr->next != NULL) {
80229b0bbeeSclaudio 			if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) {
80329b0bbeeSclaudio 				log_warnx("kroute_remove failed to add %s/%u",
80429b0bbeeSclaudio 				    inet_ntoa(kr->r.prefix), kr->r.prefixlen);
80529b0bbeeSclaudio 				return (-1);
80629b0bbeeSclaudio 			}
80729b0bbeeSclaudio 		}
80829b0bbeeSclaudio 	} else {
80929b0bbeeSclaudio 		/* somewhere in the list */
81029b0bbeeSclaudio 		while (krm->next != kr && krm->next != NULL)
81129b0bbeeSclaudio 			krm = krm->next;
81229b0bbeeSclaudio 		if (krm->next == NULL) {
81329b0bbeeSclaudio 			log_warnx("kroute_remove multipath list corrupted "
81429b0bbeeSclaudio 			    "for %s/%u", inet_ntoa(kr->r.prefix),
81529b0bbeeSclaudio 			    kr->r.prefixlen);
81629b0bbeeSclaudio 			return (-1);
81729b0bbeeSclaudio 		}
81829b0bbeeSclaudio 		krm->next = kr->next;
81929b0bbeeSclaudio 	}
820204df0f8Sclaudio 
82129b0bbeeSclaudio 	kr_redist_remove(krm, kr);
822bbb232d2Sclaudio 	rtlabel_unref(kr->r.rtlabel);
823e2993955Sclaudio 
824204df0f8Sclaudio 	free(kr);
825204df0f8Sclaudio 	return (0);
826204df0f8Sclaudio }
827204df0f8Sclaudio 
828204df0f8Sclaudio void
829204df0f8Sclaudio kroute_clear(void)
830204df0f8Sclaudio {
831204df0f8Sclaudio 	struct kroute_node	*kr;
832204df0f8Sclaudio 
833204df0f8Sclaudio 	while ((kr = RB_MIN(kroute_tree, &krt)) != NULL)
834204df0f8Sclaudio 		kroute_remove(kr);
835204df0f8Sclaudio }
836204df0f8Sclaudio 
837204df0f8Sclaudio struct kif_node *
83873e34765Sclaudio kif_find(u_short ifindex)
839204df0f8Sclaudio {
840204df0f8Sclaudio 	struct kif_node	s;
841204df0f8Sclaudio 
842204df0f8Sclaudio 	bzero(&s, sizeof(s));
843204df0f8Sclaudio 	s.k.ifindex = ifindex;
844204df0f8Sclaudio 
845204df0f8Sclaudio 	return (RB_FIND(kif_tree, &kit, &s));
846204df0f8Sclaudio }
847204df0f8Sclaudio 
848e9fa2173Sclaudio struct kif *
84966dd3991Sclaudio kif_findname(char *ifname, struct in_addr addr, struct kif_addr **kap)
850e9fa2173Sclaudio {
851e9fa2173Sclaudio 	struct kif_node	*kif;
85266dd3991Sclaudio 	struct kif_addr	*ka;
853e9fa2173Sclaudio 
854e9fa2173Sclaudio 	RB_FOREACH(kif, kif_tree, &kit)
85566dd3991Sclaudio 		if (!strcmp(ifname, kif->k.ifname)) {
85666dd3991Sclaudio 			ka = TAILQ_FIRST(&kif->addrs);
85766dd3991Sclaudio 			if (addr.s_addr != 0) {
85866dd3991Sclaudio 				TAILQ_FOREACH(ka, &kif->addrs, entry) {
85966dd3991Sclaudio 					if (addr.s_addr == ka->addr.s_addr)
86066dd3991Sclaudio 						break;
86166dd3991Sclaudio 				}
86266dd3991Sclaudio 			}
86366dd3991Sclaudio 			if (kap != NULL)
86466dd3991Sclaudio 				*kap = ka;
865e9fa2173Sclaudio 			return (&kif->k);
86666dd3991Sclaudio 		}
867e9fa2173Sclaudio 
868e9fa2173Sclaudio 	return (NULL);
869e9fa2173Sclaudio }
870e9fa2173Sclaudio 
87173e34765Sclaudio struct kif_node *
87273e34765Sclaudio kif_insert(u_short ifindex)
873204df0f8Sclaudio {
87473e34765Sclaudio 	struct kif_node	*kif;
875204df0f8Sclaudio 
87673e34765Sclaudio 	if ((kif = calloc(1, sizeof(struct kif_node))) == NULL)
87773e34765Sclaudio 		return (NULL);
87873e34765Sclaudio 
87973e34765Sclaudio 	kif->k.ifindex = ifindex;
88073e34765Sclaudio 	TAILQ_INIT(&kif->addrs);
88173e34765Sclaudio 
88273e34765Sclaudio 	if (RB_INSERT(kif_tree, &kit, kif) != NULL)
88373e34765Sclaudio 		fatalx("kif_insert: RB_INSERT");
88473e34765Sclaudio 
88573e34765Sclaudio 	return (kif);
886204df0f8Sclaudio }
887204df0f8Sclaudio 
888204df0f8Sclaudio int
889204df0f8Sclaudio kif_remove(struct kif_node *kif)
890204df0f8Sclaudio {
89166dd3991Sclaudio 	struct kif_addr	*ka;
89266dd3991Sclaudio 
893204df0f8Sclaudio 	if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
894204df0f8Sclaudio 		log_warnx("RB_REMOVE(kif_tree, &kit, kif)");
895204df0f8Sclaudio 		return (-1);
896204df0f8Sclaudio 	}
897204df0f8Sclaudio 
89866dd3991Sclaudio 	while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) {
89966dd3991Sclaudio 		TAILQ_REMOVE(&kif->addrs, ka, entry);
90066dd3991Sclaudio 		free(ka);
90166dd3991Sclaudio 	}
902204df0f8Sclaudio 	free(kif);
903204df0f8Sclaudio 	return (0);
904204df0f8Sclaudio }
905204df0f8Sclaudio 
906204df0f8Sclaudio void
907204df0f8Sclaudio kif_clear(void)
908204df0f8Sclaudio {
909204df0f8Sclaudio 	struct kif_node	*kif;
910204df0f8Sclaudio 
911204df0f8Sclaudio 	while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
912204df0f8Sclaudio 		kif_remove(kif);
913204df0f8Sclaudio }
914204df0f8Sclaudio 
91573e34765Sclaudio struct kif *
91673e34765Sclaudio kif_update(u_short ifindex, int flags, struct if_data *ifd,
91773e34765Sclaudio     struct sockaddr_dl *sdl)
91873e34765Sclaudio {
91973e34765Sclaudio 	struct kif_node		*kif;
92073e34765Sclaudio 
9211c282279Sclaudio 	if ((kif = kif_find(ifindex)) == NULL) {
92273e34765Sclaudio 		if ((kif = kif_insert(ifindex)) == NULL)
92373e34765Sclaudio 			return (NULL);
9241c282279Sclaudio 		kif->k.nh_reachable = (flags & IFF_UP) &&
9259a2e0324Sclaudio 		    LINK_STATE_IS_UP(ifd->ifi_link_state);
9261c282279Sclaudio 	}
92773e34765Sclaudio 
92873e34765Sclaudio 	kif->k.flags = flags;
92973e34765Sclaudio 	kif->k.link_state = ifd->ifi_link_state;
93018ffdd94Sstsp 	kif->k.if_type = ifd->ifi_type;
93173e34765Sclaudio 	kif->k.baudrate = ifd->ifi_baudrate;
93273e34765Sclaudio 	kif->k.mtu = ifd->ifi_mtu;
933358269bbSclaudio 	kif->k.rdomain = ifd->ifi_rdomain;
93473e34765Sclaudio 
93573e34765Sclaudio 	if (sdl && sdl->sdl_family == AF_LINK) {
93673e34765Sclaudio 		if (sdl->sdl_nlen >= sizeof(kif->k.ifname))
93773e34765Sclaudio 			memcpy(kif->k.ifname, sdl->sdl_data,
93873e34765Sclaudio 			    sizeof(kif->k.ifname) - 1);
93973e34765Sclaudio 		else if (sdl->sdl_nlen > 0)
94073e34765Sclaudio 			memcpy(kif->k.ifname, sdl->sdl_data,
94173e34765Sclaudio 			    sdl->sdl_nlen);
94273e34765Sclaudio 		/* string already terminated via calloc() */
94373e34765Sclaudio 	}
94473e34765Sclaudio 
94573e34765Sclaudio 	return (&kif->k);
94673e34765Sclaudio }
94773e34765Sclaudio 
948f97d3cd4Sclaudio int
94973e34765Sclaudio kif_validate(u_short ifindex)
950f97d3cd4Sclaudio {
951f97d3cd4Sclaudio 	struct kif_node		*kif;
952f97d3cd4Sclaudio 
953f97d3cd4Sclaudio 	if ((kif = kif_find(ifindex)) == NULL) {
9549edfd913Sclaudio 		log_warnx("interface with index %u not found", ifindex);
955f97d3cd4Sclaudio 		return (1);
956f97d3cd4Sclaudio 	}
957f97d3cd4Sclaudio 
958f97d3cd4Sclaudio 	return (kif->k.nh_reachable);
959f97d3cd4Sclaudio }
960f97d3cd4Sclaudio 
961204df0f8Sclaudio struct kroute_node *
962204df0f8Sclaudio kroute_match(in_addr_t key)
963204df0f8Sclaudio {
964204df0f8Sclaudio 	int			 i;
965204df0f8Sclaudio 	struct kroute_node	*kr;
966204df0f8Sclaudio 
967204df0f8Sclaudio 	/* we will never match the default route */
968204df0f8Sclaudio 	for (i = 32; i > 0; i--)
9691795d796Sclaudio 		if ((kr = kroute_find(key & prefixlen2mask(i), i,
9701795d796Sclaudio 		    RTP_ANY)) != NULL)
971204df0f8Sclaudio 			return (kr);
972204df0f8Sclaudio 
973204df0f8Sclaudio 	/* if we don't have a match yet, try to find a default route */
9741795d796Sclaudio 	if ((kr = kroute_find(0, 0, RTP_ANY)) != NULL)
975204df0f8Sclaudio 			return (kr);
976204df0f8Sclaudio 
977204df0f8Sclaudio 	return (NULL);
978204df0f8Sclaudio }
979204df0f8Sclaudio 
980204df0f8Sclaudio /* misc */
981204df0f8Sclaudio int
982204df0f8Sclaudio protect_lo(void)
983204df0f8Sclaudio {
984204df0f8Sclaudio 	struct kroute_node	*kr;
985204df0f8Sclaudio 
986204df0f8Sclaudio 	/* special protection for 127/8 */
987204df0f8Sclaudio 	if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
988204df0f8Sclaudio 		log_warn("protect_lo");
989204df0f8Sclaudio 		return (-1);
990204df0f8Sclaudio 	}
991ca75fca8Sclaudio 	kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
992204df0f8Sclaudio 	kr->r.prefixlen = 8;
993204df0f8Sclaudio 	kr->r.flags = F_KERNEL|F_CONNECTED;
994204df0f8Sclaudio 
995204df0f8Sclaudio 	if (RB_INSERT(kroute_tree, &krt, kr) != NULL)
996204df0f8Sclaudio 		free(kr);	/* kernel route already there, no problem */
997204df0f8Sclaudio 
998204df0f8Sclaudio 	return (0);
999204df0f8Sclaudio }
1000204df0f8Sclaudio 
1001204df0f8Sclaudio u_int8_t
1002204df0f8Sclaudio prefixlen_classful(in_addr_t ina)
1003204df0f8Sclaudio {
1004204df0f8Sclaudio 	/* it hurt to write this. */
1005204df0f8Sclaudio 
1006204df0f8Sclaudio 	if (ina >= 0xf0000000U)		/* class E */
1007204df0f8Sclaudio 		return (32);
1008204df0f8Sclaudio 	else if (ina >= 0xe0000000U)	/* class D */
1009204df0f8Sclaudio 		return (4);
1010204df0f8Sclaudio 	else if (ina >= 0xc0000000U)	/* class C */
1011204df0f8Sclaudio 		return (24);
1012204df0f8Sclaudio 	else if (ina >= 0x80000000U)	/* class B */
1013204df0f8Sclaudio 		return (16);
1014204df0f8Sclaudio 	else				/* class A */
1015204df0f8Sclaudio 		return (8);
1016204df0f8Sclaudio }
1017204df0f8Sclaudio 
1018204df0f8Sclaudio u_int8_t
1019204df0f8Sclaudio mask2prefixlen(in_addr_t ina)
1020204df0f8Sclaudio {
1021204df0f8Sclaudio 	if (ina == 0)
1022204df0f8Sclaudio 		return (0);
1023204df0f8Sclaudio 	else
1024204df0f8Sclaudio 		return (33 - ffs(ntohl(ina)));
1025204df0f8Sclaudio }
1026204df0f8Sclaudio 
1027204df0f8Sclaudio in_addr_t
1028204df0f8Sclaudio prefixlen2mask(u_int8_t prefixlen)
1029204df0f8Sclaudio {
1030204df0f8Sclaudio 	if (prefixlen == 0)
1031204df0f8Sclaudio 		return (0);
1032204df0f8Sclaudio 
103315ab6e8cSclaudio 	return (htonl(0xffffffff << (32 - prefixlen)));
1034204df0f8Sclaudio }
1035204df0f8Sclaudio 
103648305c21Sclaudio #define ROUNDUP(a) \
1037064707deSfriehm 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
1038204df0f8Sclaudio 
1039204df0f8Sclaudio void
1040204df0f8Sclaudio get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
1041204df0f8Sclaudio {
1042204df0f8Sclaudio 	int	i;
1043204df0f8Sclaudio 
1044204df0f8Sclaudio 	for (i = 0; i < RTAX_MAX; i++) {
1045204df0f8Sclaudio 		if (addrs & (1 << i)) {
1046204df0f8Sclaudio 			rti_info[i] = sa;
1047204df0f8Sclaudio 			sa = (struct sockaddr *)((char *)(sa) +
104848305c21Sclaudio 			    ROUNDUP(sa->sa_len));
1049204df0f8Sclaudio 		} else
1050204df0f8Sclaudio 			rti_info[i] = NULL;
1051204df0f8Sclaudio 	}
1052204df0f8Sclaudio }
1053204df0f8Sclaudio 
1054204df0f8Sclaudio void
10551c282279Sclaudio if_change(u_short ifindex, int flags, struct if_data *ifd,
10561c282279Sclaudio     struct sockaddr_dl *sdl)
1057204df0f8Sclaudio {
105829b0bbeeSclaudio 	struct kroute_node	*kr, *tkr;
105973e34765Sclaudio 	struct kif		*kif;
1060204df0f8Sclaudio 	u_int8_t		 reachable;
1061204df0f8Sclaudio 
10621c282279Sclaudio 	if ((kif = kif_update(ifindex, flags, ifd, sdl)) == NULL) {
106373e34765Sclaudio 		log_warn("if_change:  kif_update(%u)", ifindex);
1064204df0f8Sclaudio 		return;
1065204df0f8Sclaudio 	}
1066204df0f8Sclaudio 
10673a9b53d2Sbenno 	/* notify ospfe about interface link state */
10683a9b53d2Sbenno 	main_imsg_compose_ospfe(IMSG_IFINFO, 0, kif, sizeof(struct kif));
10693a9b53d2Sbenno 
107073e34765Sclaudio 	reachable = (kif->flags & IFF_UP) &&
10719a2e0324Sclaudio 	    LINK_STATE_IS_UP(kif->link_state);
1072204df0f8Sclaudio 
107373e34765Sclaudio 	if (reachable == kif->nh_reachable)
1074204df0f8Sclaudio 		return;		/* nothing changed wrt nexthop validity */
1075204df0f8Sclaudio 
107673e34765Sclaudio 	kif->nh_reachable = reachable;
107744cb7d8eSclaudio 
107844cb7d8eSclaudio 	/* update redistribute list */
107929b0bbeeSclaudio 	RB_FOREACH(kr, kroute_tree, &krt) {
108029b0bbeeSclaudio 		for (tkr = kr; tkr != NULL; tkr = tkr->next) {
108129b0bbeeSclaudio 			if (tkr->r.ifindex == ifindex) {
108244cb7d8eSclaudio 				if (reachable)
108329b0bbeeSclaudio 					tkr->r.flags &= ~F_DOWN;
108444cb7d8eSclaudio 				else
108529b0bbeeSclaudio 					tkr->r.flags |= F_DOWN;
108644cb7d8eSclaudio 
108744cb7d8eSclaudio 			}
1088204df0f8Sclaudio 		}
108929b0bbeeSclaudio 		kr_redistribute(kr);
109029b0bbeeSclaudio 	}
109173e34765Sclaudio }
109229b0bbeeSclaudio 
109373e34765Sclaudio void
109473e34765Sclaudio if_newaddr(u_short ifindex, struct sockaddr_in *ifa, struct sockaddr_in *mask,
109573e34765Sclaudio     struct sockaddr_in *brd)
109673e34765Sclaudio {
109773e34765Sclaudio 	struct kif_node 	*kif;
109873e34765Sclaudio 	struct kif_addr 	*ka;
10992dc8313fSremi 	struct ifaddrchange	 ifn;
110073e34765Sclaudio 
110173e34765Sclaudio 	if (ifa == NULL || ifa->sin_family != AF_INET)
110273e34765Sclaudio 		return;
110373e34765Sclaudio 	if ((kif = kif_find(ifindex)) == NULL) {
1104fae4ffeaSderaadt 		log_warnx("if_newaddr: corresponding if %d not found", ifindex);
110572c35befSnorby 		return;
110673e34765Sclaudio 	}
110773e34765Sclaudio 	if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL)
110873e34765Sclaudio 		fatal("if_newaddr");
110973e34765Sclaudio 	ka->addr = ifa->sin_addr;
111073e34765Sclaudio 	if (mask)
111173e34765Sclaudio 		ka->mask = mask->sin_addr;
111273e34765Sclaudio 	else
111373e34765Sclaudio 		ka->mask.s_addr = INADDR_NONE;
111473e34765Sclaudio 	if (brd)
111573e34765Sclaudio 		ka->dstbrd = brd->sin_addr;
111673e34765Sclaudio 	else
111773e34765Sclaudio 		ka->dstbrd.s_addr = INADDR_NONE;
111873e34765Sclaudio 
111973e34765Sclaudio 	TAILQ_INSERT_TAIL(&kif->addrs, ka, entry);
11202dc8313fSremi 
11212dc8313fSremi 	ifn.addr = ka->addr;
11222dc8313fSremi 	ifn.mask = ka->mask;
11232dc8313fSremi 	ifn.dst = ka->dstbrd;
11242dc8313fSremi 	ifn.ifindex = ifindex;
11252dc8313fSremi 	main_imsg_compose_ospfe(IMSG_IFADDRADD, 0, &ifn, sizeof(ifn));
112629b0bbeeSclaudio }
1127204df0f8Sclaudio 
1128204df0f8Sclaudio void
1129a60d5a8aSclaudio if_deladdr(u_short ifindex, struct sockaddr_in *ifa, struct sockaddr_in *mask,
1130a60d5a8aSclaudio     struct sockaddr_in *brd)
1131a60d5a8aSclaudio {
1132a60d5a8aSclaudio 	struct kif_node 	*kif;
1133a60d5a8aSclaudio 	struct kif_addr		*ka, *nka;
11342dc8313fSremi 	struct ifaddrchange	 ifc;
1135a60d5a8aSclaudio 
1136a60d5a8aSclaudio 	if (ifa == NULL || ifa->sin_family != AF_INET)
1137a60d5a8aSclaudio 		return;
1138a60d5a8aSclaudio 	if ((kif = kif_find(ifindex)) == NULL) {
1139fae4ffeaSderaadt 		log_warnx("if_deladdr: corresponding if %d not found", ifindex);
1140a60d5a8aSclaudio 		return;
1141a60d5a8aSclaudio 	}
1142a60d5a8aSclaudio 
1143a60d5a8aSclaudio 	for (ka = TAILQ_FIRST(&kif->addrs); ka != NULL; ka = nka) {
1144a60d5a8aSclaudio 		nka = TAILQ_NEXT(ka, entry);
1145a60d5a8aSclaudio 
1146a60d5a8aSclaudio 		if (ka->addr.s_addr == ifa->sin_addr.s_addr) {
1147a60d5a8aSclaudio 			TAILQ_REMOVE(&kif->addrs, ka, entry);
1148a60d5a8aSclaudio 			ifc.addr = ifa->sin_addr;
1149a60d5a8aSclaudio 			ifc.ifindex = ifindex;
1150a60d5a8aSclaudio 			main_imsg_compose_ospfe(IMSG_IFADDRDEL, 0, &ifc,
1151a60d5a8aSclaudio 			    sizeof(ifc));
1152a60d5a8aSclaudio 			free(ka);
1153a60d5a8aSclaudio 			return;
1154a60d5a8aSclaudio 		}
1155a60d5a8aSclaudio 	}
1156a60d5a8aSclaudio }
1157a60d5a8aSclaudio 
1158a60d5a8aSclaudio void
1159204df0f8Sclaudio if_announce(void *msg)
1160204df0f8Sclaudio {
1161204df0f8Sclaudio 	struct if_announcemsghdr	*ifan;
1162204df0f8Sclaudio 	struct kif_node			*kif;
1163204df0f8Sclaudio 
1164204df0f8Sclaudio 	ifan = msg;
1165204df0f8Sclaudio 
1166204df0f8Sclaudio 	switch (ifan->ifan_what) {
1167204df0f8Sclaudio 	case IFAN_ARRIVAL:
116873e34765Sclaudio 		kif = kif_insert(ifan->ifan_index);
1169204df0f8Sclaudio 		strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname));
1170204df0f8Sclaudio 		break;
1171204df0f8Sclaudio 	case IFAN_DEPARTURE:
1172204df0f8Sclaudio 		kif = kif_find(ifan->ifan_index);
1173*95d3cd23Santon 		if (kif != NULL)
1174204df0f8Sclaudio 			kif_remove(kif);
1175204df0f8Sclaudio 		break;
1176204df0f8Sclaudio 	}
1177204df0f8Sclaudio }
1178204df0f8Sclaudio 
1179204df0f8Sclaudio /* rtsock */
1180204df0f8Sclaudio int
1181204df0f8Sclaudio send_rtmsg(int fd, int action, struct kroute *kroute)
1182204df0f8Sclaudio {
1183fcb4545bSreyk 	struct iovec		iov[5];
1184204df0f8Sclaudio 	struct rt_msghdr	hdr;
1185204df0f8Sclaudio 	struct sockaddr_in	prefix;
1186204df0f8Sclaudio 	struct sockaddr_in	nexthop;
1187204df0f8Sclaudio 	struct sockaddr_in	mask;
1188fcb4545bSreyk 	struct sockaddr_rtlabel	sa_rl;
11892dbdedf7Sclaudio 	int			iovcnt = 0;
1190fcb4545bSreyk 	const char		*label;
1191204df0f8Sclaudio 
1192204df0f8Sclaudio 	if (kr_state.fib_sync == 0)
1193204df0f8Sclaudio 		return (0);
1194204df0f8Sclaudio 
11952dbdedf7Sclaudio 	/* initialize header */
11962dbdedf7Sclaudio 	bzero(&hdr, sizeof(hdr));
11972dbdedf7Sclaudio 	hdr.rtm_version = RTM_VERSION;
11982dbdedf7Sclaudio 	hdr.rtm_type = action;
11994c260f66Sremi 	hdr.rtm_priority = kr_state.fib_prio;
1200e4caa3d9Sclaudio 	hdr.rtm_tableid = kr_state.rdomain;	/* rtableid */
12011795d796Sclaudio 	if (action == RTM_CHANGE)
12021795d796Sclaudio 		hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE;
12031795d796Sclaudio 	else
12041795d796Sclaudio 		hdr.rtm_flags = RTF_MPATH;
12052dbdedf7Sclaudio 	hdr.rtm_seq = kr_state.rtseq++;	/* overflow doesn't matter */
12062dbdedf7Sclaudio 	hdr.rtm_msglen = sizeof(hdr);
12072dbdedf7Sclaudio 	/* adjust iovec */
12082dbdedf7Sclaudio 	iov[iovcnt].iov_base = &hdr;
12092dbdedf7Sclaudio 	iov[iovcnt++].iov_len = sizeof(hdr);
1210204df0f8Sclaudio 
12112dbdedf7Sclaudio 	bzero(&prefix, sizeof(prefix));
12122dbdedf7Sclaudio 	prefix.sin_len = sizeof(prefix);
12132dbdedf7Sclaudio 	prefix.sin_family = AF_INET;
12142dbdedf7Sclaudio 	prefix.sin_addr.s_addr = kroute->prefix.s_addr;
12152dbdedf7Sclaudio 	/* adjust header */
12162dbdedf7Sclaudio 	hdr.rtm_addrs |= RTA_DST;
12172dbdedf7Sclaudio 	hdr.rtm_msglen += sizeof(prefix);
12182dbdedf7Sclaudio 	/* adjust iovec */
12192dbdedf7Sclaudio 	iov[iovcnt].iov_base = &prefix;
12202dbdedf7Sclaudio 	iov[iovcnt++].iov_len = sizeof(prefix);
1221204df0f8Sclaudio 
12222dbdedf7Sclaudio 	if (kroute->nexthop.s_addr != 0) {
12232dbdedf7Sclaudio 		bzero(&nexthop, sizeof(nexthop));
12242dbdedf7Sclaudio 		nexthop.sin_len = sizeof(nexthop);
12252dbdedf7Sclaudio 		nexthop.sin_family = AF_INET;
12262dbdedf7Sclaudio 		nexthop.sin_addr.s_addr = kroute->nexthop.s_addr;
12272dbdedf7Sclaudio 		/* adjust header */
12282dbdedf7Sclaudio 		hdr.rtm_flags |= RTF_GATEWAY;
12292dbdedf7Sclaudio 		hdr.rtm_addrs |= RTA_GATEWAY;
12302dbdedf7Sclaudio 		hdr.rtm_msglen += sizeof(nexthop);
12312dbdedf7Sclaudio 		/* adjust iovec */
12322dbdedf7Sclaudio 		iov[iovcnt].iov_base = &nexthop;
12332dbdedf7Sclaudio 		iov[iovcnt++].iov_len = sizeof(nexthop);
12342dbdedf7Sclaudio 	}
12352dbdedf7Sclaudio 
12362dbdedf7Sclaudio 	bzero(&mask, sizeof(mask));
12372dbdedf7Sclaudio 	mask.sin_len = sizeof(mask);
12382dbdedf7Sclaudio 	mask.sin_family = AF_INET;
12392dbdedf7Sclaudio 	mask.sin_addr.s_addr = prefixlen2mask(kroute->prefixlen);
12402dbdedf7Sclaudio 	/* adjust header */
12412dbdedf7Sclaudio 	hdr.rtm_addrs |= RTA_NETMASK;
12422dbdedf7Sclaudio 	hdr.rtm_msglen += sizeof(mask);
12432dbdedf7Sclaudio 	/* adjust iovec */
12442dbdedf7Sclaudio 	iov[iovcnt].iov_base = &mask;
12452dbdedf7Sclaudio 	iov[iovcnt++].iov_len = sizeof(mask);
1246204df0f8Sclaudio 
1247fcb4545bSreyk 	if (kroute->rtlabel != 0) {
1248fcb4545bSreyk 		sa_rl.sr_len = sizeof(sa_rl);
1249fcb4545bSreyk 		sa_rl.sr_family = AF_UNSPEC;
1250fcb4545bSreyk 		label = rtlabel_id2name(kroute->rtlabel);
1251fcb4545bSreyk 		if (strlcpy(sa_rl.sr_label, label,
1252fcb4545bSreyk 		    sizeof(sa_rl.sr_label)) >= sizeof(sa_rl.sr_label)) {
1253fcb4545bSreyk 			log_warnx("send_rtmsg: invalid rtlabel");
1254fcb4545bSreyk 			return (-1);
1255fcb4545bSreyk 		}
1256fcb4545bSreyk 		/* adjust header */
1257fcb4545bSreyk 		hdr.rtm_addrs |= RTA_LABEL;
1258fcb4545bSreyk 		hdr.rtm_msglen += sizeof(sa_rl);
1259fcb4545bSreyk 		/* adjust iovec */
1260fcb4545bSreyk 		iov[iovcnt].iov_base = &sa_rl;
1261fcb4545bSreyk 		iov[iovcnt++].iov_len = sizeof(sa_rl);
1262fcb4545bSreyk 	}
1263fcb4545bSreyk 
1264204df0f8Sclaudio retry:
12652dbdedf7Sclaudio 	if (writev(fd, iov, iovcnt) == -1) {
12664324a051Sbluhm 		if (errno == ESRCH) {
12672dbdedf7Sclaudio 			if (hdr.rtm_type == RTM_CHANGE) {
12682dbdedf7Sclaudio 				hdr.rtm_type = RTM_ADD;
1269204df0f8Sclaudio 				goto retry;
12702dbdedf7Sclaudio 			} else if (hdr.rtm_type == RTM_DELETE) {
1271204df0f8Sclaudio 				log_info("route %s/%u vanished before delete",
1272204df0f8Sclaudio 				    inet_ntoa(kroute->prefix),
1273204df0f8Sclaudio 				    kroute->prefixlen);
1274204df0f8Sclaudio 				return (0);
1275204df0f8Sclaudio 			}
1276204df0f8Sclaudio 		}
12774324a051Sbluhm 		log_warn("send_rtmsg: action %u, prefix %s/%u", hdr.rtm_type,
12784324a051Sbluhm 		    inet_ntoa(kroute->prefix), kroute->prefixlen);
12794324a051Sbluhm 		return (0);
1280204df0f8Sclaudio 	}
1281204df0f8Sclaudio 
1282204df0f8Sclaudio 	return (0);
1283204df0f8Sclaudio }
1284204df0f8Sclaudio 
1285204df0f8Sclaudio int
1286204df0f8Sclaudio fetchtable(void)
1287204df0f8Sclaudio {
1288204df0f8Sclaudio 	size_t			 len;
1289836113cfShenning 	int			 mib[7];
12905fba50a6Sclaudio 	char			*buf;
12915fba50a6Sclaudio 	int			 rv;
1292119f0f1dSdlg 
1293119f0f1dSdlg 	mib[0] = CTL_NET;
1294149dc9fcSguenther 	mib[1] = PF_ROUTE;
1295119f0f1dSdlg 	mib[2] = 0;
1296119f0f1dSdlg 	mib[3] = AF_INET;
1297119f0f1dSdlg 	mib[4] = NET_RT_DUMP;
1298119f0f1dSdlg 	mib[5] = 0;
1299119f0f1dSdlg 	mib[6] = kr_state.rdomain;	/* rtableid */
1300119f0f1dSdlg 
1301119f0f1dSdlg 	if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
1302119f0f1dSdlg 		log_warn("sysctl");
1303119f0f1dSdlg 		return (-1);
1304119f0f1dSdlg 	}
1305119f0f1dSdlg 	if ((buf = malloc(len)) == NULL) {
1306119f0f1dSdlg 		log_warn("fetchtable");
1307119f0f1dSdlg 		return (-1);
1308119f0f1dSdlg 	}
1309119f0f1dSdlg 	if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
1310119f0f1dSdlg 		log_warn("sysctl");
1311119f0f1dSdlg 		free(buf);
1312119f0f1dSdlg 		return (-1);
1313119f0f1dSdlg 	}
1314119f0f1dSdlg 
1315c2a29d3bSdlg 	rv = rtmsg_process(buf, len);
1316119f0f1dSdlg 	free(buf);
1317c2a29d3bSdlg 
1318119f0f1dSdlg 	return (rv);
1319119f0f1dSdlg }
1320119f0f1dSdlg 
1321119f0f1dSdlg int
132273e34765Sclaudio fetchifs(u_short ifindex)
1323204df0f8Sclaudio {
1324204df0f8Sclaudio 	size_t			 len;
1325204df0f8Sclaudio 	int			 mib[6];
13269d7a9d5fSdlg 	char			*buf;
13279d7a9d5fSdlg 	int			 rv;
1328204df0f8Sclaudio 
1329204df0f8Sclaudio 	mib[0] = CTL_NET;
1330149dc9fcSguenther 	mib[1] = PF_ROUTE;
1331204df0f8Sclaudio 	mib[2] = 0;
1332204df0f8Sclaudio 	mib[3] = AF_INET;
1333204df0f8Sclaudio 	mib[4] = NET_RT_IFLIST;
1334204df0f8Sclaudio 	mib[5] = ifindex;
1335204df0f8Sclaudio 
1336204df0f8Sclaudio 	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
1337204df0f8Sclaudio 		log_warn("sysctl");
1338204df0f8Sclaudio 		return (-1);
1339204df0f8Sclaudio 	}
1340204df0f8Sclaudio 	if ((buf = malloc(len)) == NULL) {
1341204df0f8Sclaudio 		log_warn("fetchif");
1342204df0f8Sclaudio 		return (-1);
1343204df0f8Sclaudio 	}
1344204df0f8Sclaudio 	if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
1345204df0f8Sclaudio 		log_warn("sysctl");
1346204df0f8Sclaudio 		free(buf);
1347204df0f8Sclaudio 		return (-1);
1348204df0f8Sclaudio 	}
1349204df0f8Sclaudio 
13509d7a9d5fSdlg 	rv = rtmsg_process(buf, len);
1351204df0f8Sclaudio 	free(buf);
13529d7a9d5fSdlg 
13539d7a9d5fSdlg 	return (rv);
1354204df0f8Sclaudio }
1355204df0f8Sclaudio 
1356204df0f8Sclaudio int
1357204df0f8Sclaudio dispatch_rtmsg(void)
1358204df0f8Sclaudio {
1359204df0f8Sclaudio 	char			 buf[RT_BUF_SIZE];
1360204df0f8Sclaudio 	ssize_t			 n;
13619d7a9d5fSdlg 
13629d7a9d5fSdlg 	if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) {
1363f2c05001Sclaudio 		if (errno == EAGAIN || errno == EINTR)
1364f2c05001Sclaudio 			return (0);
13659d7a9d5fSdlg 		log_warn("dispatch_rtmsg: read error");
13669d7a9d5fSdlg 		return (-1);
13679d7a9d5fSdlg 	}
13689d7a9d5fSdlg 
13699d7a9d5fSdlg 	if (n == 0) {
13709d7a9d5fSdlg 		log_warnx("routing socket closed");
13719d7a9d5fSdlg 		return (-1);
13729d7a9d5fSdlg 	}
13739d7a9d5fSdlg 
13749d7a9d5fSdlg 	return (rtmsg_process(buf, n));
13759d7a9d5fSdlg }
13769d7a9d5fSdlg 
13779d7a9d5fSdlg int
137858ef7452Sclaudio rtmsg_process(char *buf, size_t len)
13799d7a9d5fSdlg {
1380204df0f8Sclaudio 	struct rt_msghdr	*rtm;
1381204df0f8Sclaudio 	struct if_msghdr	 ifm;
138273e34765Sclaudio 	struct ifa_msghdr	*ifam;
1383204df0f8Sclaudio 	struct sockaddr		*sa, *rti_info[RTAX_MAX];
1384204df0f8Sclaudio 	struct sockaddr_in	*sa_in;
1385bbb232d2Sclaudio 	struct sockaddr_rtlabel	*label;
138629b0bbeeSclaudio 	struct kroute_node	*kr, *okr;
1387204df0f8Sclaudio 	struct in_addr		 prefix, nexthop;
13881795d796Sclaudio 	u_int8_t		 prefixlen, prio;
138929b0bbeeSclaudio 	int			 flags, mpath;
1390f08c543aShenning 	u_short			 ifindex = 0;
1391fddf39b9Ssthen 	int			 rv, delay;
1392204df0f8Sclaudio 
139358ef7452Sclaudio 	size_t			 offset;
13949d7a9d5fSdlg 	char			*next;
1395204df0f8Sclaudio 
13969d7a9d5fSdlg 	for (offset = 0; offset < len; offset += rtm->rtm_msglen) {
13979d7a9d5fSdlg 		next = buf + offset;
1398204df0f8Sclaudio 		rtm = (struct rt_msghdr *)next;
1399242fc2efSclaudio 		if (len < offset + sizeof(u_short) ||
140058ef7452Sclaudio 		    len < offset + rtm->rtm_msglen)
14018a4d3917Sdenis 			fatalx("%s: partial rtm in buffer", __func__);
140218e1f14bSclaudio 		if (rtm->rtm_version != RTM_VERSION)
140318e1f14bSclaudio 			continue;
1404204df0f8Sclaudio 
1405204df0f8Sclaudio 		prefix.s_addr = 0;
1406204df0f8Sclaudio 		prefixlen = 0;
1407204df0f8Sclaudio 		nexthop.s_addr = 0;
140829b0bbeeSclaudio 		mpath = 0;
14091795d796Sclaudio 		prio = 0;
141033afd907Sclaudio 		flags = F_KERNEL;
1411204df0f8Sclaudio 
14125fba50a6Sclaudio 		sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
14135fba50a6Sclaudio 		get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
14145fba50a6Sclaudio 
1415c2a29d3bSdlg 		switch (rtm->rtm_type) {
1416c2a29d3bSdlg 		case RTM_ADD:
1417c2a29d3bSdlg 		case RTM_GET:
1418c2a29d3bSdlg 		case RTM_CHANGE:
1419c2a29d3bSdlg 		case RTM_DELETE:
1420ade6468bSsthen 			if (rtm->rtm_errno)		/* failed attempts... */
1421ade6468bSsthen 				continue;
1422ade6468bSsthen 
1423e4caa3d9Sclaudio 			if (rtm->rtm_tableid != kr_state.rdomain)
1424a8b4fd7dSclaudio 				continue;
1425a8b4fd7dSclaudio 
1426bef2e7d6Sclaudio 			if (rtm->rtm_type == RTM_GET &&
1427c1040092Sclaudio 			    rtm->rtm_pid != kr_state.pid)
1428bef2e7d6Sclaudio 				continue;
1429bef2e7d6Sclaudio 
1430c2a29d3bSdlg 			if ((sa = rti_info[RTAX_DST]) == NULL)
1431204df0f8Sclaudio 				continue;
1432204df0f8Sclaudio 
143334635c53Sclaudio 			/* Skip ARP/ND cache and broadcast routes. */
143434635c53Sclaudio 			if (rtm->rtm_flags & (RTF_LLINFO|RTF_BROADCAST))
1435204df0f8Sclaudio 				continue;
1436f08c543aShenning 
143729b0bbeeSclaudio 			if (rtm->rtm_flags & RTF_MPATH)
143829b0bbeeSclaudio 				mpath = 1;
14391795d796Sclaudio 			prio = rtm->rtm_priority;
14404c260f66Sremi 			flags = (prio == kr_state.fib_prio) ?
1441c2a29d3bSdlg 			    F_OSPFD_INSERTED : F_KERNEL;
14421795d796Sclaudio 
1443204df0f8Sclaudio 			switch (sa->sa_family) {
1444204df0f8Sclaudio 			case AF_INET:
1445204df0f8Sclaudio 				prefix.s_addr =
1446204df0f8Sclaudio 				    ((struct sockaddr_in *)sa)->sin_addr.s_addr;
1447204df0f8Sclaudio 				sa_in = (struct sockaddr_in *)
1448204df0f8Sclaudio 				    rti_info[RTAX_NETMASK];
1449204df0f8Sclaudio 				if (sa_in != NULL) {
1450204df0f8Sclaudio 					if (sa_in->sin_len != 0)
1451204df0f8Sclaudio 						prefixlen = mask2prefixlen(
1452204df0f8Sclaudio 						    sa_in->sin_addr.s_addr);
1453204df0f8Sclaudio 				} else if (rtm->rtm_flags & RTF_HOST)
1454204df0f8Sclaudio 					prefixlen = 32;
1455204df0f8Sclaudio 				else
1456204df0f8Sclaudio 					prefixlen =
1457204df0f8Sclaudio 					    prefixlen_classful(prefix.s_addr);
1458e2993955Sclaudio 				if (rtm->rtm_flags & RTF_STATIC)
1459e2993955Sclaudio 					flags |= F_STATIC;
1460d9eec83cSclaudio 				if (rtm->rtm_flags & RTF_BLACKHOLE)
1461d9eec83cSclaudio 					flags |= F_BLACKHOLE;
1462d9eec83cSclaudio 				if (rtm->rtm_flags & RTF_REJECT)
1463d9eec83cSclaudio 					flags |= F_REJECT;
146440df8bcdSclaudio 				if (rtm->rtm_flags & RTF_DYNAMIC)
146540df8bcdSclaudio 					flags |= F_DYNAMIC;
1466204df0f8Sclaudio 				break;
1467204df0f8Sclaudio 			default:
1468204df0f8Sclaudio 				continue;
1469204df0f8Sclaudio 			}
1470204df0f8Sclaudio 
147107db309bSclaudio 			ifindex = rtm->rtm_index;
1472f08c543aShenning 			if ((sa = rti_info[RTAX_GATEWAY]) != NULL) {
1473204df0f8Sclaudio 				switch (sa->sa_family) {
1474204df0f8Sclaudio 				case AF_INET:
1475c90ecb6bSfriehm 					if (rtm->rtm_flags & RTF_CONNECTED)
147634635c53Sclaudio 						flags |= F_CONNECTED;
147734635c53Sclaudio 
1478a66fd4eeShenning 					nexthop.s_addr = ((struct
1479a66fd4eeShenning 					    sockaddr_in *)sa)->sin_addr.s_addr;
1480204df0f8Sclaudio 					break;
1481204df0f8Sclaudio 				case AF_LINK:
148234635c53Sclaudio 					/*
148334635c53Sclaudio 					 * Traditional BSD connected routes have
148434635c53Sclaudio 					 * a gateway of type AF_LINK.
148534635c53Sclaudio 					 */
1486204df0f8Sclaudio 					flags |= F_CONNECTED;
1487204df0f8Sclaudio 					break;
1488204df0f8Sclaudio 				}
1489f08c543aShenning 			}
1490f08c543aShenning 		}
1491204df0f8Sclaudio 
1492204df0f8Sclaudio 		switch (rtm->rtm_type) {
1493204df0f8Sclaudio 		case RTM_ADD:
1494c2a29d3bSdlg 		case RTM_GET:
1495204df0f8Sclaudio 		case RTM_CHANGE:
1496204df0f8Sclaudio 			if (nexthop.s_addr == 0 && !(flags & F_CONNECTED)) {
1497c2a29d3bSdlg 				log_warnx("no nexthop for %s/%u",
1498204df0f8Sclaudio 				    inet_ntoa(prefix), prefixlen);
1499204df0f8Sclaudio 				continue;
1500204df0f8Sclaudio 			}
1501204df0f8Sclaudio 
15021795d796Sclaudio 			if ((okr = kroute_find(prefix.s_addr, prefixlen, prio))
15031795d796Sclaudio 			    != NULL) {
150429b0bbeeSclaudio 				/* get the correct route */
150529b0bbeeSclaudio 				kr = okr;
15064c260f66Sremi 				if ((mpath || prio == kr_state.fib_prio) &&
1507c2a29d3bSdlg 				    (kr = kroute_matchgw(okr, nexthop)) ==
1508c2a29d3bSdlg 				    NULL) {
15098a4d3917Sdenis 					log_warnx("%s: mpath route not found",
15108a4d3917Sdenis 					    __func__);
151129b0bbeeSclaudio 					/* add routes we missed out earlier */
151229b0bbeeSclaudio 					goto add;
151329b0bbeeSclaudio 				}
151429b0bbeeSclaudio 
1515bbb232d2Sclaudio 				if (kr->r.flags & F_REDISTRIBUTED)
1516bbb232d2Sclaudio 					flags |= F_REDISTRIBUTED;
1517204df0f8Sclaudio 				kr->r.nexthop.s_addr = nexthop.s_addr;
1518204df0f8Sclaudio 				kr->r.flags = flags;
15191446e0e7Sclaudio 				kr->r.ifindex = ifindex;
152044cb7d8eSclaudio 
1521bbb232d2Sclaudio 				rtlabel_unref(kr->r.rtlabel);
1522bbb232d2Sclaudio 				kr->r.rtlabel = 0;
1523fcb4545bSreyk 				kr->r.ext_tag = 0;
1524bbb232d2Sclaudio 				if ((label = (struct sockaddr_rtlabel *)
1525fcb4545bSreyk 				    rti_info[RTAX_LABEL]) != NULL) {
1526bbb232d2Sclaudio 					kr->r.rtlabel =
1527bbb232d2Sclaudio 					    rtlabel_name2id(label->sr_label);
1528fcb4545bSreyk 					kr->r.ext_tag =
1529fcb4545bSreyk 					    rtlabel_id2tag(kr->r.rtlabel);
1530fcb4545bSreyk 				}
1531bbb232d2Sclaudio 
153244cb7d8eSclaudio 				if (kif_validate(kr->r.ifindex))
153344cb7d8eSclaudio 					kr->r.flags &= ~F_DOWN;
153444cb7d8eSclaudio 				else
153544cb7d8eSclaudio 					kr->r.flags |= F_DOWN;
153644cb7d8eSclaudio 
1537e2993955Sclaudio 				/* just readd, the RDE will care */
1538c2a29d3bSdlg 				kr->serial = kr_state.fib_serial;
15399ae468ceSclaudio 				kr_redistribute(okr);
1540204df0f8Sclaudio 			} else {
154129b0bbeeSclaudio add:
1542204df0f8Sclaudio 				if ((kr = calloc(1,
1543204df0f8Sclaudio 				    sizeof(struct kroute_node))) == NULL) {
15448a4d3917Sdenis 					log_warn("%s: calloc", __func__);
1545412a8077Sdlg 					return (-1);
1546204df0f8Sclaudio 				}
1547c2a29d3bSdlg 
1548204df0f8Sclaudio 				kr->r.prefix.s_addr = prefix.s_addr;
1549204df0f8Sclaudio 				kr->r.prefixlen = prefixlen;
1550204df0f8Sclaudio 				kr->r.nexthop.s_addr = nexthop.s_addr;
1551204df0f8Sclaudio 				kr->r.flags = flags;
1552204df0f8Sclaudio 				kr->r.ifindex = ifindex;
15531795d796Sclaudio 				kr->r.priority = prio;
1554204df0f8Sclaudio 
15554c260f66Sremi 				if (rtm->rtm_priority == kr_state.fib_prio) {
1556c2a29d3bSdlg 					log_warnx("alien OSPF route %s/%d",
1557c2a29d3bSdlg 					    inet_ntoa(prefix), prefixlen);
1558c2a29d3bSdlg 					rv = send_rtmsg(kr_state.fd,
1559c2a29d3bSdlg 					    RTM_DELETE, &kr->r);
1560c2a29d3bSdlg 					free(kr);
1561c2a29d3bSdlg 					if (rv == -1)
1562412a8077Sdlg 						return (-1);
1563c2a29d3bSdlg 				} else {
1564bbb232d2Sclaudio 					if ((label = (struct sockaddr_rtlabel *)
1565fcb4545bSreyk 					    rti_info[RTAX_LABEL]) != NULL) {
1566bbb232d2Sclaudio 						kr->r.rtlabel =
1567c2a29d3bSdlg 						    rtlabel_name2id(
1568c2a29d3bSdlg 						    label->sr_label);
1569fcb4545bSreyk 						kr->r.ext_tag =
1570c2a29d3bSdlg 						    rtlabel_id2tag(
1571c2a29d3bSdlg 						    kr->r.rtlabel);
1572fcb4545bSreyk 					}
1573bbb232d2Sclaudio 
1574204df0f8Sclaudio 					kroute_insert(kr);
1575204df0f8Sclaudio 				}
1576c2a29d3bSdlg 			}
1577204df0f8Sclaudio 			break;
1578204df0f8Sclaudio 		case RTM_DELETE:
15791795d796Sclaudio 			if ((kr = kroute_find(prefix.s_addr, prefixlen, prio))
15801795d796Sclaudio 			    == NULL)
1581204df0f8Sclaudio 				continue;
1582204df0f8Sclaudio 			if (!(kr->r.flags & F_KERNEL))
1583204df0f8Sclaudio 				continue;
158429b0bbeeSclaudio 			/* get the correct route */
158529b0bbeeSclaudio 			okr = kr;
158629b0bbeeSclaudio 			if (mpath &&
158729b0bbeeSclaudio 			    (kr = kroute_matchgw(kr, nexthop)) == NULL) {
15888a4d3917Sdenis 				log_warnx("%s: mpath route not found",
15898a4d3917Sdenis 				    __func__);
159029b0bbeeSclaudio 				return (-1);
159129b0bbeeSclaudio 			}
1592204df0f8Sclaudio 			if (kroute_remove(kr) == -1)
1593204df0f8Sclaudio 				return (-1);
1594204df0f8Sclaudio 			break;
1595204df0f8Sclaudio 		case RTM_IFINFO:
1596204df0f8Sclaudio 			memcpy(&ifm, next, sizeof(ifm));
15971c282279Sclaudio 			if_change(ifm.ifm_index, ifm.ifm_flags, &ifm.ifm_data,
15981c282279Sclaudio 			    (struct sockaddr_dl *)rti_info[RTAX_IFP]);
1599204df0f8Sclaudio 			break;
160073e34765Sclaudio 		case RTM_NEWADDR:
160173e34765Sclaudio 			ifam = (struct ifa_msghdr *)rtm;
160273e34765Sclaudio 			if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
160373e34765Sclaudio 			    RTA_BRD)) == 0)
160473e34765Sclaudio 				break;
160573e34765Sclaudio 
160673e34765Sclaudio 			if_newaddr(ifam->ifam_index,
160773e34765Sclaudio 			    (struct sockaddr_in *)rti_info[RTAX_IFA],
160873e34765Sclaudio 			    (struct sockaddr_in *)rti_info[RTAX_NETMASK],
160973e34765Sclaudio 			    (struct sockaddr_in *)rti_info[RTAX_BRD]);
161073e34765Sclaudio 			break;
1611a60d5a8aSclaudio 		case RTM_DELADDR:
1612a60d5a8aSclaudio 			ifam = (struct ifa_msghdr *)rtm;
1613a60d5a8aSclaudio 			if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
1614a60d5a8aSclaudio 			    RTA_BRD)) == 0)
1615a60d5a8aSclaudio 				break;
1616a60d5a8aSclaudio 
1617a60d5a8aSclaudio 			if_deladdr(ifam->ifam_index,
1618a60d5a8aSclaudio 			    (struct sockaddr_in *)rti_info[RTAX_IFA],
1619a60d5a8aSclaudio 			    (struct sockaddr_in *)rti_info[RTAX_NETMASK],
1620a60d5a8aSclaudio 			    (struct sockaddr_in *)rti_info[RTAX_BRD]);
1621a60d5a8aSclaudio 			break;
1622204df0f8Sclaudio 		case RTM_IFANNOUNCE:
1623204df0f8Sclaudio 			if_announce(next);
1624204df0f8Sclaudio 			break;
1625fa19e37cSdlg 		case RTM_DESYNC:
1626fa19e37cSdlg 			/*
1627fddf39b9Ssthen 			 * We lost some routing packets. Schedule a reload
1628fddf39b9Ssthen 			 * of the kernel route/interface information.
1629fa19e37cSdlg 			 */
1630fddf39b9Ssthen 			if (kr_state.reload_state == KR_RELOAD_IDLE) {
1631fddf39b9Ssthen 				delay = KR_RELOAD_TIMER;
1632fddf39b9Ssthen 				log_info("desync; scheduling fib reload");
1633fddf39b9Ssthen 			} else {
1634fddf39b9Ssthen 				delay = KR_RELOAD_HOLD_TIMER;
1635fddf39b9Ssthen 				log_debug("desync during KR_RELOAD_%s",
1636fddf39b9Ssthen 				    kr_state.reload_state ==
1637fddf39b9Ssthen 				    KR_RELOAD_FETCH ? "FETCH" : "HOLD");
1638fddf39b9Ssthen 			}
1639fddf39b9Ssthen 			kr_state.reload_state = KR_RELOAD_FETCH;
1640fddf39b9Ssthen 			kr_fib_reload_arm_timer(delay);
1641fa19e37cSdlg 			break;
1642204df0f8Sclaudio 		default:
1643204df0f8Sclaudio 			/* ignore for now */
1644204df0f8Sclaudio 			break;
1645204df0f8Sclaudio 		}
1646204df0f8Sclaudio 	}
16479d7a9d5fSdlg 
16489d7a9d5fSdlg 	return (offset);
1649204df0f8Sclaudio }
1650