xref: /openbsd-src/usr.sbin/ospf6d/rde_spf.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: rde_spf.c,v 1.14 2009/04/09 19:06:52 stsp Exp $ */
2 
3 /*
4  * Copyright (c) 2005 Esben Norby <norby@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <err.h>
24 #include <stdlib.h>
25 #include <strings.h>
26 
27 #include "ospf6d.h"
28 #include "ospf6.h"
29 #include "log.h"
30 #include "rde.h"
31 
32 extern struct ospfd_conf	*rdeconf;
33 TAILQ_HEAD(, vertex)		 cand_list;
34 RB_HEAD(rt_tree, rt_node)	 rt;
35 RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare)
36 RB_GENERATE(rt_tree, rt_node, entry, rt_compare)
37 struct vertex			*spf_root = NULL;
38 
39 void		 calc_nexthop_clear(struct vertex *);
40 void		 calc_nexthop_add(struct vertex *, struct vertex *,
41 		     const struct in6_addr *, u_int32_t);
42 struct in6_addr	*calc_nexthop_lladdr(struct vertex *, struct lsa_rtr_link *);
43 void		 calc_nexthop_transit_nbr(struct vertex *, struct vertex *,
44 		     u_int32_t ifindex);
45 void		 calc_nexthop(struct vertex *, struct vertex *,
46 		     struct area *, struct lsa_rtr_link *);
47 void		 rt_nexthop_clear(struct rt_node *);
48 void		 rt_nexthop_add(struct rt_node *, struct v_nexthead *,
49 		     struct in_addr);
50 void		 rt_update(struct in6_addr *, u_int8_t, struct v_nexthead *,
51 		     u_int32_t, u_int32_t, struct in_addr, struct in_addr,
52 		     enum path_type, enum dst_type, u_int8_t, u_int32_t);
53 struct rt_node	*rt_lookup(enum dst_type, struct in6_addr *);
54 void		 rt_invalidate(struct area *);
55 int		 linked(struct vertex *, struct vertex *);
56 
57 void
58 spf_calc(struct area *area)
59 {
60 	struct vertex		*v, *w;
61 	struct lsa_rtr_link	*rtr_link = NULL;
62 	struct lsa_net_link	*net_link;
63 	u_int32_t		 d;
64 	unsigned int		 i;
65 
66 	/* clear SPF tree */
67 	spf_tree_clr(area);
68 	cand_list_clr();
69 
70 	/* initialize SPF tree */
71 	if ((v = spf_root = lsa_find_rtr(area, rde_router_id())) == NULL)
72 		/* empty area because no interface is active */
73 		return;
74 
75 	area->transit = 0;
76 	spf_root->cost = 0;
77 	w = NULL;
78 
79 	/* calculate SPF tree */
80 	do {
81 		/* TODO: Treat multiple router LSAs originated by a single
82 		 * router as one aggregate. We don't do this [yet],
83 		 * but RFC5340 says we MUST do it. */
84 
85 		/* loop links */
86 		for (i = 0; i < lsa_num_links(v); i++) {
87 			switch (v->type) {
88 			case LSA_TYPE_ROUTER:
89 				rtr_link = get_rtr_link(v, i);
90 				switch (rtr_link->type) {
91 				case LINK_TYPE_POINTTOPOINT:
92 				case LINK_TYPE_VIRTUAL:
93 					/* find router LSA */
94 					w = lsa_find_rtr(area,
95 					    rtr_link->nbr_rtr_id);
96 					break;
97 				case LINK_TYPE_TRANSIT_NET:
98 					/* find network LSA */
99 					w = lsa_find_tree(&area->lsa_tree,
100 					    htons(LSA_TYPE_NETWORK),
101 					    rtr_link->nbr_iface_id,
102 					    rtr_link->nbr_rtr_id);
103 					break;
104 				default:
105 					fatalx("spf_calc: invalid link type");
106 				}
107 				break;
108 			case LSA_TYPE_NETWORK:
109 				net_link = get_net_link(v, i);
110 				/* find router LSA */
111 				w = lsa_find_rtr(area, net_link->att_rtr);
112 				break;
113 			default:
114 				fatalx("spf_calc: invalid LSA type");
115 			}
116 
117 			if (w == NULL)
118 				continue;
119 
120 			if (ntohs(w->lsa->hdr.age) == MAX_AGE)
121 				continue;
122 
123 			if (lsa_num_links(w) == 0)
124 				continue;
125 
126 			if (!linked(w, v)) {
127 				log_debug("spf_calc: w adv_rtr %s ls_id %s "
128 				    "type 0x%x numlinks %hu has no link to "
129 				    "v adv_rtr %s ls_id %s type 0x%x",
130 				    log_rtr_id(htonl(w->adv_rtr)),
131 				    log_rtr_id(htonl(w->ls_id)), w->type,
132 				    lsa_num_links(w),
133 				    log_rtr_id(htonl(v->adv_rtr)),
134 				    log_rtr_id(htonl(v->ls_id)), v->type);
135 				continue;
136 			}
137 
138 			if (v->type == LSA_TYPE_ROUTER)
139 				d = v->cost + ntohs(rtr_link->metric);
140 			else
141 				d = v->cost;
142 
143 			if (cand_list_present(w)) {
144 				if (d > w->cost)
145 					continue;
146 				if (d < w->cost) {
147 					w->cost = d;
148 					calc_nexthop_clear(w);
149 					calc_nexthop(w, v, area, rtr_link);
150 					/*
151 					 * need to readd to candidate list
152 					 * because the list is sorted
153 					 */
154 					TAILQ_REMOVE(&cand_list, w, cand);
155 					cand_list_add(w);
156 				} else
157 					/* equal cost path */
158 					calc_nexthop(w, v, area, rtr_link);
159 			} else if (w->cost == LS_INFINITY && d < LS_INFINITY) {
160 				w->cost = d;
161 
162 				calc_nexthop_clear(w);
163 				calc_nexthop(w, v, area, rtr_link);
164 				cand_list_add(w);
165 			}
166 		}
167 
168 		/* get next vertex */
169 		v = cand_list_pop();
170 		w = NULL;
171 	} while (v != NULL);
172 
173 	/* spf_dump(area); */
174 	log_debug("spf_calc: area %s calculated", inet_ntoa(area->id));
175 
176 	/* Dump SPF tree to log */
177 	RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
178 		struct v_nexthop *vn;
179 		char hops[4096];
180 		struct iface *iface;
181 
182 		bzero(hops, sizeof(hops));
183 
184 		if (v->type != LSA_TYPE_ROUTER && v->type != LSA_TYPE_NETWORK)
185 			continue;
186 
187 		TAILQ_FOREACH(vn, &v->nexthop, entry) {
188 			strlcat(hops, log_in6addr(&vn->nexthop), sizeof(hops));
189 			strlcat(hops, "%", sizeof(hops));
190 			if ((iface = if_find(vn->ifindex)) == NULL)
191 				fatalx("spf_calc: lost iface");
192 			strlcat(hops, iface->name, sizeof(hops));
193 			if (vn != TAILQ_LAST(&v->nexthop, v_nexthead))
194 				strlcat(hops, ", ", sizeof(hops));
195 		}
196 		log_debug("%s(%s, 0x%x, %s) cost %u has nexthops [%s]",
197 		    v == spf_root ? "*" : " ", log_rtr_id(htonl(v->adv_rtr)),
198 		    v->type, log_rtr_id(htonl(v->ls_id)), v->cost, hops);
199 	}
200 
201 	area->num_spf_calc++;
202 	start_spf_timer();
203 }
204 
205 void
206 rt_calc(struct vertex *v, struct area *area, struct ospfd_conf *conf)
207 {
208 	struct vertex		*w;
209 	struct lsa_intra_prefix	*iap;
210 	struct lsa_prefix	*prefix;
211 	struct in_addr		 adv_rtr;
212 	struct in6_addr		 ia6;
213 	u_int16_t		 i, off;
214 	u_int8_t		 flags;
215 
216 	lsa_age(v);
217 	if (ntohs(v->lsa->hdr.age) == MAX_AGE)
218 		return;
219 
220 	switch (v->type) {
221 	case LSA_TYPE_INTRA_A_PREFIX:
222 		/* Find referenced LSA */
223 		iap = &v->lsa->data.pref_intra;
224 		switch (ntohs(iap->ref_type)) {
225 		case LSA_TYPE_ROUTER:
226 			w = lsa_find_rtr(area, iap->ref_adv_rtr);
227 			if (w == NULL) {
228 				warnx("rt_calc: Intra-Area-Prefix LSA (%s, %u) "
229 				    "references non-existent router %s",
230 				    log_rtr_id(htonl(v->adv_rtr)),
231 				    v->ls_id, log_rtr_id(iap->ref_adv_rtr));
232 				return;
233 			}
234 			flags = LSA_24_GETHI(w->lsa->data.rtr.opts);
235 			break;
236 		case LSA_TYPE_NETWORK:
237 			w = lsa_find_tree(&area->lsa_tree, iap->ref_type,
238 			    iap->ref_ls_id, iap->ref_adv_rtr);
239 			if (w == NULL) {
240 				warnx("rt_calc: Intra-Area-Prefix LSA (%s, %u) "
241 				    "references non-existent Network LSA (%s, "
242 				    "%u)", log_rtr_id(htonl(v->adv_rtr)),
243 				    v->ls_id, log_rtr_id(iap->ref_adv_rtr),
244 				    ntohl(iap->ref_ls_id));
245 				return;
246 			}
247 			flags = 0;
248 			break;
249 		default:
250 			warnx("rt_calc: Intra-Area-Prefix LSA (%s, %u) has "
251 			    "invalid ref_type 0x%hx", log_rtr_id(v->adv_rtr),
252 			    v->ls_id, ntohs(iap->ref_type));
253 			return;
254 		}
255 
256 		if (w->cost >= LS_INFINITY || TAILQ_EMPTY(&w->nexthop))
257 			return;
258 
259 		/* Add prefixes listed in Intra-Area-Prefix LSA to routing
260 		 * table, using w as destination. */
261 		off = sizeof(v->lsa->hdr) + sizeof(struct lsa_intra_prefix);
262 		for (i = 0; i < ntohs(v->lsa->data.pref_intra.numprefix); i++) {
263 			prefix = (struct lsa_prefix *)((char *)(v->lsa) + off);
264 			if (!(prefix->options & OSPF_PREFIX_NU)) {
265 				bzero(&ia6, sizeof(ia6));
266 				bcopy(prefix + 1, &ia6,
267 				    LSA_PREFIXSIZE(prefix->prefixlen));
268 
269 				adv_rtr.s_addr = htonl(w->adv_rtr);
270 
271 				rt_update(&ia6, prefix->prefixlen, &w->nexthop,
272 				    w->cost + ntohs(prefix->metric), 0,
273 				    area->id, adv_rtr, PT_INTRA_AREA, DT_NET,
274 				    flags, 0);
275 			}
276 			off += sizeof(struct lsa_prefix)
277 			    + LSA_PREFIXSIZE(prefix->prefixlen);
278 		}
279 	/* TODO: inter-area routes, as-external routes */
280 	default:
281 		break;
282 	}
283 }
284 
285 void
286 asext_calc(struct vertex *v)
287 {
288 #if 0
289 	struct rt_node		*r;
290 	struct rt_nexthop	*rn;
291 	u_int32_t		 cost2;
292 	struct in_addr		 addr, adv_rtr, a;
293 	enum path_type		 type;
294 #endif
295 
296 	lsa_age(v);
297 	if (ntohs(v->lsa->hdr.age) == MAX_AGE ||
298 	    (ntohl(v->lsa->data.asext.metric) & LSA_METRIC_MASK) >=
299 	    LS_INFINITY)
300 		return;
301 
302 	switch (v->type) {
303 	case LSA_TYPE_EXTERNAL:
304 		/* ignore self-originated stuff */
305 		if (v->self)
306 			return;
307 
308 #if 0 /* XXX this will be different for sure */
309 		if ((r = rt_lookup(DT_RTR, htonl(v->adv_rtr))) == NULL)
310 			return;
311 
312 		/* XXX RFC1583Compatibility */
313 		if (v->lsa->data.asext.fw_addr != 0 &&
314 		    (r = rt_lookup(DT_NET, v->lsa->data.asext.fw_addr)) == NULL)
315 			return;
316 
317 		if (v->lsa->data.asext.fw_addr != 0 &&
318 		    r->p_type != PT_INTRA_AREA &&
319 		    r->p_type != PT_INTER_AREA)
320 			return;
321 
322 		if (ntohl(v->lsa->data.asext.metric) & LSA_ASEXT_E_FLAG) {
323 			v->cost = r->cost;
324 			cost2 = ntohl(v->lsa->data.asext.metric) &
325 			    LSA_METRIC_MASK;
326 			type = PT_TYPE2_EXT;
327 		} else {
328 			v->cost = r->cost + (ntohl(v->lsa->data.asext.metric) &
329 			     LSA_METRIC_MASK);
330 			cost2 = 0;
331 			type = PT_TYPE1_EXT;
332 		}
333 
334 		a.s_addr = 0;
335 		adv_rtr.s_addr = htonl(v->adv_rtr);
336 		addr.s_addr = htonl(v->ls_id) & v->lsa->data.asext.mask;
337 
338 		calc_nexthop_clear(v);
339 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
340 			if (rn->invalid)
341 				continue;
342 
343 			if (rn->connected && r->d_type == DT_NET) {
344 				if (v->lsa->data.asext.fw_addr != 0)
345 					calc_nexthop_add(v, NULL,
346 					    v->lsa->data.asext.fw_addr);
347 				else
348 					calc_nexthop_add(v, NULL,
349 					    htonl(v->adv_rtr));
350 			} else
351 				calc_nexthop_add(v, NULL, 0
352 				    /* XXX rn->nexthop.s_addri */);
353 		}
354 
355 		rt_update(addr, mask2prefixlen(v->lsa->data.asext.mask),
356 		    &v->nexthop, v->cost, cost2, a, adv_rtr, type,
357 		    DT_NET, 0, ntohl(v->lsa->data.asext.ext_tag));
358 #endif
359 		break;
360 	default:
361 		fatalx("asext_calc: invalid LSA type");
362 	}
363 }
364 
365 void
366 spf_tree_clr(struct area *area)
367 {
368 	struct lsa_tree	*tree = &area->lsa_tree;
369 	struct vertex	*v;
370 
371 	RB_FOREACH(v, lsa_tree, tree) {
372 		v->cost = LS_INFINITY;
373 		calc_nexthop_clear(v);
374 	}
375 }
376 
377 void
378 calc_nexthop_clear(struct vertex *v)
379 {
380 	struct v_nexthop	*vn;
381 
382 	while ((vn = TAILQ_FIRST(&v->nexthop))) {
383 		TAILQ_REMOVE(&v->nexthop, vn, entry);
384 		free(vn);
385 	}
386 }
387 
388 void
389 calc_nexthop_add(struct vertex *dst, struct vertex *parent,
390 	const struct in6_addr *nexthop, u_int32_t ifindex)
391 {
392 	struct v_nexthop	*vn;
393 
394 	if ((vn = calloc(1, sizeof(*vn))) == NULL)
395 		fatal("calc_nexthop_add");
396 
397 	vn->prev = parent;
398 	if (nexthop)
399 		vn->nexthop = *nexthop;
400 	vn->ifindex = ifindex;
401 
402 	TAILQ_INSERT_TAIL(&dst->nexthop, vn, entry);
403 }
404 
405 struct in6_addr *
406 calc_nexthop_lladdr(struct vertex *dst, struct lsa_rtr_link *rtr_link)
407 {
408 	struct iface	*iface;
409 	struct vertex	*link;
410 	struct rde_nbr	*nbr;
411 
412 	/* Find outgoing interface, we need its LSA tree */
413 	LIST_FOREACH(iface, &dst->area->iface_list, entry) {
414 		if (ntohl(rtr_link->iface_id) == iface->ifindex)
415 			break;
416 	}
417 	if (!iface) {
418 		warnx("calc_nexthop_lladdr: no interface found for ifindex");
419 		return (NULL);
420 	}
421 
422 	/* Determine neighbor's link-local address.
423 	 * Try to get it from link LSA first. */
424 	link = lsa_find_tree(&iface->lsa_tree,
425 		htons(LSA_TYPE_LINK), rtr_link->nbr_iface_id,
426 		htonl(dst->adv_rtr));
427 	if (link)
428 		return &link->lsa->data.link.lladdr;
429 
430 	/* Not found, so fall back to source address
431 	 * advertised in hello packet. */
432 	if ((nbr = rde_nbr_find(dst->peerid)) == NULL)
433 		fatalx("next hop is not a neighbor");
434 	return &nbr->addr;
435 }
436 
437 void
438 calc_nexthop_transit_nbr(struct vertex *dst, struct vertex *parent,
439     u_int32_t ifindex)
440 {
441 	struct lsa_rtr_link	*rtr_link;
442 	unsigned int		 i;
443 	struct in6_addr		*lladdr;
444 
445 	if (dst->type != LSA_TYPE_ROUTER)
446 		fatalx("calc_nexthop_transit_nbr: dst is not a router");
447 	if (parent->type != LSA_TYPE_NETWORK)
448 		fatalx("calc_nexthop_transit_nbr: parent is not a network");
449 
450 	/* dst is a neighbor on a directly attached transit network.
451 	 * Figure out dst's link local address and add it as nexthop. */
452 	for (i = 0; i < lsa_num_links(dst); i++) {
453 		rtr_link = get_rtr_link(dst, i);
454 		if (rtr_link->type == LINK_TYPE_TRANSIT_NET &&
455 		    rtr_link->nbr_rtr_id == parent->lsa->hdr.adv_rtr &&
456 		    rtr_link->nbr_iface_id == parent->lsa->hdr.ls_id) {
457 			lladdr = calc_nexthop_lladdr(dst, rtr_link);
458 			calc_nexthop_add(dst, parent, lladdr, ifindex);
459 		}
460 	}
461 }
462 
463 void
464 calc_nexthop(struct vertex *dst, struct vertex *parent,
465     struct area *area, struct lsa_rtr_link *rtr_link)
466 {
467 	struct v_nexthop	*vn;
468 	struct in6_addr		*nexthop;
469 
470 	/* case 1 */
471 	if (parent == spf_root) {
472 		switch (dst->type) {
473 		case LSA_TYPE_ROUTER:
474 			if (rtr_link->type != LINK_TYPE_POINTTOPOINT)
475 				fatalx("inconsistent SPF tree");
476 			nexthop = calc_nexthop_lladdr(dst, rtr_link);
477 			break;
478 		case LSA_TYPE_NETWORK:
479 			if (rtr_link->type != LINK_TYPE_TRANSIT_NET)
480 				fatalx("inconsistent SPF tree");
481 
482 			/* Next hop address cannot be determined yet,
483 			 * we only know the outgoing interface. */
484 			nexthop = NULL;
485 			break;
486 		default:
487 			fatalx("calc_nexthop: invalid dst type");
488 		}
489 
490 		calc_nexthop_add(dst, spf_root, nexthop,
491 		    ntohl(rtr_link->iface_id));
492 		return;
493 	}
494 
495 	/* case 2 */
496 	if (parent->type == LSA_TYPE_NETWORK && dst->type == LSA_TYPE_ROUTER) {
497 		TAILQ_FOREACH(vn, &parent->nexthop, entry) {
498 			if (vn->prev == spf_root)
499 				calc_nexthop_transit_nbr(dst, parent,
500 				    vn->ifindex);
501 			else
502 				/* dst is more than one transit net away */
503 				calc_nexthop_add(dst, parent, &vn->nexthop,
504 				    vn->ifindex);
505 		}
506 		return;
507 	}
508 
509 	/* case 3 */
510 	TAILQ_FOREACH(vn, &parent->nexthop, entry)
511 	    calc_nexthop_add(dst, parent, &vn->nexthop, vn->ifindex);
512 }
513 
514 /* candidate list */
515 void
516 cand_list_init(void)
517 {
518 	TAILQ_INIT(&cand_list);
519 }
520 
521 void
522 cand_list_add(struct vertex *v)
523 {
524 	struct vertex	*c = NULL;
525 
526 	TAILQ_FOREACH(c, &cand_list, cand) {
527 		if (c->cost > v->cost) {
528 			TAILQ_INSERT_BEFORE(c, v, cand);
529 			return;
530 		} else if (c->cost == v->cost && c->type == LSA_TYPE_ROUTER &&
531 		    v->type == LSA_TYPE_NETWORK) {
532 			TAILQ_INSERT_BEFORE(c, v, cand);
533 			return;
534 		}
535 	}
536 	TAILQ_INSERT_TAIL(&cand_list, v, cand);
537 }
538 
539 struct vertex *
540 cand_list_pop(void)
541 {
542 	struct vertex	*c;
543 
544 	if ((c = TAILQ_FIRST(&cand_list)) != NULL) {
545 		TAILQ_REMOVE(&cand_list, c, cand);
546 	}
547 
548 	return (c);
549 }
550 
551 int
552 cand_list_present(struct vertex *v)
553 {
554 	struct vertex	*c;
555 
556 	TAILQ_FOREACH(c, &cand_list, cand) {
557 		if (c == v)
558 			return (1);
559 	}
560 
561 	return (0);
562 }
563 
564 void
565 cand_list_clr(void)
566 {
567 	struct vertex *c;
568 
569 	while ((c = TAILQ_FIRST(&cand_list)) != NULL) {
570 		TAILQ_REMOVE(&cand_list, c, cand);
571 	}
572 }
573 
574 /* timers */
575 /* ARGSUSED */
576 void
577 spf_timer(int fd, short event, void *arg)
578 {
579 	struct vertex		*v;
580 	struct ospfd_conf	*conf = arg;
581 	struct area		*area;
582 	struct rt_node		*r;
583 
584 	switch (conf->spf_state) {
585 	case SPF_IDLE:
586 		fatalx("spf_timer: invalid state IDLE");
587 	case SPF_HOLDQUEUE:
588 		conf->spf_state = SPF_DELAY;
589 		/* FALLTHROUGH */
590 	case SPF_DELAY:
591 		LIST_FOREACH(area, &conf->area_list, entry) {
592 			if (area->dirty) {
593 				/* invalidate RIB entries of this area */
594 				rt_invalidate(area);
595 
596 				/* calculate SPF tree */
597 				spf_calc(area);
598 
599 				/* calculate route table */
600 				RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
601 					rt_calc(v, area, conf);
602 				}
603 
604 				area->dirty = 0;
605 			}
606 		}
607 
608 		/* calculate as-external routes, first invalidate them */
609 		rt_invalidate(NULL);
610 		RB_FOREACH(v, lsa_tree, &asext_tree) {
611 			asext_calc(v);
612 		}
613 
614 		RB_FOREACH(r, rt_tree, &rt) {
615 			LIST_FOREACH(area, &conf->area_list, entry)
616 				rde_summary_update(r, area);
617 
618 			if (r->d_type != DT_NET)
619 				continue;
620 
621 			if (r->invalid)
622 				rde_send_delete_kroute(r);
623 			else
624 				rde_send_change_kroute(r);
625 		}
626 
627 		LIST_FOREACH(area, &conf->area_list, entry)
628 			lsa_remove_invalid_sums(area);
629 
630 		start_spf_holdtimer(conf);
631 		break;
632 	case SPF_HOLD:
633 		conf->spf_state = SPF_IDLE;
634 		break;
635 	default:
636 		fatalx("spf_timer: unknown state");
637 	}
638 }
639 
640 void
641 start_spf_timer(void)
642 {
643 	struct timeval	tv;
644 
645 	switch (rdeconf->spf_state) {
646 	case SPF_IDLE:
647 		timerclear(&tv);
648 		tv.tv_sec = rdeconf->spf_delay;
649 		rdeconf->spf_state = SPF_DELAY;
650 		if (evtimer_add(&rdeconf->ev, &tv) == -1)
651 			fatal("start_spf_timer");
652 		break;
653 	case SPF_DELAY:
654 		/* ignore */
655 		break;
656 	case SPF_HOLD:
657 		rdeconf->spf_state = SPF_HOLDQUEUE;
658 		break;
659 	case SPF_HOLDQUEUE:
660 		/* ignore */
661 		break;
662 	default:
663 		fatalx("start_spf_timer: invalid spf_state");
664 	}
665 }
666 
667 void
668 stop_spf_timer(struct ospfd_conf *conf)
669 {
670 	if (evtimer_del(&conf->ev) == -1)
671 		fatal("stop_spf_timer");
672 }
673 
674 void
675 start_spf_holdtimer(struct ospfd_conf *conf)
676 {
677 	struct timeval	tv;
678 
679 	switch (conf->spf_state) {
680 	case SPF_DELAY:
681 		timerclear(&tv);
682 		tv.tv_sec = conf->spf_hold_time;
683 		conf->spf_state = SPF_HOLD;
684 		if (evtimer_add(&conf->ev, &tv) == -1)
685 			fatal("start_spf_holdtimer");
686 		break;
687 	case SPF_IDLE:
688 	case SPF_HOLD:
689 	case SPF_HOLDQUEUE:
690 		fatalx("start_spf_holdtimer: invalid state");
691 	default:
692 		fatalx("start_spf_holdtimer: unknown state");
693 	}
694 }
695 
696 /* route table */
697 void
698 rt_init(void)
699 {
700 	RB_INIT(&rt);
701 }
702 
703 int
704 rt_compare(struct rt_node *a, struct rt_node *b)
705 {
706 	int	i;
707 
708 	/* XXX maybe a & b need to be switched */
709 	i = memcmp(&a->prefix, &b->prefix, sizeof(a->prefix));
710 	if (i)
711 		return (i);
712 	if (a->prefixlen < b->prefixlen)
713 		return (-1);
714 	if (a->prefixlen > b->prefixlen)
715 		return (1);
716 	if (a->d_type > b->d_type)
717 		return (-1);
718 	if (a->d_type < b->d_type)
719 		return (1);
720 	return (0);
721 }
722 
723 struct rt_node *
724 rt_find(struct in6_addr *prefix, u_int8_t prefixlen, enum dst_type d_type)
725 {
726 	struct rt_node	s;
727 
728 	s.prefix = *prefix;
729 	s.prefixlen = prefixlen;
730 	s.d_type = d_type;
731 
732 	return (RB_FIND(rt_tree, &rt, &s));
733 }
734 
735 int
736 rt_insert(struct rt_node *r)
737 {
738 	if (RB_INSERT(rt_tree, &rt, r) != NULL) {
739 		log_warnx("rt_insert failed for %s/%u",
740 		    log_in6addr(&r->prefix), r->prefixlen);
741 		free(r);
742 		return (-1);
743 	}
744 
745 	return (0);
746 }
747 
748 int
749 rt_remove(struct rt_node *r)
750 {
751 	if (RB_REMOVE(rt_tree, &rt, r) == NULL) {
752 		log_warnx("rt_remove failed for %s/%u",
753 		    log_in6addr(&r->prefix), r->prefixlen);
754 		return (-1);
755 	}
756 
757 	rt_nexthop_clear(r);
758 	free(r);
759 	return (0);
760 }
761 
762 void
763 rt_invalidate(struct area *area)
764 {
765 	struct rt_node		*r, *nr;
766 	struct rt_nexthop	*rn, *nrn;
767 
768 	for (r = RB_MIN(rt_tree, &rt); r != NULL; r = nr) {
769 		nr = RB_NEXT(rt_tree, &rt, r);
770 		if (area == NULL) {
771 			/* look only at as_ext routes */
772 			if (r->p_type != PT_TYPE1_EXT &&
773 			    r->p_type != PT_TYPE2_EXT)
774 				continue;
775 		} else {
776 			/* ignore all as_ext routes */
777 			if (r->p_type == PT_TYPE1_EXT ||
778 			    r->p_type == PT_TYPE2_EXT)
779 				continue;
780 
781 			/* look only at routes matching the area */
782 			if (r->area.s_addr != area->id.s_addr)
783 				continue;
784 		}
785 		r->invalid = 1;
786 		for (rn = TAILQ_FIRST(&r->nexthop); rn != NULL; rn = nrn) {
787 			nrn = TAILQ_NEXT(rn, entry);
788 			if (rn->invalid) {
789 				TAILQ_REMOVE(&r->nexthop, rn, entry);
790 				free(rn);
791 			} else
792 				rn->invalid = 1;
793 		}
794 		if (TAILQ_EMPTY(&r->nexthop))
795 			rt_remove(r);
796 	}
797 }
798 
799 void
800 rt_nexthop_clear(struct rt_node *r)
801 {
802 	struct rt_nexthop	*rn;
803 
804 	while ((rn = TAILQ_FIRST(&r->nexthop)) != NULL) {
805 		TAILQ_REMOVE(&r->nexthop, rn, entry);
806 		free(rn);
807 	}
808 }
809 
810 void
811 rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh,
812     struct in_addr adv_rtr)
813 {
814 	struct v_nexthop	*vn;
815 	struct rt_nexthop	*rn;
816 	struct timespec		 now;
817 
818 	TAILQ_FOREACH(vn, vnh, entry) {
819 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
820 			if (!IN6_ARE_ADDR_EQUAL(&rn->nexthop, &vn->nexthop))
821 				continue;
822 
823 			rn->adv_rtr.s_addr = adv_rtr.s_addr;
824 			rn->connected = vn->prev == spf_root;
825 			rn->invalid = 0;
826 
827 			r->invalid = 0;
828 			break;
829 		}
830 		if (rn)
831 			continue;
832 
833 		if ((rn = calloc(1, sizeof(struct rt_nexthop))) == NULL)
834 			fatal("rt_nexthop_add");
835 
836 		clock_gettime(CLOCK_MONOTONIC, &now);
837 		rn->nexthop = vn->nexthop;
838 		rn->adv_rtr.s_addr = adv_rtr.s_addr;
839 		rn->uptime = now.tv_sec;
840 		rn->connected = vn->prev == spf_root;
841 		rn->invalid = 0;
842 
843 		r->invalid = 0;
844 		TAILQ_INSERT_TAIL(&r->nexthop, rn, entry);
845 	}
846 }
847 
848 void
849 rt_clear(void)
850 {
851 	struct rt_node	*r;
852 
853 	while ((r = RB_MIN(rt_tree, &rt)) != NULL)
854 		rt_remove(r);
855 }
856 
857 void
858 rt_dump(struct in_addr area, pid_t pid, u_int8_t r_type)
859 {
860 	static struct ctl_rt	 rtctl;
861 	struct timespec		 now;
862 	struct rt_node		*r;
863 	struct rt_nexthop	*rn;
864 
865 	clock_gettime(CLOCK_MONOTONIC, &now);
866 
867 	RB_FOREACH(r, rt_tree, &rt) {
868 		if (r->invalid)
869 			continue;
870 
871 		if (r->area.s_addr != area.s_addr)
872 			continue;
873 
874 		switch (r_type) {
875 		case RIB_RTR:
876 			if (r->d_type != DT_RTR)
877 				continue;
878 			break;
879 		case RIB_NET:
880 			if (r->d_type != DT_NET)
881 				continue;
882 			if (r->p_type == PT_TYPE1_EXT ||
883 			    r->p_type == PT_TYPE2_EXT)
884 				continue;
885 			break;
886 		case RIB_EXT:
887 			if (r->p_type != PT_TYPE1_EXT &&
888 			    r->p_type != PT_TYPE2_EXT)
889 				continue;
890 			break;
891 		default:
892 			fatalx("rt_dump: invalid RIB type");
893 		}
894 
895 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
896 			if (rn->invalid)
897 				continue;
898 
899 			rtctl.prefix = r->prefix;
900 			rtctl.nexthop = rn->nexthop;
901 			rtctl.area.s_addr = r->area.s_addr;
902 			rtctl.adv_rtr.s_addr = rn->adv_rtr.s_addr;
903 			rtctl.cost = r->cost;
904 			rtctl.cost2 = r->cost2;
905 			rtctl.p_type = r->p_type;
906 			rtctl.d_type = r->d_type;
907 			rtctl.flags = r->flags;
908 			rtctl.prefixlen = r->prefixlen;
909 			rtctl.uptime = now.tv_sec - rn->uptime;
910 
911 			rde_imsg_compose_ospfe(IMSG_CTL_SHOW_RIB, 0, pid,
912 			    &rtctl, sizeof(rtctl));
913 		}
914 	}
915 }
916 
917 void
918 rt_update(struct in6_addr *prefix, u_int8_t prefixlen, struct v_nexthead *vnh,
919      u_int32_t cost, u_int32_t cost2, struct in_addr area,
920      struct in_addr adv_rtr, enum path_type p_type, enum dst_type d_type,
921      u_int8_t flags, u_int32_t tag)
922 {
923 	struct rt_node		*rte;
924 	struct rt_nexthop	*rn;
925 	int			 better = 0, equal = 0;
926 
927 	if (vnh == NULL || TAILQ_EMPTY(vnh))	/* XXX remove */
928 		fatalx("rt_update: invalid nexthop");
929 
930 	if ((rte = rt_find(prefix, prefixlen, d_type)) == NULL) {
931 		if ((rte = calloc(1, sizeof(struct rt_node))) == NULL)
932 			fatal("rt_update");
933 
934 		TAILQ_INIT(&rte->nexthop);
935 		rte->prefix = *prefix;
936 		rte->prefixlen = prefixlen;
937 		rte->cost = cost;
938 		rte->cost2 = cost2;
939 		rte->area = area;
940 		rte->p_type = p_type;
941 		rte->d_type = d_type;
942 		rte->flags = flags;
943 		rte->ext_tag = tag;
944 
945 		rt_nexthop_add(rte, vnh, adv_rtr);
946 
947 		rt_insert(rte);
948 	} else {
949 		/* order:
950 		 * 1. intra-area
951 		 * 2. inter-area
952 		 * 3. type 1 as ext
953 		 * 4. type 2 as ext
954 		 */
955 		if (rte->invalid)	/* everything is better than invalid */
956 			better = 1;
957 		else if (p_type < rte->p_type)
958 			better = 1;
959 		else if (p_type == rte->p_type)
960 			switch (p_type) {
961 			case PT_INTRA_AREA:
962 			case PT_INTER_AREA:
963 				if (cost < rte->cost)
964 					better = 1;
965 				else if (cost == rte->cost &&
966 				    rte->area.s_addr == area.s_addr)
967 					equal = 1;
968 				break;
969 			case PT_TYPE1_EXT:
970 				if (cost < rte->cost)
971 					better = 1;
972 				else if (cost == rte->cost)
973 					equal = 1;
974 				break;
975 			case PT_TYPE2_EXT:
976 				if (cost2 < rte->cost2)
977 					better = 1;
978 				else if (cost2 == rte->cost2 &&
979 				    cost < rte->cost)
980 					better = 1;
981 				else if (cost2 == rte->cost2 &&
982 				    cost == rte->cost)
983 					equal = 1;
984 				break;
985 			}
986 
987 		if (!better && !equal)
988 			return;
989 
990 		if (better) {
991 			TAILQ_FOREACH(rn, &rte->nexthop, entry)
992 				rn->invalid = 1;
993 
994 			rte->area = area;
995 			rte->cost = cost;
996 			rte->cost2 = cost2;
997 			rte->p_type = p_type;
998 			rte->flags = flags;
999 			rte->ext_tag = tag;
1000 		}
1001 
1002 		if (equal || better)
1003 			rt_nexthop_add(rte, vnh, adv_rtr);
1004 	}
1005 }
1006 
1007 struct rt_node *
1008 rt_lookup(enum dst_type type, struct in6_addr *addr)
1009 {
1010 	struct rt_node	*rn;
1011 	struct in6_addr	 ina;
1012 	u_int8_t	 i = 128;
1013 
1014 	if (type == DT_RTR) {
1015 		rn = rt_find(addr, 32, type);
1016 		if (rn && rn->invalid == 0)
1017 			return (rn);
1018 		return (NULL);
1019 	}
1020 
1021 	/* type == DT_NET */
1022 	do {
1023 		inet6applymask(&ina, addr, i);
1024 		if ((rn = rt_find(&ina, i, type)) && rn->invalid == 0)
1025 			return (rn);
1026 	} while (i-- != 0);
1027 
1028 	return (NULL);
1029 }
1030 
1031 /* router LSA links */
1032 struct lsa_rtr_link *
1033 get_rtr_link(struct vertex *v, unsigned int idx)
1034 {
1035 	struct lsa_rtr_link	*rtr_link = NULL;
1036 	char			*buf = (char *)v->lsa;
1037 	unsigned int		 i;
1038 
1039 	if (v->type != LSA_TYPE_ROUTER)
1040 		fatalx("get_rtr_link: invalid LSA type");
1041 
1042 	/* number of links validated earlier by lsa_check() */
1043 	rtr_link = (struct lsa_rtr_link *)(buf + sizeof(v->lsa->hdr) +
1044 	    sizeof(struct lsa_rtr));
1045 	for (i = 0; i < lsa_num_links(v); i++) {
1046 		if (i == idx)
1047 			return (rtr_link);
1048 		rtr_link++;
1049 	}
1050 
1051 	return (NULL);
1052 }
1053 
1054 /* network LSA links */
1055 struct lsa_net_link *
1056 get_net_link(struct vertex *v, unsigned int idx)
1057 {
1058 	struct lsa_net_link	*net_link = NULL;
1059 	char			*buf = (char *)v->lsa;
1060 	unsigned int		 i;
1061 
1062 	if (v->type != LSA_TYPE_NETWORK)
1063 		fatalx("get_net_link: invalid LSA type");
1064 
1065 	/* number of links validated earlier by lsa_check() */
1066 	net_link = (struct lsa_net_link *)(buf + sizeof(v->lsa->hdr) +
1067 	    sizeof(struct lsa_net));
1068 	for (i = 0; i < lsa_num_links(v); i++) {
1069 		if (i == idx)
1070 			return (net_link);
1071 		net_link++;
1072 	}
1073 
1074 	return (NULL);
1075 }
1076 
1077 /* misc */
1078 int
1079 linked(struct vertex *w, struct vertex *v)
1080 {
1081 	struct lsa_rtr_link	*rtr_link = NULL;
1082 	struct lsa_net_link	*net_link = NULL;
1083 	unsigned int		 i;
1084 
1085 	switch (w->type) {
1086 	case LSA_TYPE_ROUTER:
1087 		for (i = 0; i < lsa_num_links(w); i++) {
1088 			rtr_link = get_rtr_link(w, i);
1089 			switch (v->type) {
1090 			case LSA_TYPE_ROUTER:
1091 				if (rtr_link->type == LINK_TYPE_POINTTOPOINT &&
1092 				    rtr_link->nbr_rtr_id == htonl(v->adv_rtr))
1093 					return (1);
1094 				break;
1095 			case LSA_TYPE_NETWORK:
1096 				if (rtr_link->type == LINK_TYPE_TRANSIT_NET &&
1097 				    rtr_link->nbr_rtr_id == htonl(v->adv_rtr) &&
1098 				    rtr_link->nbr_iface_id == htonl(v->ls_id))
1099 					return (1);
1100 				break;
1101 			default:
1102 				fatalx("linked: invalid type");
1103 			}
1104 		}
1105 		return (0);
1106 	case LSA_TYPE_NETWORK:
1107 		for (i = 0; i < lsa_num_links(w); i++) {
1108 			net_link = get_net_link(w, i);
1109 			switch (v->type) {
1110 			case LSA_TYPE_ROUTER:
1111 				if (net_link->att_rtr == htonl(v->adv_rtr))
1112 					return (1);
1113 				break;
1114 			default:
1115 				fatalx("linked: invalid type");
1116 			}
1117 		}
1118 		return (0);
1119 	default:
1120 		fatalx("linked: invalid LSA type");
1121 	}
1122 
1123 	return (0);
1124 }
1125