xref: /openbsd-src/usr.sbin/ospfd/rde_spf.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: rde_spf.c,v 1.79 2023/03/08 04:43:14 guenther Exp $ */
23ada9d8fSnorby 
33ada9d8fSnorby /*
43ada9d8fSnorby  * Copyright (c) 2005 Esben Norby <norby@openbsd.org>
53ada9d8fSnorby  *
63ada9d8fSnorby  * Permission to use, copy, modify, and distribute this software for any
73ada9d8fSnorby  * purpose with or without fee is hereby granted, provided that the above
83ada9d8fSnorby  * copyright notice and this permission notice appear in all copies.
93ada9d8fSnorby  *
103ada9d8fSnorby  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113ada9d8fSnorby  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123ada9d8fSnorby  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133ada9d8fSnorby  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143ada9d8fSnorby  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153ada9d8fSnorby  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163ada9d8fSnorby  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173ada9d8fSnorby  */
183ada9d8fSnorby 
193ada9d8fSnorby #include <sys/types.h>
203ada9d8fSnorby #include <sys/socket.h>
213ada9d8fSnorby #include <netinet/in.h>
223ada9d8fSnorby #include <arpa/inet.h>
233ada9d8fSnorby #include <err.h>
243ada9d8fSnorby #include <stdlib.h>
2506267228Sclaudio #include <string.h>
263ada9d8fSnorby 
273ada9d8fSnorby #include "ospfd.h"
283ada9d8fSnorby #include "ospf.h"
293ada9d8fSnorby #include "log.h"
303ada9d8fSnorby #include "rde.h"
313ada9d8fSnorby 
323ada9d8fSnorby extern struct ospfd_conf	*rdeconf;
333ada9d8fSnorby TAILQ_HEAD(, vertex)		 cand_list;
34eb1522a1Snorby RB_HEAD(rt_tree, rt_node)	 rt;
353ada9d8fSnorby RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare)
363ada9d8fSnorby RB_GENERATE(rt_tree, rt_node, entry, rt_compare)
373ada9d8fSnorby struct vertex			*spf_root = NULL;
383ada9d8fSnorby 
39d91e982dSclaudio void	 calc_nexthop(struct vertex *, struct vertex *,
40d91e982dSclaudio 	     struct area *, struct lsa_rtr_link *);
417d1924b4Sclaudio void	 rt_nexthop_clear(struct rt_node *);
426f151e9aSclaudio void	 rt_nexthop_add(struct rt_node *, struct v_nexthead *, u_int8_t,
437d1924b4Sclaudio 	     struct in_addr);
446f151e9aSclaudio void	 rt_update(struct in_addr, u_int8_t, struct v_nexthead *, u_int8_t,
457d1924b4Sclaudio 	     u_int32_t, u_int32_t, struct in_addr, struct in_addr,
46fcb4545bSreyk 	     enum path_type, enum dst_type, u_int8_t, u_int32_t);
4782284507Sclaudio void	 rt_invalidate(struct area *);
486e97b0ccSclaudio struct lsa_rtr_link	*get_rtr_link(struct vertex *, int);
496e97b0ccSclaudio struct lsa_net_link	*get_net_link(struct vertex *, int);
505c6e55e9Snorby int	 linked(struct vertex *, struct vertex *);
513ada9d8fSnorby 
523ada9d8fSnorby void
spf_calc(struct area * area)533ada9d8fSnorby spf_calc(struct area *area)
543ada9d8fSnorby {
553ada9d8fSnorby 	struct vertex		*v, *w;
563ada9d8fSnorby 	struct lsa_rtr_link	*rtr_link = NULL;
5763f14b64Sclaudio 	struct lsa_net_link	*net_link;
5890625dffSnorby 	u_int32_t		 d;
593ada9d8fSnorby 	int			 i;
60cffa66bfSnorby 	struct in_addr		 addr;
613ada9d8fSnorby 
623ada9d8fSnorby 	/* clear SPF tree */
633ada9d8fSnorby 	spf_tree_clr(area);
643ada9d8fSnorby 	cand_list_clr();
653ada9d8fSnorby 
663ada9d8fSnorby 	/* initialize SPF tree */
67097ed198Sclaudio 	if ((v = spf_root = lsa_find_area(area, LSA_TYPE_ROUTER,
6806267228Sclaudio 	    rde_router_id(), rde_router_id())) == NULL) {
694f07e0d9Sclaudio 		/* empty area because no interface is active */
704f07e0d9Sclaudio 		return;
7106267228Sclaudio 	}
724f07e0d9Sclaudio 
735c6e55e9Snorby 	area->transit = 0;
743ada9d8fSnorby 	spf_root->cost = 0;
753ada9d8fSnorby 	w = NULL;
763ada9d8fSnorby 
7706267228Sclaudio 	/* make sure the spf root has a nexthop */
7806267228Sclaudio 	vertex_nexthop_clear(spf_root);
7906267228Sclaudio 	vertex_nexthop_add(spf_root, spf_root, 0);
8006267228Sclaudio 
813ada9d8fSnorby 	/* calculate SPF tree */
823ada9d8fSnorby 	do {
833ada9d8fSnorby 		/* loop links */
843ada9d8fSnorby 		for (i = 0; i < lsa_num_links(v); i++) {
853ada9d8fSnorby 			switch (v->type) {
863ada9d8fSnorby 			case LSA_TYPE_ROUTER:
873ada9d8fSnorby 				rtr_link = get_rtr_link(v, i);
883ada9d8fSnorby 				switch (rtr_link->type) {
893ada9d8fSnorby 				case LINK_TYPE_STUB_NET:
903ada9d8fSnorby 					/* skip */
913ada9d8fSnorby 					continue;
923ada9d8fSnorby 				case LINK_TYPE_POINTTOPOINT:
933ada9d8fSnorby 				case LINK_TYPE_VIRTUAL:
943ada9d8fSnorby 					/* find router LSA */
95097ed198Sclaudio 					w = lsa_find_area(area, LSA_TYPE_ROUTER,
963ada9d8fSnorby 					    rtr_link->id, rtr_link->id);
973ada9d8fSnorby 					break;
983ada9d8fSnorby 				case LINK_TYPE_TRANSIT_NET:
993ada9d8fSnorby 					/* find network LSA */
1003ada9d8fSnorby 					w = lsa_find_net(area, rtr_link->id);
1013ada9d8fSnorby 					break;
1023ada9d8fSnorby 				default:
1033ada9d8fSnorby 					fatalx("spf_calc: invalid link type");
1043ada9d8fSnorby 				}
1053ada9d8fSnorby 				break;
1063ada9d8fSnorby 			case LSA_TYPE_NETWORK:
1073ada9d8fSnorby 				net_link = get_net_link(v, i);
1083ada9d8fSnorby 				/* find router LSA */
109097ed198Sclaudio 				w = lsa_find_area(area, LSA_TYPE_ROUTER,
1103ada9d8fSnorby 				    net_link->att_rtr, net_link->att_rtr);
1113ada9d8fSnorby 				break;
1123ada9d8fSnorby 			default:
1133ada9d8fSnorby 				fatalx("spf_calc: invalid LSA type");
1143ada9d8fSnorby 			}
1153ada9d8fSnorby 
116bcaba526Snorby 			if (w == NULL)
1173ada9d8fSnorby 				continue;
1183ada9d8fSnorby 
1197ed8a196Sclaudio 			if (w->lsa->hdr.age == MAX_AGE)
1203ada9d8fSnorby 				continue;
1213ada9d8fSnorby 
1223ada9d8fSnorby 			if (!linked(w, v)) {
123cffa66bfSnorby 				addr.s_addr = htonl(w->ls_id);
124970128ccSclaudio 				log_debug("spf_calc: w id %s type %d has ",
125cffa66bfSnorby 				    inet_ntoa(addr), w->type);
126cffa66bfSnorby 				addr.s_addr = htonl(v->ls_id);
127970128ccSclaudio 				log_debug("    no link to v id %s type %d",
128cffa66bfSnorby 				    inet_ntoa(addr), v->type);
1293ada9d8fSnorby 				continue;
1303ada9d8fSnorby 			}
1311bd5f9d2Sclaudio 
1323ada9d8fSnorby 			if (v->type == LSA_TYPE_ROUTER)
1333ada9d8fSnorby 				d = v->cost + ntohs(rtr_link->metric);
1343ada9d8fSnorby 			else
1353ada9d8fSnorby 				d = v->cost;
1363ada9d8fSnorby 
1373ada9d8fSnorby 			if (cand_list_present(w)) {
1383ada9d8fSnorby 				if (d > w->cost)
1393ada9d8fSnorby 					continue;
1403ada9d8fSnorby 				if (d < w->cost) {
1413ada9d8fSnorby 					w->cost = d;
14272f25908Sclaudio 					vertex_nexthop_clear(w);
143d91e982dSclaudio 					calc_nexthop(w, v, area, rtr_link);
1445f836030Sclaudio 					/*
1455f836030Sclaudio 					 * need to readd to candidate list
1465f836030Sclaudio 					 * because the list is sorted
1475f836030Sclaudio 					 */
1485f836030Sclaudio 					TAILQ_REMOVE(&cand_list, w, cand);
1495f836030Sclaudio 					cand_list_add(w);
1507d1924b4Sclaudio 				} else
1517d1924b4Sclaudio 					/* equal cost path */
152d91e982dSclaudio 					calc_nexthop(w, v, area, rtr_link);
153970128ccSclaudio 			} else if (w->cost == LS_INFINITY && d < LS_INFINITY) {
1542c8d771bSclaudio 				w->cost = d;
15528ab0831Snorby 
15672f25908Sclaudio 				vertex_nexthop_clear(w);
157d91e982dSclaudio 				calc_nexthop(w, v, area, rtr_link);
1585f836030Sclaudio 				cand_list_add(w);
1593ada9d8fSnorby 			}
1603ada9d8fSnorby 		}
1613ada9d8fSnorby 
1623ada9d8fSnorby 		/* get next vertex */
1633ada9d8fSnorby 		v = cand_list_pop();
1643ada9d8fSnorby 		w = NULL;
1653ada9d8fSnorby 	} while (v != NULL);
1663ada9d8fSnorby 
16790625dffSnorby 	/* spf_dump(area); */
16806267228Sclaudio 	log_debug("spf_calc: area %s calculated", inet_ntoa(area->id));
16990625dffSnorby 
17090625dffSnorby 	area->num_spf_calc++;
17190625dffSnorby 	start_spf_timer();
17290625dffSnorby }
17390625dffSnorby 
17490625dffSnorby void
rt_calc(struct vertex * v,struct area * area,struct ospfd_conf * conf)1752d9ff8e6Sclaudio rt_calc(struct vertex *v, struct area *area, struct ospfd_conf *conf)
176fbc938d4Snorby {
177fbc938d4Snorby 	struct vertex		*w;
1787d1924b4Sclaudio 	struct v_nexthop	*vn;
179fbc938d4Snorby 	struct lsa_rtr_link	*rtr_link = NULL;
180fbc938d4Snorby 	int			 i;
181fbc938d4Snorby 	struct in_addr		 addr, adv_rtr;
182fbc938d4Snorby 
183fbc938d4Snorby 	lsa_age(v);
184fbc938d4Snorby 	if (ntohs(v->lsa->hdr.age) == MAX_AGE)
185fbc938d4Snorby 		return;
186fbc938d4Snorby 
187fbc938d4Snorby 	switch (v->type) {
188fbc938d4Snorby 	case LSA_TYPE_ROUTER:
189fbc938d4Snorby 		/* stub networks */
19006267228Sclaudio 		if (v->cost >= LS_INFINITY)
191fbc938d4Snorby 			return;
192fbc938d4Snorby 
193fbc938d4Snorby 		for (i = 0; i < lsa_num_links(v); i++) {
194fbc938d4Snorby 			rtr_link = get_rtr_link(v, i);
195fbc938d4Snorby 			if (rtr_link->type != LINK_TYPE_STUB_NET)
196fbc938d4Snorby 				continue;
197fbc938d4Snorby 
198aed65e40Sremi 			addr.s_addr = rtr_link->id & rtr_link->data;
199fbc938d4Snorby 			adv_rtr.s_addr = htonl(v->adv_rtr);
200fbc938d4Snorby 
201fbc938d4Snorby 			rt_update(addr, mask2prefixlen(rtr_link->data),
2026f151e9aSclaudio 			    &v->nexthop, v->type,
2036f151e9aSclaudio 			    v->cost + ntohs(rtr_link->metric), 0,
204fbc938d4Snorby 			    area->id, adv_rtr, PT_INTRA_AREA, DT_NET,
205fcb4545bSreyk 			    v->lsa->data.rtr.flags, 0);
206fbc938d4Snorby 		}
207fbc938d4Snorby 
208fbc938d4Snorby 		/* router, only add border and as-external routers */
209fbc938d4Snorby 		if ((v->lsa->data.rtr.flags & (OSPF_RTR_B | OSPF_RTR_E)) == 0)
210fbc938d4Snorby 			return;
211fbc938d4Snorby 
212fbc938d4Snorby 		addr.s_addr = htonl(v->ls_id);
213fbc938d4Snorby 		adv_rtr.s_addr = htonl(v->adv_rtr);
214fbc938d4Snorby 
2156f151e9aSclaudio 		rt_update(addr, 32, &v->nexthop, v->type, v->cost, 0, area->id,
216fcb4545bSreyk 		    adv_rtr, PT_INTRA_AREA, DT_RTR, v->lsa->data.rtr.flags, 0);
217fbc938d4Snorby 		break;
218fbc938d4Snorby 	case LSA_TYPE_NETWORK:
21906267228Sclaudio 		if (v->cost >= LS_INFINITY)
220fbc938d4Snorby 			return;
221fbc938d4Snorby 
222fbc938d4Snorby 		addr.s_addr = htonl(v->ls_id) & v->lsa->data.net.mask;
223fbc938d4Snorby 		adv_rtr.s_addr = htonl(v->adv_rtr);
224fbc938d4Snorby 		rt_update(addr, mask2prefixlen(v->lsa->data.net.mask),
2256f151e9aSclaudio 		    &v->nexthop, v->type, v->cost, 0, area->id, adv_rtr,
2266f151e9aSclaudio 		    PT_INTRA_AREA, DT_NET, 0, 0);
227fbc938d4Snorby 		break;
228fbc938d4Snorby 	case LSA_TYPE_SUM_NETWORK:
229fbc938d4Snorby 	case LSA_TYPE_SUM_ROUTER:
230fbc938d4Snorby 		/* if ABR only look at area 0.0.0.0 LSA */
2312d9ff8e6Sclaudio 		if (area_border_router(conf) && area->id.s_addr != INADDR_ANY)
2322d9ff8e6Sclaudio 			return;
233fbc938d4Snorby 
234fbc938d4Snorby 		/* ignore self-originated stuff */
2359bd8cd2aSclaudio 		if (v->self)
236fbc938d4Snorby 			return;
237fbc938d4Snorby 
238fbc938d4Snorby 		/* TODO type 3 area address range check */
239fbc938d4Snorby 
240097ed198Sclaudio 		if ((w = lsa_find_area(area, LSA_TYPE_ROUTER,
241fbc938d4Snorby 		    htonl(v->adv_rtr),
242fbc938d4Snorby 		    htonl(v->adv_rtr))) == NULL)
243fbc938d4Snorby 			return;
244fbc938d4Snorby 
2457d1924b4Sclaudio 		/* copy nexthops */
24672f25908Sclaudio 		vertex_nexthop_clear(v);	/* XXX needed ??? */
2477d1924b4Sclaudio 		TAILQ_FOREACH(vn, &w->nexthop, entry)
24872f25908Sclaudio 			vertex_nexthop_add(v, w, vn->nexthop.s_addr);
2497d1924b4Sclaudio 
250fbc938d4Snorby 		v->cost = w->cost +
251fbc938d4Snorby 		    (ntohl(v->lsa->data.sum.metric) & LSA_METRIC_MASK);
252fbc938d4Snorby 
25306267228Sclaudio 		if (v->cost >= LS_INFINITY)
254fbc938d4Snorby 			return;
255fbc938d4Snorby 
256fbc938d4Snorby 		adv_rtr.s_addr = htonl(v->adv_rtr);
257fbc938d4Snorby 		if (v->type == LSA_TYPE_SUM_NETWORK) {
258fbc938d4Snorby 			addr.s_addr = htonl(v->ls_id) & v->lsa->data.sum.mask;
259fbc938d4Snorby 			rt_update(addr, mask2prefixlen(v->lsa->data.sum.mask),
2606f151e9aSclaudio 			    &v->nexthop, v->type, v->cost, 0, area->id, adv_rtr,
261fcb4545bSreyk 			    PT_INTER_AREA, DT_NET, 0, 0);
262fbc938d4Snorby 		} else {
263fbc938d4Snorby 			addr.s_addr = htonl(v->ls_id);
2646f151e9aSclaudio 			rt_update(addr, 32, &v->nexthop, v->type, v->cost, 0,
2656f151e9aSclaudio 			    area->id, adv_rtr, PT_INTER_AREA, DT_RTR,
266fcb4545bSreyk 			    v->lsa->data.rtr.flags, 0);
267fbc938d4Snorby 		}
268fbc938d4Snorby 
269fbc938d4Snorby 		break;
270f87086e7Sclaudio 	case LSA_TYPE_AREA_OPAQ:
271f87086e7Sclaudio 		/* nothing to calculate */
272f87086e7Sclaudio 		break;
273fbc938d4Snorby 	default:
274fbc938d4Snorby 		/* as-external LSA are stored in a different tree */
275fbc938d4Snorby 		fatalx("rt_calc: invalid LSA type");
276fbc938d4Snorby 	}
277fbc938d4Snorby }
278fbc938d4Snorby 
279fbc938d4Snorby void
asext_calc(struct vertex * v)28090625dffSnorby asext_calc(struct vertex *v)
28190625dffSnorby {
28290625dffSnorby 	struct rt_node		*r;
2837d1924b4Sclaudio 	struct rt_nexthop	*rn;
28490625dffSnorby 	u_int32_t		 cost2;
28590625dffSnorby 	struct in_addr		 addr, adv_rtr, a;
286d8bef31fSnorby 	enum path_type		 type;
28790625dffSnorby 
28863f14b64Sclaudio 	lsa_age(v);
28990625dffSnorby 	if (ntohs(v->lsa->hdr.age) == MAX_AGE ||
29090625dffSnorby 	    (ntohl(v->lsa->data.asext.metric) & LSA_METRIC_MASK) >=
29190625dffSnorby 	    LS_INFINITY)
29290625dffSnorby 		return;
29363f14b64Sclaudio 
29463f14b64Sclaudio 	switch (v->type) {
2953ada9d8fSnorby 	case LSA_TYPE_EXTERNAL:
29663f14b64Sclaudio 		/* ignore self-originated stuff */
2979bd8cd2aSclaudio 		if (v->self)
29890625dffSnorby 			return;
29963f14b64Sclaudio 
30063f14b64Sclaudio 		if ((r = rt_lookup(DT_RTR, htonl(v->adv_rtr))) == NULL)
30190625dffSnorby 			return;
30263f14b64Sclaudio 
30390625dffSnorby 		/* XXX RFC1583Compatibility */
30463f14b64Sclaudio 		if (v->lsa->data.asext.fw_addr != 0 &&
305b1bc6696Snorby 		    (r = rt_lookup(DT_NET, v->lsa->data.asext.fw_addr)) == NULL)
30690625dffSnorby 			return;
30763f14b64Sclaudio 
30890625dffSnorby 		if (v->lsa->data.asext.fw_addr != 0 &&
30990625dffSnorby 		    r->p_type != PT_INTRA_AREA &&
3102c8d771bSclaudio 		    r->p_type != PT_INTER_AREA)
31190625dffSnorby 			return;
3122c8d771bSclaudio 
3137d1924b4Sclaudio 		if (ntohl(v->lsa->data.asext.metric) & LSA_ASEXT_E_FLAG) {
31463f14b64Sclaudio 			v->cost = r->cost;
3158ae50567Snorby 			cost2 = ntohl(v->lsa->data.asext.metric) &
3168ae50567Snorby 			    LSA_METRIC_MASK;
31763f14b64Sclaudio 			type = PT_TYPE2_EXT;
31863f14b64Sclaudio 		} else {
3197d1924b4Sclaudio 			v->cost = r->cost + (ntohl(v->lsa->data.asext.metric) &
32063f14b64Sclaudio 			     LSA_METRIC_MASK);
3218ae50567Snorby 			cost2 = 0;
32263f14b64Sclaudio 			type = PT_TYPE1_EXT;
32363f14b64Sclaudio 		}
32463f14b64Sclaudio 
32563f14b64Sclaudio 		a.s_addr = 0;
32663f14b64Sclaudio 		adv_rtr.s_addr = htonl(v->adv_rtr);
32763f14b64Sclaudio 		addr.s_addr = htonl(v->ls_id) & v->lsa->data.asext.mask;
3287d1924b4Sclaudio 
32972f25908Sclaudio 		vertex_nexthop_clear(v);
3307d1924b4Sclaudio 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
3317d1924b4Sclaudio 			if (rn->invalid)
3327d1924b4Sclaudio 				continue;
3337d1924b4Sclaudio 
3346f151e9aSclaudio 			/*
3356f151e9aSclaudio 			 * if a fw_addr is specified and the nexthop
3366f151e9aSclaudio 			 * is directly connected then it is possible to
3376f151e9aSclaudio 			 * send traffic directly to fw_addr.
3386f151e9aSclaudio 			 */
3396f151e9aSclaudio 			if (v->lsa->data.asext.fw_addr != 0 && rn->connected)
34072f25908Sclaudio 				vertex_nexthop_add(v, NULL,
3417d1924b4Sclaudio 				    v->lsa->data.asext.fw_addr);
3427d1924b4Sclaudio 			else
34372f25908Sclaudio 				vertex_nexthop_add(v, NULL, rn->nexthop.s_addr);
3447d1924b4Sclaudio 		}
3457d1924b4Sclaudio 
34663f14b64Sclaudio 		rt_update(addr, mask2prefixlen(v->lsa->data.asext.mask),
3476f151e9aSclaudio 		    &v->nexthop, v->type, v->cost, cost2, a, adv_rtr, type,
348fcb4545bSreyk 		    DT_NET, 0, ntohl(v->lsa->data.asext.ext_tag));
3493ada9d8fSnorby 		break;
350f87086e7Sclaudio 	case LSA_TYPE_AS_OPAQ:
351f87086e7Sclaudio 		/* nothing to calculate */
352f87086e7Sclaudio 		break;
3533ada9d8fSnorby 	default:
354af7d9427Snorby 		fatalx("asext_calc: invalid LSA type");
3553ada9d8fSnorby 	}
3563ada9d8fSnorby }
3573ada9d8fSnorby 
3583ada9d8fSnorby void
spf_tree_clr(struct area * area)3593ada9d8fSnorby spf_tree_clr(struct area *area)
3603ada9d8fSnorby {
3613ada9d8fSnorby 	struct lsa_tree	*tree = &area->lsa_tree;
3623ada9d8fSnorby 	struct vertex	*v;
3633ada9d8fSnorby 
3643ada9d8fSnorby 	RB_FOREACH(v, lsa_tree, tree) {
3653ada9d8fSnorby 		v->cost = LS_INFINITY;
36672f25908Sclaudio 		vertex_nexthop_clear(v);
3673ada9d8fSnorby 	}
3683ada9d8fSnorby }
3693ada9d8fSnorby 
3703ada9d8fSnorby void
calc_nexthop(struct vertex * dst,struct vertex * parent,struct area * area,struct lsa_rtr_link * rtr_link)371d91e982dSclaudio calc_nexthop(struct vertex *dst, struct vertex *parent,
372d91e982dSclaudio     struct area *area, struct lsa_rtr_link *rtr_link)
3733ada9d8fSnorby {
3747d1924b4Sclaudio 	struct v_nexthop	*vn;
375d91e982dSclaudio 	struct iface		*iface;
376ef209401Sremi 	struct rde_nbr		*nbr;
3773ada9d8fSnorby 	int			 i;
3783ada9d8fSnorby 
3793ada9d8fSnorby 	/* case 1 */
3803ada9d8fSnorby 	if (parent == spf_root) {
3813ada9d8fSnorby 		switch (dst->type) {
3823ada9d8fSnorby 		case LSA_TYPE_ROUTER:
383d91e982dSclaudio 			if (rtr_link->type != LINK_TYPE_POINTTOPOINT)
384d91e982dSclaudio 				fatalx("inconsistent SPF tree");
385d91e982dSclaudio 			LIST_FOREACH(iface, &area->iface_list, entry) {
386ef209401Sremi 				if (rtr_link->data != iface->addr.s_addr)
387ef209401Sremi 					continue;
388ef209401Sremi 				LIST_FOREACH(nbr, &area->nbr_list, entry) {
389ef209401Sremi 					if (nbr->ifindex == iface->ifindex) {
39072f25908Sclaudio 						vertex_nexthop_add(dst, parent,
391ef209401Sremi 						    nbr->addr.s_addr);
3925053f3c1Snorby 						return;
393d91e982dSclaudio 					}
394d91e982dSclaudio 				}
395ef209401Sremi 			}
396d91e982dSclaudio 			fatalx("no interface found for interface");
3973ada9d8fSnorby 		case LSA_TYPE_NETWORK:
398eb1522a1Snorby 			switch (rtr_link->type) {
399eb1522a1Snorby 			case LINK_TYPE_POINTTOPOINT:
400d91e982dSclaudio 			case LINK_TYPE_STUB_NET:
401eb1522a1Snorby 				/* ignore */
402eb1522a1Snorby 				break;
403eb1522a1Snorby 			case LINK_TYPE_TRANSIT_NET:
404eb1522a1Snorby 				if ((htonl(dst->ls_id) &
405eb1522a1Snorby 				    dst->lsa->data.net.mask) ==
406eb1522a1Snorby 				    (rtr_link->data &
407eb1522a1Snorby 				     dst->lsa->data.net.mask)) {
40872f25908Sclaudio 					vertex_nexthop_add(dst, parent,
4097d1924b4Sclaudio 					    rtr_link->data);
410eb1522a1Snorby 				}
411eb1522a1Snorby 				break;
412eb1522a1Snorby 			default:
4137d1924b4Sclaudio 				fatalx("calc_nexthop: invalid link "
414eb1522a1Snorby 				    "type");
415eb1522a1Snorby 			}
4165053f3c1Snorby 			return;
4173ada9d8fSnorby 		default:
4187d1924b4Sclaudio 			fatalx("calc_nexthop: invalid dst type");
4193ada9d8fSnorby 		}
420d91e982dSclaudio 		return;
4213ada9d8fSnorby 	}
4223ada9d8fSnorby 
4233ada9d8fSnorby 	/* case 2 */
4247d1924b4Sclaudio 	if (parent->type == LSA_TYPE_NETWORK && dst->type == LSA_TYPE_ROUTER) {
4257d1924b4Sclaudio 		TAILQ_FOREACH(vn, &parent->nexthop, entry) {
4267d1924b4Sclaudio 			if (vn->prev == spf_root) {
4273ada9d8fSnorby 				for (i = 0; i < lsa_num_links(dst); i++) {
4283ada9d8fSnorby 					rtr_link = get_rtr_link(dst, i);
4297d1924b4Sclaudio 					if ((rtr_link->type ==
4307d1924b4Sclaudio 					    LINK_TYPE_TRANSIT_NET) &&
4317d1924b4Sclaudio 					    (rtr_link->data &
4327d1924b4Sclaudio 					    parent->lsa->data.net.mask) ==
4337d1924b4Sclaudio 					    (htonl(parent->ls_id) &
4347d1924b4Sclaudio 					    parent->lsa->data.net.mask))
43572f25908Sclaudio 						vertex_nexthop_add(dst, parent,
4367d1924b4Sclaudio 						    rtr_link->data);
4373ada9d8fSnorby 				}
4387d1924b4Sclaudio 			} else {
43972f25908Sclaudio 				vertex_nexthop_add(dst, parent,
4407d1924b4Sclaudio 				    vn->nexthop.s_addr);
4417d1924b4Sclaudio 			}
4427d1924b4Sclaudio 		}
4433ada9d8fSnorby 		return;
4443ada9d8fSnorby 	}
4457d1924b4Sclaudio 
4463ada9d8fSnorby 	/* case 3 */
4477d1924b4Sclaudio 	TAILQ_FOREACH(vn, &parent->nexthop, entry)
44872f25908Sclaudio 		vertex_nexthop_add(dst, parent, vn->nexthop.s_addr);
4493ada9d8fSnorby }
4503ada9d8fSnorby 
4513ada9d8fSnorby /* candidate list */
4523ada9d8fSnorby void
cand_list_init(void)4533ada9d8fSnorby cand_list_init(void)
4543ada9d8fSnorby {
4553ada9d8fSnorby 	TAILQ_INIT(&cand_list);
4563ada9d8fSnorby }
4573ada9d8fSnorby 
4583ada9d8fSnorby void
cand_list_add(struct vertex * v)4593ada9d8fSnorby cand_list_add(struct vertex *v)
4603ada9d8fSnorby {
4613ada9d8fSnorby 	struct vertex	*c = NULL;
4623ada9d8fSnorby 
4633ada9d8fSnorby 	TAILQ_FOREACH(c, &cand_list, cand) {
4643ada9d8fSnorby 		if (c->cost > v->cost) {
4653ada9d8fSnorby 			TAILQ_INSERT_BEFORE(c, v, cand);
4663ada9d8fSnorby 			return;
4672c8d771bSclaudio 		} else if (c->cost == v->cost && c->type == LSA_TYPE_ROUTER &&
4682c8d771bSclaudio 		    v->type == LSA_TYPE_NETWORK) {
4692c8d771bSclaudio 			TAILQ_INSERT_BEFORE(c, v, cand);
4702c8d771bSclaudio 			return;
4713ada9d8fSnorby 		}
4723ada9d8fSnorby 	}
4733ada9d8fSnorby 	TAILQ_INSERT_TAIL(&cand_list, v, cand);
4743ada9d8fSnorby }
4753ada9d8fSnorby 
4763ada9d8fSnorby struct vertex *
cand_list_pop(void)4773ada9d8fSnorby cand_list_pop(void)
4783ada9d8fSnorby {
4793ada9d8fSnorby 	struct vertex	*c;
4803ada9d8fSnorby 
4813ada9d8fSnorby 	if ((c = TAILQ_FIRST(&cand_list)) != NULL) {
4823ada9d8fSnorby 		TAILQ_REMOVE(&cand_list, c, cand);
4833ada9d8fSnorby 	}
4843ada9d8fSnorby 
4853ada9d8fSnorby 	return (c);
4863ada9d8fSnorby }
4873ada9d8fSnorby 
4885c6e55e9Snorby int
cand_list_present(struct vertex * v)4893ada9d8fSnorby cand_list_present(struct vertex *v)
4903ada9d8fSnorby {
4913ada9d8fSnorby 	struct vertex	*c;
4923ada9d8fSnorby 
4933ada9d8fSnorby 	TAILQ_FOREACH(c, &cand_list, cand) {
4943ada9d8fSnorby 		if (c == v)
4955c6e55e9Snorby 			return (1);
4963ada9d8fSnorby 	}
4973ada9d8fSnorby 
4985c6e55e9Snorby 	return (0);
4993ada9d8fSnorby }
5003ada9d8fSnorby 
5013ada9d8fSnorby void
cand_list_clr(void)5023ada9d8fSnorby cand_list_clr(void)
5033ada9d8fSnorby {
5043ada9d8fSnorby 	struct vertex *c;
5053ada9d8fSnorby 
5063ada9d8fSnorby 	while ((c = TAILQ_FIRST(&cand_list)) != NULL) {
5073ada9d8fSnorby 		TAILQ_REMOVE(&cand_list, c, cand);
5083ada9d8fSnorby 	}
5093ada9d8fSnorby }
5103ada9d8fSnorby 
5113ada9d8fSnorby /* timers */
5123ada9d8fSnorby void
spf_timer(int fd,short event,void * arg)5133ada9d8fSnorby spf_timer(int fd, short event, void *arg)
5143ada9d8fSnorby {
51535b29713Snorby 	struct vertex		*v;
5163ada9d8fSnorby 	struct ospfd_conf	*conf = arg;
5173ada9d8fSnorby 	struct area		*area;
518eb1522a1Snorby 	struct rt_node		*r;
5193ada9d8fSnorby 
5203ada9d8fSnorby 	switch (conf->spf_state) {
5213ada9d8fSnorby 	case SPF_IDLE:
5223ada9d8fSnorby 		fatalx("spf_timer: invalid state IDLE");
5233ada9d8fSnorby 	case SPF_HOLDQUEUE:
5243ada9d8fSnorby 		conf->spf_state = SPF_DELAY;
5253ada9d8fSnorby 		/* FALLTHROUGH */
5263ada9d8fSnorby 	case SPF_DELAY:
52735b29713Snorby 		LIST_FOREACH(area, &conf->area_list, entry) {
528678f1ea5Snorby 			if (area->dirty) {
52982284507Sclaudio 				/* invalidate RIB entries of this area */
53082284507Sclaudio 				rt_invalidate(area);
53182284507Sclaudio 
53235b29713Snorby 				/* calculate SPF tree */
5333ada9d8fSnorby 				spf_calc(area);
534eb1522a1Snorby 
53535b29713Snorby 				/* calculate route table */
53635b29713Snorby 				RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
5372d9ff8e6Sclaudio 					rt_calc(v, area, conf);
53835b29713Snorby 				}
539678f1ea5Snorby 
540678f1ea5Snorby 				area->dirty = 0;
541678f1ea5Snorby 			}
54235b29713Snorby 		}
54335b29713Snorby 
54482284507Sclaudio 		/* calculate as-external routes, first invalidate them */
54582284507Sclaudio 		rt_invalidate(NULL);
546b200c123Sclaudio 		RB_FOREACH(v, lsa_tree, &asext_tree) {
54735b29713Snorby 			asext_calc(v);
54835b29713Snorby 		}
54935b29713Snorby 
550eb1522a1Snorby 		RB_FOREACH(r, rt_tree, &rt) {
5519ff4bef2Sclaudio 			LIST_FOREACH(area, &conf->area_list, entry)
5529ff4bef2Sclaudio 				rde_summary_update(r, area);
5539ff4bef2Sclaudio 
55437355230Snorby 			if (r->d_type != DT_NET)
55537355230Snorby 				continue;
55637355230Snorby 
557eb1522a1Snorby 			if (r->invalid)
558eb1522a1Snorby 				rde_send_delete_kroute(r);
559eb1522a1Snorby 			else
560eb1522a1Snorby 				rde_send_change_kroute(r);
561eb1522a1Snorby 		}
562eb1522a1Snorby 
5636fa28760Sclaudio 		LIST_FOREACH(area, &conf->area_list, entry) {
5646fa28760Sclaudio 			lsa_generate_stub_sums(area);
5652f560febSclaudio 			lsa_remove_invalid_sums(area);
5666fa28760Sclaudio 		}
5672f560febSclaudio 
568582957f9Sclaudio 		start_spf_holdtimer(conf);
5693ada9d8fSnorby 		break;
5703ada9d8fSnorby 	case SPF_HOLD:
5713ada9d8fSnorby 		conf->spf_state = SPF_IDLE;
5723ada9d8fSnorby 		break;
5733ada9d8fSnorby 	default:
5743ada9d8fSnorby 		fatalx("spf_timer: unknown state");
5753ada9d8fSnorby 	}
5763ada9d8fSnorby }
5773ada9d8fSnorby 
5783aede346Sclaudio void
start_spf_timer(void)579582957f9Sclaudio start_spf_timer(void)
5803ada9d8fSnorby {
5813ada9d8fSnorby 	struct timeval	tv;
5823ada9d8fSnorby 
583582957f9Sclaudio 	switch (rdeconf->spf_state) {
5843ada9d8fSnorby 	case SPF_IDLE:
5853ada9d8fSnorby 		timerclear(&tv);
58637877ca4Sdlg 		tv.tv_sec = rdeconf->spf_delay / 1000;
58737877ca4Sdlg 		tv.tv_usec = (rdeconf->spf_delay % 1000) * 1000;
588582957f9Sclaudio 		rdeconf->spf_state = SPF_DELAY;
5893aede346Sclaudio 		if (evtimer_add(&rdeconf->ev, &tv) == -1)
5903aede346Sclaudio 			fatal("start_spf_timer");
5913aede346Sclaudio 		break;
5923ada9d8fSnorby 	case SPF_DELAY:
5933ada9d8fSnorby 		/* ignore */
5943ada9d8fSnorby 		break;
5953ada9d8fSnorby 	case SPF_HOLD:
596582957f9Sclaudio 		rdeconf->spf_state = SPF_HOLDQUEUE;
5973ada9d8fSnorby 		break;
5983ada9d8fSnorby 	case SPF_HOLDQUEUE:
59937355230Snorby 		/* ignore */
6003ada9d8fSnorby 		break;
6013ada9d8fSnorby 	default:
6023ada9d8fSnorby 		fatalx("start_spf_timer: invalid spf_state");
6033ada9d8fSnorby 	}
6043ada9d8fSnorby }
6053ada9d8fSnorby 
6063aede346Sclaudio void
stop_spf_timer(struct ospfd_conf * conf)6073ada9d8fSnorby stop_spf_timer(struct ospfd_conf *conf)
6083ada9d8fSnorby {
6093aede346Sclaudio 	if (evtimer_del(&conf->ev) == -1)
6103aede346Sclaudio 		fatal("stop_spf_timer");
6113ada9d8fSnorby }
6123ada9d8fSnorby 
6133aede346Sclaudio void
start_spf_holdtimer(struct ospfd_conf * conf)6143ada9d8fSnorby start_spf_holdtimer(struct ospfd_conf *conf)
6153ada9d8fSnorby {
6163ada9d8fSnorby 	struct timeval	tv;
6173ada9d8fSnorby 
6183ada9d8fSnorby 	switch (conf->spf_state) {
6193ada9d8fSnorby 	case SPF_DELAY:
6203ada9d8fSnorby 		timerclear(&tv);
62137877ca4Sdlg 		tv.tv_sec = rdeconf->spf_hold_time / 1000;
62237877ca4Sdlg 		tv.tv_usec = (rdeconf->spf_hold_time % 1000) * 1000;
6233ada9d8fSnorby 		conf->spf_state = SPF_HOLD;
6243aede346Sclaudio 		if (evtimer_add(&conf->ev, &tv) == -1)
6253aede346Sclaudio 			fatal("start_spf_holdtimer");
6263aede346Sclaudio 		break;
6273ada9d8fSnorby 	case SPF_IDLE:
6283ada9d8fSnorby 	case SPF_HOLD:
6293ada9d8fSnorby 	case SPF_HOLDQUEUE:
6303ada9d8fSnorby 		fatalx("start_spf_holdtimer: invalid state");
6313ada9d8fSnorby 	default:
63298c612a2Snorby 		fatalx("start_spf_holdtimer: unknown state");
6333ada9d8fSnorby 	}
6343ada9d8fSnorby }
6353ada9d8fSnorby 
6363ada9d8fSnorby /* route table */
6373ada9d8fSnorby void
rt_init(void)6383ada9d8fSnorby rt_init(void)
6393ada9d8fSnorby {
6403ada9d8fSnorby 	RB_INIT(&rt);
6413ada9d8fSnorby }
6423ada9d8fSnorby 
6433ada9d8fSnorby int
rt_compare(struct rt_node * a,struct rt_node * b)6443ada9d8fSnorby rt_compare(struct rt_node *a, struct rt_node *b)
6453ada9d8fSnorby {
6463ada9d8fSnorby 	if (ntohl(a->prefix.s_addr) < ntohl(b->prefix.s_addr))
6473ada9d8fSnorby 		return (-1);
6483ada9d8fSnorby 	if (ntohl(a->prefix.s_addr) > ntohl(b->prefix.s_addr))
6493ada9d8fSnorby 		return (1);
6503ada9d8fSnorby 	if (a->prefixlen < b->prefixlen)
6513ada9d8fSnorby 		return (-1);
6523ada9d8fSnorby 	if (a->prefixlen > b->prefixlen)
6533ada9d8fSnorby 		return (1);
654f8d9331dSclaudio 	if (a->d_type > b->d_type)
655f8d9331dSclaudio 		return (-1);
656f8d9331dSclaudio 	if (a->d_type < b->d_type)
657f8d9331dSclaudio 		return (1);
6583ada9d8fSnorby 	return (0);
6593ada9d8fSnorby }
6603ada9d8fSnorby 
6613ada9d8fSnorby struct rt_node *
rt_find(in_addr_t prefix,u_int8_t prefixlen,enum dst_type d_type)662f8d9331dSclaudio rt_find(in_addr_t prefix, u_int8_t prefixlen, enum dst_type d_type)
6633ada9d8fSnorby {
6643ada9d8fSnorby 	struct rt_node	s;
6653ada9d8fSnorby 
6663ada9d8fSnorby 	s.prefix.s_addr = prefix;
6673ada9d8fSnorby 	s.prefixlen = prefixlen;
668f8d9331dSclaudio 	s.d_type = d_type;
6693ada9d8fSnorby 
6703ada9d8fSnorby 	return (RB_FIND(rt_tree, &rt, &s));
6713ada9d8fSnorby }
6723ada9d8fSnorby 
6733ada9d8fSnorby int
rt_insert(struct rt_node * r)6743ada9d8fSnorby rt_insert(struct rt_node *r)
6753ada9d8fSnorby {
6763ada9d8fSnorby 	if (RB_INSERT(rt_tree, &rt, r) != NULL) {
6773ada9d8fSnorby 		log_warnx("rt_insert failed for %s/%u",
6783ada9d8fSnorby 		    inet_ntoa(r->prefix), r->prefixlen);
6793ada9d8fSnorby 		free(r);
6803ada9d8fSnorby 		return (-1);
6813ada9d8fSnorby 	}
6823ada9d8fSnorby 
6833ada9d8fSnorby 	return (0);
6843ada9d8fSnorby }
6853ada9d8fSnorby 
6863ada9d8fSnorby int
rt_remove(struct rt_node * r)6873ada9d8fSnorby rt_remove(struct rt_node *r)
6883ada9d8fSnorby {
6893ada9d8fSnorby 	if (RB_REMOVE(rt_tree, &rt, r) == NULL) {
6903ada9d8fSnorby 		log_warnx("rt_remove failed for %s/%u",
6913ada9d8fSnorby 		    inet_ntoa(r->prefix), r->prefixlen);
6923ada9d8fSnorby 		return (-1);
6933ada9d8fSnorby 	}
6943ada9d8fSnorby 
6957d1924b4Sclaudio 	rt_nexthop_clear(r);
6963ada9d8fSnorby 	free(r);
6973ada9d8fSnorby 	return (0);
6983ada9d8fSnorby }
6993ada9d8fSnorby 
7003ada9d8fSnorby void
rt_invalidate(struct area * area)70182284507Sclaudio rt_invalidate(struct area *area)
702eb1522a1Snorby {
7038fd251b3Sclaudio 	struct rt_node		*r, *nr;
7047d1924b4Sclaudio 	struct rt_nexthop	*rn, *nrn;
705eb1522a1Snorby 
7068fd251b3Sclaudio 	for (r = RB_MIN(rt_tree, &rt); r != NULL; r = nr) {
7078fd251b3Sclaudio 		nr = RB_NEXT(rt_tree, &rt, r);
70882284507Sclaudio 		if (area == NULL) {
70982284507Sclaudio 			/* look only at as_ext routes */
71082284507Sclaudio 			if (r->p_type != PT_TYPE1_EXT &&
71182284507Sclaudio 			    r->p_type != PT_TYPE2_EXT)
71282284507Sclaudio 				continue;
71382284507Sclaudio 		} else {
714675ce83aSclaudio 			/* ignore all as_ext routes */
715675ce83aSclaudio 			if (r->p_type == PT_TYPE1_EXT ||
716675ce83aSclaudio 			    r->p_type == PT_TYPE2_EXT)
717675ce83aSclaudio 				continue;
718675ce83aSclaudio 
71982284507Sclaudio 			/* look only at routes matching the area */
72082284507Sclaudio 			if (r->area.s_addr != area->id.s_addr)
72182284507Sclaudio 				continue;
72282284507Sclaudio 		}
7235c6e55e9Snorby 		r->invalid = 1;
7247d1924b4Sclaudio 		for (rn = TAILQ_FIRST(&r->nexthop); rn != NULL; rn = nrn) {
7257d1924b4Sclaudio 			nrn = TAILQ_NEXT(rn, entry);
7267d1924b4Sclaudio 			if (rn->invalid) {
7277d1924b4Sclaudio 				TAILQ_REMOVE(&r->nexthop, rn, entry);
7287d1924b4Sclaudio 				free(rn);
7297d1924b4Sclaudio 			} else
7307d1924b4Sclaudio 				rn->invalid = 1;
7317d1924b4Sclaudio 		}
7327d1924b4Sclaudio 		if (TAILQ_EMPTY(&r->nexthop))
7337d1924b4Sclaudio 			rt_remove(r);
7347d1924b4Sclaudio 	}
7357d1924b4Sclaudio }
7367d1924b4Sclaudio 
7377d1924b4Sclaudio void
rt_nexthop_clear(struct rt_node * r)7387d1924b4Sclaudio rt_nexthop_clear(struct rt_node *r)
7397d1924b4Sclaudio {
7407d1924b4Sclaudio 	struct rt_nexthop	*rn;
7417d1924b4Sclaudio 
7427d1924b4Sclaudio 	while ((rn = TAILQ_FIRST(&r->nexthop)) != NULL) {
7437d1924b4Sclaudio 		TAILQ_REMOVE(&r->nexthop, rn, entry);
7447d1924b4Sclaudio 		free(rn);
7457d1924b4Sclaudio 	}
7467d1924b4Sclaudio }
7477d1924b4Sclaudio 
7487d1924b4Sclaudio void
rt_nexthop_add(struct rt_node * r,struct v_nexthead * vnh,u_int8_t type,struct in_addr adv_rtr)7496f151e9aSclaudio rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh, u_int8_t type,
7507d1924b4Sclaudio     struct in_addr adv_rtr)
7517d1924b4Sclaudio {
7527d1924b4Sclaudio 	struct v_nexthop	*vn;
7537d1924b4Sclaudio 	struct rt_nexthop	*rn;
7547d1924b4Sclaudio 	struct timespec		 now;
7557d1924b4Sclaudio 
7567d1924b4Sclaudio 	TAILQ_FOREACH(vn, vnh, entry) {
7577d1924b4Sclaudio 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
7587d1924b4Sclaudio 			if (rn->nexthop.s_addr != vn->nexthop.s_addr)
7597d1924b4Sclaudio 				continue;
7607d1924b4Sclaudio 
7617d1924b4Sclaudio 			rn->adv_rtr.s_addr = adv_rtr.s_addr;
7626f151e9aSclaudio 			rn->connected = (type == LSA_TYPE_NETWORK &&
76306267228Sclaudio 			    vn->prev == spf_root) || (vn->nexthop.s_addr == 0);
7647d1924b4Sclaudio 			rn->invalid = 0;
7657d1924b4Sclaudio 
7667d1924b4Sclaudio 			r->invalid = 0;
7673654546cSclaudio 			break;
7687d1924b4Sclaudio 		}
7693654546cSclaudio 		if (rn)
7703654546cSclaudio 			continue;
7717d1924b4Sclaudio 
7727d1924b4Sclaudio 		if ((rn = calloc(1, sizeof(struct rt_nexthop))) == NULL)
7737d1924b4Sclaudio 			fatal("rt_nexthop_add");
7747d1924b4Sclaudio 
7757d1924b4Sclaudio 		clock_gettime(CLOCK_MONOTONIC, &now);
7767d1924b4Sclaudio 		rn->nexthop.s_addr = vn->nexthop.s_addr;
7777d1924b4Sclaudio 		rn->adv_rtr.s_addr = adv_rtr.s_addr;
7787d1924b4Sclaudio 		rn->uptime = now.tv_sec;
7796f151e9aSclaudio 		rn->connected = (type == LSA_TYPE_NETWORK &&
78006267228Sclaudio 		    vn->prev == spf_root) || (vn->nexthop.s_addr == 0);
7817d1924b4Sclaudio 		rn->invalid = 0;
7827d1924b4Sclaudio 
7837d1924b4Sclaudio 		r->invalid = 0;
7847d1924b4Sclaudio 		TAILQ_INSERT_TAIL(&r->nexthop, rn, entry);
785eb1522a1Snorby 	}
7868fd251b3Sclaudio }
787eb1522a1Snorby 
788eb1522a1Snorby void
rt_clear(void)7893ada9d8fSnorby rt_clear(void)
7903ada9d8fSnorby {
7913ada9d8fSnorby 	struct rt_node	*r;
7923ada9d8fSnorby 
7933ada9d8fSnorby 	while ((r = RB_MIN(rt_tree, &rt)) != NULL)
7943ada9d8fSnorby 		rt_remove(r);
7953ada9d8fSnorby }
7963ada9d8fSnorby 
7973ada9d8fSnorby void
rt_dump(struct in_addr area,pid_t pid,u_int8_t r_type)79837355230Snorby rt_dump(struct in_addr area, pid_t pid, u_int8_t r_type)
79937355230Snorby {
80037355230Snorby 	static struct ctl_rt	 rtctl;
8017aa44f3cSnorby 	struct timespec		 now;
80237355230Snorby 	struct rt_node		*r;
8037d1924b4Sclaudio 	struct rt_nexthop	*rn;
80437355230Snorby 
8057aa44f3cSnorby 	clock_gettime(CLOCK_MONOTONIC, &now);
8067aa44f3cSnorby 
80737355230Snorby 	RB_FOREACH(r, rt_tree, &rt) {
8081f637b31Sclaudio 		if (r->invalid)
8091f637b31Sclaudio 			continue;
8101f637b31Sclaudio 
81137355230Snorby 		if (r->area.s_addr != area.s_addr)
81237355230Snorby 			continue;
81337355230Snorby 
81437355230Snorby 		switch (r_type) {
81537355230Snorby 		case RIB_RTR:
81637355230Snorby 			if (r->d_type != DT_RTR)
81737355230Snorby 				continue;
81837355230Snorby 			break;
81937355230Snorby 		case RIB_NET:
82037355230Snorby 			if (r->d_type != DT_NET)
82137355230Snorby 				continue;
8228ae50567Snorby 			if (r->p_type == PT_TYPE1_EXT ||
8238ae50567Snorby 			    r->p_type == PT_TYPE2_EXT)
8248ae50567Snorby 				continue;
82537355230Snorby 			break;
82637355230Snorby 		case RIB_EXT:
8278ae50567Snorby 			if (r->p_type != PT_TYPE1_EXT &&
82837355230Snorby 			    r->p_type != PT_TYPE2_EXT)
82937355230Snorby 				continue;
83037355230Snorby 			break;
83137355230Snorby 		default:
83237355230Snorby 			fatalx("rt_dump: invalid RIB type");
83337355230Snorby 		}
83437355230Snorby 
83506267228Sclaudio 		bzero(&rtctl, sizeof(rtctl));
83637355230Snorby 		rtctl.prefix.s_addr = r->prefix.s_addr;
83737355230Snorby 		rtctl.area.s_addr = r->area.s_addr;
83837355230Snorby 		rtctl.cost = r->cost;
8398ae50567Snorby 		rtctl.cost2 = r->cost2;
84037355230Snorby 		rtctl.p_type = r->p_type;
84137355230Snorby 		rtctl.d_type = r->d_type;
8428ae50567Snorby 		rtctl.flags = r->flags;
84337355230Snorby 		rtctl.prefixlen = r->prefixlen;
84406267228Sclaudio 
84506267228Sclaudio 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
84606267228Sclaudio 			if (rn->invalid)
84706267228Sclaudio 				continue;
84806267228Sclaudio 
84906267228Sclaudio 			rtctl.connected = rn->connected;
85006267228Sclaudio 			rtctl.nexthop.s_addr = rn->nexthop.s_addr;
85106267228Sclaudio 			rtctl.adv_rtr.s_addr = rn->adv_rtr.s_addr;
8527d1924b4Sclaudio 			rtctl.uptime = now.tv_sec - rn->uptime;
85337355230Snorby 
8547d1924b4Sclaudio 			rde_imsg_compose_ospfe(IMSG_CTL_SHOW_RIB, 0, pid,
8557d1924b4Sclaudio 			    &rtctl, sizeof(rtctl));
8567d1924b4Sclaudio 		}
85737355230Snorby 	}
85837355230Snorby }
85937355230Snorby 
86037355230Snorby void
rt_update(struct in_addr prefix,u_int8_t prefixlen,struct v_nexthead * vnh,u_int8_t v_type,u_int32_t cost,u_int32_t cost2,struct in_addr area,struct in_addr adv_rtr,enum path_type p_type,enum dst_type d_type,u_int8_t flags,u_int32_t tag)8617d1924b4Sclaudio rt_update(struct in_addr prefix, u_int8_t prefixlen, struct v_nexthead *vnh,
8626f151e9aSclaudio      u_int8_t v_type, u_int32_t cost, u_int32_t cost2, struct in_addr area,
863f8d9331dSclaudio      struct in_addr adv_rtr, enum path_type p_type, enum dst_type d_type,
864fcb4545bSreyk      u_int8_t flags, u_int32_t tag)
8653ada9d8fSnorby {
8663ada9d8fSnorby 	struct rt_node		*rte;
8673654546cSclaudio 	struct rt_nexthop	*rn;
8687d1924b4Sclaudio 	int			 better = 0, equal = 0;
8693ada9d8fSnorby 
870f8d9331dSclaudio 	if ((rte = rt_find(prefix.s_addr, prefixlen, d_type)) == NULL) {
8713ada9d8fSnorby 		if ((rte = calloc(1, sizeof(struct rt_node))) == NULL)
872d71bb90fSclaudio 			fatal("rt_update");
8737d1924b4Sclaudio 
8747d1924b4Sclaudio 		TAILQ_INIT(&rte->nexthop);
8753ada9d8fSnorby 		rte->prefix.s_addr = prefix.s_addr;
8763ada9d8fSnorby 		rte->prefixlen = prefixlen;
8773ada9d8fSnorby 		rte->cost = cost;
8788ae50567Snorby 		rte->cost2 = cost2;
879eb1522a1Snorby 		rte->area = area;
8803ada9d8fSnorby 		rte->p_type = p_type;
881eb1522a1Snorby 		rte->d_type = d_type;
8828ae50567Snorby 		rte->flags = flags;
883fcb4545bSreyk 		rte->ext_tag = tag;
8847d1924b4Sclaudio 
8856f151e9aSclaudio 		rt_nexthop_add(rte, vnh, v_type, adv_rtr);
8863ada9d8fSnorby 
8873ada9d8fSnorby 		rt_insert(rte);
8883ada9d8fSnorby 	} else {
8892c8d771bSclaudio 		/* order:
8902c8d771bSclaudio 		 * 1. intra-area
8912c8d771bSclaudio 		 * 2. inter-area
8922c8d771bSclaudio 		 * 3. type 1 as ext
8932c8d771bSclaudio 		 * 4. type 2 as ext
8942c8d771bSclaudio 		 */
8957d1924b4Sclaudio 		if (rte->invalid)	/* everything is better than invalid */
8967d1924b4Sclaudio 			better = 1;
8977d1924b4Sclaudio 		else if (p_type < rte->p_type)
8982c8d771bSclaudio 			better = 1;
8992c8d771bSclaudio 		else if (p_type == rte->p_type)
9002c8d771bSclaudio 			switch (p_type) {
9012c8d771bSclaudio 			case PT_INTRA_AREA:
9022c8d771bSclaudio 			case PT_INTER_AREA:
9032c8d771bSclaudio 				if (cost < rte->cost)
9042c8d771bSclaudio 					better = 1;
9057d1924b4Sclaudio 				else if (cost == rte->cost &&
9067d1924b4Sclaudio 				    rte->area.s_addr == area.s_addr)
9077d1924b4Sclaudio 					equal = 1;
9082c8d771bSclaudio 				break;
9092c8d771bSclaudio 			case PT_TYPE1_EXT:
9102c8d771bSclaudio 				/* XXX rfc1583 compat */
9112c8d771bSclaudio 				if (cost < rte->cost)
9122c8d771bSclaudio 					better = 1;
9137d1924b4Sclaudio 				else if (cost == rte->cost)
9147d1924b4Sclaudio 					equal = 1;
9152c8d771bSclaudio 				break;
9162c8d771bSclaudio 			case PT_TYPE2_EXT:
9172c8d771bSclaudio 				if (cost2 < rte->cost2)
9182c8d771bSclaudio 					better = 1;
9192c8d771bSclaudio 				/* XXX rfc1583 compat */
9202c8d771bSclaudio 				else if (cost2 == rte->cost2 &&
9212c8d771bSclaudio 				    cost < rte->cost)
9222c8d771bSclaudio 					better = 1;
9237d1924b4Sclaudio 				else if (cost2 == rte->cost2 &&
9247d1924b4Sclaudio 				    cost == rte->cost)
9257d1924b4Sclaudio 					equal = 1;
9262c8d771bSclaudio 				break;
9272c8d771bSclaudio 			}
9287d1924b4Sclaudio 
9292c8d771bSclaudio 		if (better) {
9303654546cSclaudio 			TAILQ_FOREACH(rn, &rte->nexthop, entry)
9313654546cSclaudio 				rn->invalid = 1;
9327d1924b4Sclaudio 
9337d1924b4Sclaudio 			rte->area = area;
9343ada9d8fSnorby 			rte->cost = cost;
9358ae50567Snorby 			rte->cost2 = cost2;
9363ada9d8fSnorby 			rte->p_type = p_type;
9378ae50567Snorby 			rte->flags = flags;
938fcb4545bSreyk 			rte->ext_tag = tag;
939eb1522a1Snorby 		}
9407d1924b4Sclaudio 
9417d1924b4Sclaudio 		if (equal || better)
9426f151e9aSclaudio 			rt_nexthop_add(rte, vnh, v_type, adv_rtr);
9433ada9d8fSnorby 	}
9443ada9d8fSnorby }
9453ada9d8fSnorby 
94663f14b64Sclaudio struct rt_node *
rt_lookup(enum dst_type type,in_addr_t addr)947f8d9331dSclaudio rt_lookup(enum dst_type type, in_addr_t addr)
94863f14b64Sclaudio {
94963f14b64Sclaudio 	struct rt_node	*rn;
95063f14b64Sclaudio 	u_int8_t	 i = 32;
95163f14b64Sclaudio 
9521f637b31Sclaudio 	if (type == DT_RTR) {
9531f637b31Sclaudio 		rn = rt_find(addr, 32, type);
9541f637b31Sclaudio 		if (rn && rn->invalid == 0)
9551f637b31Sclaudio 			return (rn);
9561f637b31Sclaudio 		return (NULL);
9571f637b31Sclaudio 	}
95863f14b64Sclaudio 
959f8d9331dSclaudio 	/* type == DT_NET */
96063f14b64Sclaudio 	do {
9611f637b31Sclaudio 		if ((rn = rt_find(addr & prefixlen2mask(i), i, type)) &&
9621f637b31Sclaudio 		    rn->invalid == 0)
96363f14b64Sclaudio 			return (rn);
96463f14b64Sclaudio 	} while (i-- != 0);
96563f14b64Sclaudio 
96663f14b64Sclaudio 	return (NULL);
96763f14b64Sclaudio }
96863f14b64Sclaudio 
9693ada9d8fSnorby /* router LSA links */
9703ada9d8fSnorby struct lsa_rtr_link *
get_rtr_link(struct vertex * v,int idx)9713ada9d8fSnorby get_rtr_link(struct vertex *v, int idx)
9723ada9d8fSnorby {
9733ada9d8fSnorby 	struct lsa_rtr_link	*rtr_link = NULL;
9743ada9d8fSnorby 	char			*buf = (char *)v->lsa;
9753ada9d8fSnorby 	u_int16_t		 i, off, nlinks;
9763ada9d8fSnorby 
9773ada9d8fSnorby 	if (v->type != LSA_TYPE_ROUTER)
9783ada9d8fSnorby 		fatalx("get_rtr_link: invalid LSA type");
9793ada9d8fSnorby 
9803ada9d8fSnorby 	off = sizeof(v->lsa->hdr) + sizeof(struct lsa_rtr);
9813ada9d8fSnorby 
982eb1522a1Snorby 	/* nlinks validated earlier by lsa_check() */
9833ada9d8fSnorby 	nlinks = lsa_num_links(v);
9843ada9d8fSnorby 	for (i = 0; i < nlinks; i++) {
9853ada9d8fSnorby 		rtr_link = (struct lsa_rtr_link *)(buf + off);
9863ada9d8fSnorby 		if (i == idx)
9873ada9d8fSnorby 			return (rtr_link);
9883ada9d8fSnorby 
9893ada9d8fSnorby 		off += sizeof(struct lsa_rtr_link) +
9903ada9d8fSnorby 		    rtr_link->num_tos * sizeof(u_int32_t);
9913ada9d8fSnorby 	}
9923ada9d8fSnorby 
993a4bfea9eSclaudio 	fatalx("get_rtr_link: index not found");
9943ada9d8fSnorby }
9953ada9d8fSnorby 
9963ada9d8fSnorby /* network LSA links */
9973ada9d8fSnorby struct lsa_net_link *
get_net_link(struct vertex * v,int idx)9983ada9d8fSnorby get_net_link(struct vertex *v, int idx)
9993ada9d8fSnorby {
10003ada9d8fSnorby 	struct lsa_net_link	*net_link = NULL;
10013ada9d8fSnorby 	char			*buf = (char *)v->lsa;
10023ada9d8fSnorby 	u_int16_t		 i, off, nlinks;
10033ada9d8fSnorby 
10043ada9d8fSnorby 	if (v->type != LSA_TYPE_NETWORK)
10053ada9d8fSnorby 		fatalx("get_net_link: invalid LSA type");
10063ada9d8fSnorby 
10073ada9d8fSnorby 	off = sizeof(v->lsa->hdr) + sizeof(u_int32_t);
10083ada9d8fSnorby 
1009eb1522a1Snorby 	/* nlinks validated earlier by lsa_check() */
10103ada9d8fSnorby 	nlinks = lsa_num_links(v);
10113ada9d8fSnorby 	for (i = 0; i < nlinks; i++) {
10123ada9d8fSnorby 		net_link = (struct lsa_net_link *)(buf + off);
10133ada9d8fSnorby 		if (i == idx)
10143ada9d8fSnorby 			return (net_link);
10153ada9d8fSnorby 
10163ada9d8fSnorby 		off += sizeof(struct lsa_net_link);
10173ada9d8fSnorby 	}
10183ada9d8fSnorby 
1019a4bfea9eSclaudio 	fatalx("get_net_link: index not found");
10203ada9d8fSnorby }
10213ada9d8fSnorby 
10223ada9d8fSnorby /* misc */
10235c6e55e9Snorby int
linked(struct vertex * w,struct vertex * v)10243ada9d8fSnorby linked(struct vertex *w, struct vertex *v)
10253ada9d8fSnorby {
10263ada9d8fSnorby 	struct lsa_rtr_link	*rtr_link = NULL;
10273ada9d8fSnorby 	struct lsa_net_link	*net_link = NULL;
10283ada9d8fSnorby 	int			 i;
10293ada9d8fSnorby 
10303ada9d8fSnorby 	switch (w->type) {
10313ada9d8fSnorby 	case LSA_TYPE_ROUTER:
10323ada9d8fSnorby 		for (i = 0; i < lsa_num_links(w); i++) {
10333ada9d8fSnorby 			rtr_link = get_rtr_link(w, i);
10343ada9d8fSnorby 			switch (v->type) {
10353ada9d8fSnorby 			case LSA_TYPE_ROUTER:
10363ada9d8fSnorby 				if (rtr_link->type == LINK_TYPE_POINTTOPOINT &&
10373ada9d8fSnorby 				    rtr_link->id == htonl(v->ls_id))
10385c6e55e9Snorby 					return (1);
10393ada9d8fSnorby 				break;
10403ada9d8fSnorby 			case LSA_TYPE_NETWORK:
10413ada9d8fSnorby 				if (rtr_link->id == htonl(v->ls_id))
10425c6e55e9Snorby 					return (1);
10433ada9d8fSnorby 				break;
10443ada9d8fSnorby 			default:
10456e97b0ccSclaudio 				fatalx("linked: invalid type");
10463ada9d8fSnorby 			}
10473ada9d8fSnorby 		}
10485c6e55e9Snorby 		return (0);
10493ada9d8fSnorby 	case LSA_TYPE_NETWORK:
10503ada9d8fSnorby 		for (i = 0; i < lsa_num_links(w); i++) {
10513ada9d8fSnorby 			net_link = get_net_link(w, i);
10523ada9d8fSnorby 			switch (v->type) {
10533ada9d8fSnorby 			case LSA_TYPE_ROUTER:
10543ada9d8fSnorby 				if (net_link->att_rtr == htonl(v->ls_id))
10555c6e55e9Snorby 					return (1);
10563ada9d8fSnorby 				break;
10573ada9d8fSnorby 			default:
10586e97b0ccSclaudio 				fatalx("linked: invalid type");
10593ada9d8fSnorby 			}
10603ada9d8fSnorby 		}
10615c6e55e9Snorby 		return (0);
10623ada9d8fSnorby 	default:
10636e97b0ccSclaudio 		fatalx("linked: invalid LSA type");
10643ada9d8fSnorby 	}
10653ada9d8fSnorby 
10665c6e55e9Snorby 	return (0);
10673ada9d8fSnorby }
1068