xref: /openbsd-src/usr.sbin/ospf6d/rde_spf.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: rde_spf.c,v 1.29 2023/03/08 04:43:14 guenther Exp $ */
2a1a4e97bSnorby 
3a1a4e97bSnorby /*
4a1a4e97bSnorby  * Copyright (c) 2005 Esben Norby <norby@openbsd.org>
5a1a4e97bSnorby  *
6a1a4e97bSnorby  * Permission to use, copy, modify, and distribute this software for any
7a1a4e97bSnorby  * purpose with or without fee is hereby granted, provided that the above
8a1a4e97bSnorby  * copyright notice and this permission notice appear in all copies.
9a1a4e97bSnorby  *
10a1a4e97bSnorby  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a1a4e97bSnorby  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a1a4e97bSnorby  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a1a4e97bSnorby  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a1a4e97bSnorby  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a1a4e97bSnorby  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a1a4e97bSnorby  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a1a4e97bSnorby  */
18a1a4e97bSnorby 
19a1a4e97bSnorby #include <sys/types.h>
20a1a4e97bSnorby #include <sys/socket.h>
21a1a4e97bSnorby #include <netinet/in.h>
22a1a4e97bSnorby #include <arpa/inet.h>
23a1a4e97bSnorby #include <err.h>
24a1a4e97bSnorby #include <stdlib.h>
2585584260Smmcc #include <string.h>
26a1a4e97bSnorby 
27a1a4e97bSnorby #include "ospf6d.h"
28a1a4e97bSnorby #include "ospf6.h"
29a1a4e97bSnorby #include "log.h"
30a1a4e97bSnorby #include "rde.h"
31a1a4e97bSnorby 
32a1a4e97bSnorby extern struct ospfd_conf	*rdeconf;
33a1a4e97bSnorby TAILQ_HEAD(, vertex)		 cand_list;
34a1a4e97bSnorby RB_HEAD(rt_tree, rt_node)	 rt;
35a1a4e97bSnorby RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare)
36a1a4e97bSnorby RB_GENERATE(rt_tree, rt_node, entry, rt_compare)
37a1a4e97bSnorby struct vertex			*spf_root = NULL;
38a1a4e97bSnorby 
39f66469dfSclaudio struct in6_addr	*calc_nexthop_lladdr(struct vertex *, struct lsa_rtr_link *,
40712f52e1Sclaudio 		     unsigned int);
41abf314c6Sstsp void		 calc_nexthop_transit_nbr(struct vertex *, struct vertex *,
42712f52e1Sclaudio 		     unsigned int);
435b06b816Sstsp void		 calc_nexthop(struct vertex *, struct vertex *,
445b06b816Sstsp 		     struct area *, struct lsa_rtr_link *);
45a1a4e97bSnorby void		 rt_nexthop_clear(struct rt_node *);
46a1a4e97bSnorby void		 rt_nexthop_add(struct rt_node *, struct v_nexthead *,
47eb4e7b90Sdenis 		     u_int16_t, struct in_addr);
48f8efa005Sclaudio void		 rt_update(struct in6_addr *, u_int8_t, struct v_nexthead *,
49eb4e7b90Sdenis 		     u_int16_t, u_int32_t, u_int32_t, struct in_addr,
50eb4e7b90Sdenis 		     struct in_addr, enum path_type, enum dst_type, u_int8_t,
51eb4e7b90Sdenis 		     u_int32_t);
52f8efa005Sclaudio struct rt_node	*rt_lookup(enum dst_type, struct in6_addr *);
53a1a4e97bSnorby void		 rt_invalidate(struct area *);
54a1a4e97bSnorby int		 linked(struct vertex *, struct vertex *);
55a1a4e97bSnorby 
56a1a4e97bSnorby void
spf_calc(struct area * area)57a1a4e97bSnorby spf_calc(struct area *area)
58a1a4e97bSnorby {
59a1a4e97bSnorby 	struct vertex		*v, *w;
60a1a4e97bSnorby 	struct lsa_rtr_link	*rtr_link = NULL;
61a1a4e97bSnorby 	struct lsa_net_link	*net_link;
62a1a4e97bSnorby 	u_int32_t		 d;
63b930d91aSstsp 	unsigned int		 i;
64a1a4e97bSnorby 
65a1a4e97bSnorby 	/* clear SPF tree */
66a1a4e97bSnorby 	spf_tree_clr(area);
67a1a4e97bSnorby 	cand_list_clr();
68a1a4e97bSnorby 
69a1a4e97bSnorby 	/* initialize SPF tree */
704bbe09dfSstsp 	if ((v = spf_root = lsa_find_rtr(area, rde_router_id())) == NULL)
71a1a4e97bSnorby 		/* empty area because no interface is active */
72a1a4e97bSnorby 		return;
73a1a4e97bSnorby 
74a1a4e97bSnorby 	area->transit = 0;
75a1a4e97bSnorby 	spf_root->cost = 0;
76a1a4e97bSnorby 	w = NULL;
77a1a4e97bSnorby 
78a1a4e97bSnorby 	/* calculate SPF tree */
79a1a4e97bSnorby 	do {
80a1a4e97bSnorby 		/* loop links */
81a1a4e97bSnorby 		for (i = 0; i < lsa_num_links(v); i++) {
82a1a4e97bSnorby 			switch (v->type) {
83a1a4e97bSnorby 			case LSA_TYPE_ROUTER:
84a1a4e97bSnorby 				rtr_link = get_rtr_link(v, i);
85a1a4e97bSnorby 				switch (rtr_link->type) {
86a1a4e97bSnorby 				case LINK_TYPE_POINTTOPOINT:
87a1a4e97bSnorby 				case LINK_TYPE_VIRTUAL:
88a1a4e97bSnorby 					/* find router LSA */
894bbe09dfSstsp 					w = lsa_find_rtr(area,
904bbe09dfSstsp 					    rtr_link->nbr_rtr_id);
91a1a4e97bSnorby 					break;
92a1a4e97bSnorby 				case LINK_TYPE_TRANSIT_NET:
93a1a4e97bSnorby 					/* find network LSA */
944bbe09dfSstsp 					w = lsa_find_tree(&area->lsa_tree,
954bbe09dfSstsp 					    htons(LSA_TYPE_NETWORK),
964bbe09dfSstsp 					    rtr_link->nbr_iface_id,
974bbe09dfSstsp 					    rtr_link->nbr_rtr_id);
98a1a4e97bSnorby 					break;
99a1a4e97bSnorby 				default:
100a1a4e97bSnorby 					fatalx("spf_calc: invalid link type");
101a1a4e97bSnorby 				}
102a1a4e97bSnorby 				break;
103a1a4e97bSnorby 			case LSA_TYPE_NETWORK:
104a1a4e97bSnorby 				net_link = get_net_link(v, i);
105a1a4e97bSnorby 				/* find router LSA */
1064bbe09dfSstsp 				w = lsa_find_rtr(area, net_link->att_rtr);
107a1a4e97bSnorby 				break;
108a1a4e97bSnorby 			default:
109a1a4e97bSnorby 				fatalx("spf_calc: invalid LSA type");
110a1a4e97bSnorby 			}
111a1a4e97bSnorby 
112a1a4e97bSnorby 			if (w == NULL)
113a1a4e97bSnorby 				continue;
114a1a4e97bSnorby 
1154bbe09dfSstsp 			if (ntohs(w->lsa->hdr.age) == MAX_AGE)
1164bbe09dfSstsp 				continue;
1174bbe09dfSstsp 
1184bbe09dfSstsp 			if (lsa_num_links(w) == 0)
119a1a4e97bSnorby 				continue;
120a1a4e97bSnorby 
121a1a4e97bSnorby 			if (!linked(w, v)) {
1224bbe09dfSstsp 				log_debug("spf_calc: w adv_rtr %s ls_id %s "
1234bbe09dfSstsp 				    "type 0x%x numlinks %hu has no link to "
1244bbe09dfSstsp 				    "v adv_rtr %s ls_id %s type 0x%x",
1254bbe09dfSstsp 				    log_rtr_id(htonl(w->adv_rtr)),
1264bbe09dfSstsp 				    log_rtr_id(htonl(w->ls_id)), w->type,
1274bbe09dfSstsp 				    lsa_num_links(w),
1284bbe09dfSstsp 				    log_rtr_id(htonl(v->adv_rtr)),
1294bbe09dfSstsp 				    log_rtr_id(htonl(v->ls_id)), v->type);
130a1a4e97bSnorby 				continue;
131a1a4e97bSnorby 			}
132a1a4e97bSnorby 
133a1a4e97bSnorby 			if (v->type == LSA_TYPE_ROUTER)
134a1a4e97bSnorby 				d = v->cost + ntohs(rtr_link->metric);
135a1a4e97bSnorby 			else
136a1a4e97bSnorby 				d = v->cost;
137a1a4e97bSnorby 
138a1a4e97bSnorby 			if (cand_list_present(w)) {
139a1a4e97bSnorby 				if (d > w->cost)
140a1a4e97bSnorby 					continue;
141a1a4e97bSnorby 				if (d < w->cost) {
142a1a4e97bSnorby 					w->cost = d;
14385be3f1aSdenis 					vertex_nexthop_clear(w);
1445b06b816Sstsp 					calc_nexthop(w, v, area, rtr_link);
145a1a4e97bSnorby 					/*
146a1a4e97bSnorby 					 * need to readd to candidate list
147a1a4e97bSnorby 					 * because the list is sorted
148a1a4e97bSnorby 					 */
149a1a4e97bSnorby 					TAILQ_REMOVE(&cand_list, w, cand);
150a1a4e97bSnorby 					cand_list_add(w);
151a1a4e97bSnorby 				} else
152a1a4e97bSnorby 					/* equal cost path */
1535b06b816Sstsp 					calc_nexthop(w, v, area, rtr_link);
154a1a4e97bSnorby 			} else if (w->cost == LS_INFINITY && d < LS_INFINITY) {
155a1a4e97bSnorby 				w->cost = d;
156a1a4e97bSnorby 
15785be3f1aSdenis 				vertex_nexthop_clear(w);
1585b06b816Sstsp 				calc_nexthop(w, v, area, rtr_link);
159a1a4e97bSnorby 				cand_list_add(w);
160a1a4e97bSnorby 			}
161a1a4e97bSnorby 		}
162a1a4e97bSnorby 
163a1a4e97bSnorby 		/* get next vertex */
164a1a4e97bSnorby 		v = cand_list_pop();
165a1a4e97bSnorby 		w = NULL;
166a1a4e97bSnorby 	} while (v != NULL);
167a1a4e97bSnorby 
168a1a4e97bSnorby 	/* spf_dump(area); */
1694bbe09dfSstsp 	log_debug("spf_calc: area %s calculated", inet_ntoa(area->id));
1704bbe09dfSstsp 
1714bbe09dfSstsp 	/* Dump SPF tree to log */
1724bbe09dfSstsp 	RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
1734bbe09dfSstsp 		struct v_nexthop *vn;
1744bbe09dfSstsp 		char hops[4096];
1754bbe09dfSstsp 		struct iface *iface;
1764bbe09dfSstsp 
1774bbe09dfSstsp 		bzero(hops, sizeof(hops));
1784bbe09dfSstsp 
1794bbe09dfSstsp 		if (v->type != LSA_TYPE_ROUTER && v->type != LSA_TYPE_NETWORK)
1804bbe09dfSstsp 			continue;
1814bbe09dfSstsp 
1824bbe09dfSstsp 		TAILQ_FOREACH(vn, &v->nexthop, entry) {
1834bbe09dfSstsp 			strlcat(hops, log_in6addr(&vn->nexthop), sizeof(hops));
1844bbe09dfSstsp 			strlcat(hops, "%", sizeof(hops));
1854bbe09dfSstsp 			if ((iface = if_find(vn->ifindex)) == NULL)
1864bbe09dfSstsp 				fatalx("spf_calc: lost iface");
1874bbe09dfSstsp 			strlcat(hops, iface->name, sizeof(hops));
1884bbe09dfSstsp 			if (vn != TAILQ_LAST(&v->nexthop, v_nexthead))
1894bbe09dfSstsp 				strlcat(hops, ", ", sizeof(hops));
1904bbe09dfSstsp 		}
1914bbe09dfSstsp 		log_debug("%s(%s, 0x%x, %s) cost %u has nexthops [%s]",
1924bbe09dfSstsp 		    v == spf_root ? "*" : " ", log_rtr_id(htonl(v->adv_rtr)),
1934bbe09dfSstsp 		    v->type, log_rtr_id(htonl(v->ls_id)), v->cost, hops);
1944bbe09dfSstsp 	}
195a1a4e97bSnorby 
196a1a4e97bSnorby 	area->num_spf_calc++;
197a1a4e97bSnorby 	start_spf_timer();
198a1a4e97bSnorby }
199a1a4e97bSnorby 
200a1a4e97bSnorby void
rt_calc(struct vertex * v,struct area * area,struct ospfd_conf * conf)201a1a4e97bSnorby rt_calc(struct vertex *v, struct area *area, struct ospfd_conf *conf)
202a1a4e97bSnorby {
203a1a4e97bSnorby 	struct vertex		*w;
2048a03b303Sstsp 	struct lsa_intra_prefix	*iap;
2058a03b303Sstsp 	struct lsa_prefix	*prefix;
2068a03b303Sstsp 	struct in_addr		 adv_rtr;
2078a03b303Sstsp 	struct in6_addr		 ia6;
2088a03b303Sstsp 	u_int16_t		 i, off;
2098a03b303Sstsp 	u_int8_t		 flags;
210a1a4e97bSnorby 
211a1a4e97bSnorby 	lsa_age(v);
212a1a4e97bSnorby 	if (ntohs(v->lsa->hdr.age) == MAX_AGE)
213a1a4e97bSnorby 		return;
214a1a4e97bSnorby 
215a1a4e97bSnorby 	switch (v->type) {
21657d36a00Sclaudio 	case LSA_TYPE_ROUTER:
21757d36a00Sclaudio 		if (v->cost >= LS_INFINITY || TAILQ_EMPTY(&v->nexthop))
21857d36a00Sclaudio 			return;
21957d36a00Sclaudio 
22057d36a00Sclaudio 		/* router, only add border and as-external routers */
22157d36a00Sclaudio 		flags = LSA_24_GETHI(ntohl(v->lsa->data.rtr.opts));
22257d36a00Sclaudio 		if ((flags & (OSPF_RTR_B | OSPF_RTR_E)) == 0)
22357d36a00Sclaudio 			return;
22457d36a00Sclaudio 
22557d36a00Sclaudio 		bzero(&ia6, sizeof(ia6));
22657d36a00Sclaudio 		adv_rtr.s_addr = htonl(v->adv_rtr);
22757d36a00Sclaudio 		bcopy(&adv_rtr, &ia6.s6_addr[12], sizeof(adv_rtr));
22857d36a00Sclaudio 
229eb4e7b90Sdenis 		rt_update(&ia6, 128, &v->nexthop, v->type, v->cost, 0, area->id,
23057d36a00Sclaudio 		    adv_rtr, PT_INTER_AREA, DT_RTR, flags, 0);
23157d36a00Sclaudio 		break;
2328a03b303Sstsp 	case LSA_TYPE_INTRA_A_PREFIX:
2338a03b303Sstsp 		/* Find referenced LSA */
2348a03b303Sstsp 		iap = &v->lsa->data.pref_intra;
2358a03b303Sstsp 		switch (ntohs(iap->ref_type)) {
236a1a4e97bSnorby 		case LSA_TYPE_ROUTER:
2378a03b303Sstsp 			w = lsa_find_rtr(area, iap->ref_adv_rtr);
2388a03b303Sstsp 			if (w == NULL) {
2398a03b303Sstsp 				warnx("rt_calc: Intra-Area-Prefix LSA (%s, %u) "
2408a03b303Sstsp 				    "references non-existent router %s",
2418a03b303Sstsp 				    log_rtr_id(htonl(v->adv_rtr)),
2428a03b303Sstsp 				    v->ls_id, log_rtr_id(iap->ref_adv_rtr));
243a1a4e97bSnorby 				return;
244a1a4e97bSnorby 			}
24557d36a00Sclaudio 			flags = LSA_24_GETHI(ntohl(w->lsa->data.rtr.opts));
246a1a4e97bSnorby 			break;
247a1a4e97bSnorby 		case LSA_TYPE_NETWORK:
2488a03b303Sstsp 			w = lsa_find_tree(&area->lsa_tree, iap->ref_type,
2498a03b303Sstsp 			    iap->ref_ls_id, iap->ref_adv_rtr);
2508a03b303Sstsp 			if (w == NULL) {
2518a03b303Sstsp 				warnx("rt_calc: Intra-Area-Prefix LSA (%s, %u) "
2528a03b303Sstsp 				    "references non-existent Network LSA (%s, "
2538a03b303Sstsp 				    "%u)", log_rtr_id(htonl(v->adv_rtr)),
2548a03b303Sstsp 				    v->ls_id, log_rtr_id(iap->ref_adv_rtr),
2558a03b303Sstsp 				    ntohl(iap->ref_ls_id));
256a1a4e97bSnorby 				return;
257a1a4e97bSnorby 			}
2588a03b303Sstsp 			flags = 0;
259a1a4e97bSnorby 			break;
260a1a4e97bSnorby 		default:
2618a03b303Sstsp 			warnx("rt_calc: Intra-Area-Prefix LSA (%s, %u) has "
2628a03b303Sstsp 			    "invalid ref_type 0x%hx", log_rtr_id(v->adv_rtr),
2638a03b303Sstsp 			    v->ls_id, ntohs(iap->ref_type));
2648a03b303Sstsp 			return;
265a1a4e97bSnorby 		}
2668a03b303Sstsp 
2678a03b303Sstsp 		if (w->cost >= LS_INFINITY || TAILQ_EMPTY(&w->nexthop))
2688a03b303Sstsp 			return;
2698a03b303Sstsp 
2708a03b303Sstsp 		/* Add prefixes listed in Intra-Area-Prefix LSA to routing
2718a03b303Sstsp 		 * table, using w as destination. */
2728a03b303Sstsp 		off = sizeof(v->lsa->hdr) + sizeof(struct lsa_intra_prefix);
2738a03b303Sstsp 		for (i = 0; i < ntohs(v->lsa->data.pref_intra.numprefix); i++) {
2748a03b303Sstsp 			prefix = (struct lsa_prefix *)((char *)(v->lsa) + off);
2758a03b303Sstsp 			if (!(prefix->options & OSPF_PREFIX_NU)) {
2768a03b303Sstsp 				bzero(&ia6, sizeof(ia6));
2778a03b303Sstsp 				bcopy(prefix + 1, &ia6,
2788a03b303Sstsp 				    LSA_PREFIXSIZE(prefix->prefixlen));
2798a03b303Sstsp 
2808a03b303Sstsp 				adv_rtr.s_addr = htonl(w->adv_rtr);
2818a03b303Sstsp 
2828a03b303Sstsp 				rt_update(&ia6, prefix->prefixlen, &w->nexthop,
283eb4e7b90Sdenis 				    v->type, w->cost + ntohs(prefix->metric), 0,
284712f52e1Sclaudio 				    area->id, adv_rtr, PT_INTRA_AREA, DT_NET,
2858a03b303Sstsp 				    flags, 0);
2868a03b303Sstsp 			}
2878a03b303Sstsp 			off += sizeof(struct lsa_prefix)
2888a03b303Sstsp 			    + LSA_PREFIXSIZE(prefix->prefixlen);
2898a03b303Sstsp 		}
29057d36a00Sclaudio 		break;
29157d36a00Sclaudio 	case LSA_TYPE_INTER_A_PREFIX:
29257d36a00Sclaudio 		/* XXX if ABR only look at area 0.0.0.0 LSA */
29357d36a00Sclaudio 		/* ignore self-originated stuff */
29457d36a00Sclaudio 		if (v->self)
29557d36a00Sclaudio 			return;
29657d36a00Sclaudio 
29757d36a00Sclaudio 		adv_rtr.s_addr = htonl(v->adv_rtr);
29857d36a00Sclaudio 		w = lsa_find_rtr(area, adv_rtr.s_addr);
29957d36a00Sclaudio 		if (w == NULL) {
30057d36a00Sclaudio 			warnx("rt_calc: Inter-Area-Router LSA (%s, %u) "
30157d36a00Sclaudio 			    "originated from non-existent router",
30257d36a00Sclaudio 			    log_rtr_id(htonl(v->adv_rtr)),
30357d36a00Sclaudio 			    v->ls_id);
30457d36a00Sclaudio 			return;
30557d36a00Sclaudio 		}
30657d36a00Sclaudio 		if (w->cost >= LS_INFINITY || TAILQ_EMPTY(&w->nexthop))
30757d36a00Sclaudio 			return;
30857d36a00Sclaudio 
30957d36a00Sclaudio 		/* Add prefix listed in Inter-Area-Prefix LSA to routing
31057d36a00Sclaudio 		 * table, using w as destination. */
31157d36a00Sclaudio 		off = sizeof(v->lsa->hdr) + sizeof(struct lsa_prefix_sum);
31257d36a00Sclaudio 		prefix = (struct lsa_prefix *)((char *)(v->lsa) + off);
31357d36a00Sclaudio 		if (prefix->options & OSPF_PREFIX_NU)
31457d36a00Sclaudio 			return;
31557d36a00Sclaudio 
31657d36a00Sclaudio 		bzero(&ia6, sizeof(ia6));
31757d36a00Sclaudio 		bcopy(prefix + 1, &ia6, LSA_PREFIXSIZE(prefix->prefixlen));
31857d36a00Sclaudio 
319eb4e7b90Sdenis 		rt_update(&ia6, prefix->prefixlen, &w->nexthop, v->type,
320eb4e7b90Sdenis 		    w->cost + (ntohs(v->lsa->data.rtr_sum.metric) &
321eb4e7b90Sdenis 		    LSA_METRIC_MASK), 0, area->id, adv_rtr, PT_INTER_AREA,
322eb4e7b90Sdenis 		    DT_NET, 0, 0);
32357d36a00Sclaudio 		break;
32457d36a00Sclaudio 	case LSA_TYPE_INTER_A_ROUTER:
32557d36a00Sclaudio 		/* XXX if ABR only look at area 0.0.0.0 LSA */
32657d36a00Sclaudio 		/* ignore self-originated stuff */
32757d36a00Sclaudio 		if (v->self)
32857d36a00Sclaudio 			return;
32957d36a00Sclaudio 
33057d36a00Sclaudio 		adv_rtr.s_addr = htonl(v->adv_rtr);
33157d36a00Sclaudio 		w = lsa_find_rtr(area, adv_rtr.s_addr);
33257d36a00Sclaudio 		if (w == NULL) {
33357d36a00Sclaudio 			warnx("rt_calc: Inter-Area-Router LSA (%s, %u) "
33457d36a00Sclaudio 			    "originated from non-existent router",
33557d36a00Sclaudio 			    log_rtr_id(htonl(v->adv_rtr)),
33657d36a00Sclaudio 			    v->ls_id);
33757d36a00Sclaudio 			return;
33857d36a00Sclaudio 		}
33957d36a00Sclaudio 		if (w->cost >= LS_INFINITY || TAILQ_EMPTY(&w->nexthop))
34057d36a00Sclaudio 			return;
34157d36a00Sclaudio 
34257d36a00Sclaudio 		/* Add router listed in Inter-Area-Router LSA to routing
34357d36a00Sclaudio 		 * table, using w as destination. */
34457d36a00Sclaudio 		bzero(&ia6, sizeof(ia6));
34557d36a00Sclaudio 		bcopy(&v->lsa->data.rtr_sum.dest_rtr_id, &ia6.s6_addr[12],
34657d36a00Sclaudio 		    4);
34757d36a00Sclaudio 
348eb4e7b90Sdenis 		rt_update(&ia6, 128, &w->nexthop, v->type, w->cost +
34957d36a00Sclaudio 		    (ntohs(v->lsa->data.rtr_sum.metric) & LSA_METRIC_MASK), 0,
35057d36a00Sclaudio 		    area->id, adv_rtr, PT_INTER_AREA, DT_RTR, 0, 0);
35157d36a00Sclaudio 		break;
3528a03b303Sstsp 	default:
3538a03b303Sstsp 		break;
3548a03b303Sstsp 	}
355a1a4e97bSnorby }
356a1a4e97bSnorby 
357a1a4e97bSnorby void
asext_calc(struct vertex * v)358a1a4e97bSnorby asext_calc(struct vertex *v)
359a1a4e97bSnorby {
36057d36a00Sclaudio 	struct in6_addr		 addr, fw_addr;
361a1a4e97bSnorby 	struct rt_node		*r;
362a1a4e97bSnorby 	struct rt_nexthop	*rn;
36357d36a00Sclaudio 	struct lsa_prefix	*prefix;
36457d36a00Sclaudio 	struct in_addr		 adv_rtr, area;
36557d36a00Sclaudio 	char			*p;
36657d36a00Sclaudio 	u_int32_t		 metric, cost2, ext_tag = 0;
367a1a4e97bSnorby 	enum path_type		 type;
368a1a4e97bSnorby 
369a1a4e97bSnorby 	lsa_age(v);
370a1a4e97bSnorby 	if (ntohs(v->lsa->hdr.age) == MAX_AGE ||
371a1a4e97bSnorby 	    (ntohl(v->lsa->data.asext.metric) & LSA_METRIC_MASK) >=
372a1a4e97bSnorby 	    LS_INFINITY)
373a1a4e97bSnorby 		return;
374a1a4e97bSnorby 
375a1a4e97bSnorby 	switch (v->type) {
376a1a4e97bSnorby 	case LSA_TYPE_EXTERNAL:
377a1a4e97bSnorby 		/* ignore self-originated stuff */
378a1a4e97bSnorby 		if (v->self)
379a1a4e97bSnorby 			return;
380a1a4e97bSnorby 
38157d36a00Sclaudio 		adv_rtr.s_addr = htonl(v->adv_rtr);
38257d36a00Sclaudio 		bzero(&addr, sizeof(addr));
38357d36a00Sclaudio 		bcopy(&adv_rtr, &addr.s6_addr[12], sizeof(adv_rtr));
38457d36a00Sclaudio 		if ((r = rt_lookup(DT_RTR, &addr)) == NULL)
385a1a4e97bSnorby 			return;
386a1a4e97bSnorby 
38757d36a00Sclaudio 		prefix = &v->lsa->data.asext.prefix;
38857d36a00Sclaudio 		if (prefix->options & OSPF_PREFIX_NU)
38957d36a00Sclaudio 			break;
39057d36a00Sclaudio 		bzero(&addr, sizeof(addr));
39157d36a00Sclaudio 		bcopy(prefix + 1, &addr,
39257d36a00Sclaudio 		    LSA_PREFIXSIZE(prefix->prefixlen));
393a1a4e97bSnorby 
39457d36a00Sclaudio 		p = (char *)(prefix + 1) + LSA_PREFIXSIZE(prefix->prefixlen);
39557d36a00Sclaudio 		metric = ntohl(v->lsa->data.asext.metric);
396a1a4e97bSnorby 
39757d36a00Sclaudio 		if (metric & LSA_ASEXT_F_FLAG) {
39857d36a00Sclaudio 			bcopy(p, &fw_addr, sizeof(fw_addr));
39957d36a00Sclaudio 			p += sizeof(fw_addr);
40057d36a00Sclaudio 
40157d36a00Sclaudio 			/* lookup forwarding address */
40257d36a00Sclaudio 			if ((r = rt_lookup(DT_NET, &fw_addr)) == NULL ||
40357d36a00Sclaudio 			    (r->p_type != PT_INTRA_AREA &&
40457d36a00Sclaudio 			    r->p_type != PT_INTER_AREA))
40557d36a00Sclaudio 				return;
40657d36a00Sclaudio 		}
40757d36a00Sclaudio 		if (metric & LSA_ASEXT_T_FLAG) {
40857d36a00Sclaudio 			bcopy(p, &ext_tag, sizeof(ext_tag));
40957d36a00Sclaudio 			p += sizeof(ext_tag);
41057d36a00Sclaudio 		}
41157d36a00Sclaudio 		if (metric & LSA_ASEXT_E_FLAG) {
412a1a4e97bSnorby 			v->cost = r->cost;
41357d36a00Sclaudio 			cost2 = metric & LSA_METRIC_MASK;
414a1a4e97bSnorby 			type = PT_TYPE2_EXT;
415a1a4e97bSnorby 		} else {
41657d36a00Sclaudio 			v->cost = r->cost + (metric & LSA_METRIC_MASK);
417a1a4e97bSnorby 			cost2 = 0;
418a1a4e97bSnorby 			type = PT_TYPE1_EXT;
419a1a4e97bSnorby 		}
420a1a4e97bSnorby 
42157d36a00Sclaudio 		area.s_addr = 0;
42285be3f1aSdenis 		vertex_nexthop_clear(v);
423a1a4e97bSnorby 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
424a1a4e97bSnorby 			if (rn->invalid)
425a1a4e97bSnorby 				continue;
426a1a4e97bSnorby 
427a1a4e97bSnorby 			if (rn->connected && r->d_type == DT_NET) {
42857d36a00Sclaudio 				if (metric & LSA_ASEXT_F_FLAG)
42985be3f1aSdenis 					vertex_nexthop_add(v, NULL, &fw_addr,
43057d36a00Sclaudio 					    rn->ifindex);
431a1a4e97bSnorby 				else
43257d36a00Sclaudio 					fatalx("asext_calc: I'm sorry Dave, "
43357d36a00Sclaudio 					    "I'm afraid I can't do that.");
434a1a4e97bSnorby 			} else
43585be3f1aSdenis 				vertex_nexthop_add(v, NULL, &rn->nexthop,
43657d36a00Sclaudio 				    rn->ifindex);
437a1a4e97bSnorby 		}
438a1a4e97bSnorby 
439eb4e7b90Sdenis 		rt_update(&addr, prefix->prefixlen, &v->nexthop, v->type,
440eb4e7b90Sdenis 		    v->cost, cost2, area, adv_rtr, type, DT_NET, 0, ext_tag);
441a1a4e97bSnorby 		break;
442a1a4e97bSnorby 	default:
443a1a4e97bSnorby 		fatalx("asext_calc: invalid LSA type");
444a1a4e97bSnorby 	}
445a1a4e97bSnorby }
446a1a4e97bSnorby 
447a1a4e97bSnorby void
spf_tree_clr(struct area * area)448a1a4e97bSnorby spf_tree_clr(struct area *area)
449a1a4e97bSnorby {
450a1a4e97bSnorby 	struct lsa_tree	*tree = &area->lsa_tree;
451a1a4e97bSnorby 	struct vertex	*v;
452a1a4e97bSnorby 
453a1a4e97bSnorby 	RB_FOREACH(v, lsa_tree, tree) {
454a1a4e97bSnorby 		v->cost = LS_INFINITY;
45585be3f1aSdenis 		vertex_nexthop_clear(v);
456a1a4e97bSnorby 	}
457a1a4e97bSnorby }
458a1a4e97bSnorby 
459abf314c6Sstsp struct in6_addr *
calc_nexthop_lladdr(struct vertex * dst,struct lsa_rtr_link * rtr_link,unsigned int ifindex)460f66469dfSclaudio calc_nexthop_lladdr(struct vertex *dst, struct lsa_rtr_link *rtr_link,
461712f52e1Sclaudio     unsigned int ifindex)
462abf314c6Sstsp {
463abf314c6Sstsp 	struct iface		*iface;
464abf314c6Sstsp 	struct vertex		*link;
465abf314c6Sstsp 	struct rde_nbr		*nbr;
466abf314c6Sstsp 
467abf314c6Sstsp 	/* Find outgoing interface, we need its LSA tree */
468abf314c6Sstsp 	LIST_FOREACH(iface, &dst->area->iface_list, entry) {
469f66469dfSclaudio 		if (ifindex == iface->ifindex)
470abf314c6Sstsp 			break;
471abf314c6Sstsp 	}
472abf314c6Sstsp 	if (!iface) {
473f66469dfSclaudio 		log_warnx("calc_nexthop_lladdr: no interface found for "
474f66469dfSclaudio 		    "ifindex %d", ntohl(rtr_link->iface_id));
475abf314c6Sstsp 		return (NULL);
476abf314c6Sstsp 	}
477abf314c6Sstsp 
478abf314c6Sstsp 	/* Determine neighbor's link-local address.
479abf314c6Sstsp 	 * Try to get it from link LSA first. */
480abf314c6Sstsp 	link = lsa_find_tree(&iface->lsa_tree,
481f66469dfSclaudio 		htons(LSA_TYPE_LINK), rtr_link->iface_id,
482abf314c6Sstsp 		htonl(dst->adv_rtr));
483abf314c6Sstsp 	if (link)
484abf314c6Sstsp 		return &link->lsa->data.link.lladdr;
485abf314c6Sstsp 
486abf314c6Sstsp 	/* Not found, so fall back to source address
487abf314c6Sstsp 	 * advertised in hello packet. */
488abf314c6Sstsp 	if ((nbr = rde_nbr_find(dst->peerid)) == NULL)
489abf314c6Sstsp 		fatalx("next hop is not a neighbor");
490abf314c6Sstsp 	return &nbr->addr;
491abf314c6Sstsp }
492abf314c6Sstsp 
493abf314c6Sstsp void
calc_nexthop_transit_nbr(struct vertex * dst,struct vertex * parent,unsigned int ifindex)494abf314c6Sstsp calc_nexthop_transit_nbr(struct vertex *dst, struct vertex *parent,
495712f52e1Sclaudio     unsigned int ifindex)
496abf314c6Sstsp {
497abf314c6Sstsp 	struct lsa_rtr_link	*rtr_link;
498abf314c6Sstsp 	unsigned int		 i;
499abf314c6Sstsp 	struct in6_addr		*lladdr;
500abf314c6Sstsp 
501abf314c6Sstsp 	if (dst->type != LSA_TYPE_ROUTER)
502abf314c6Sstsp 		fatalx("calc_nexthop_transit_nbr: dst is not a router");
503abf314c6Sstsp 	if (parent->type != LSA_TYPE_NETWORK)
504abf314c6Sstsp 		fatalx("calc_nexthop_transit_nbr: parent is not a network");
505abf314c6Sstsp 
506abf314c6Sstsp 	/* dst is a neighbor on a directly attached transit network.
507abf314c6Sstsp 	 * Figure out dst's link local address and add it as nexthop. */
508abf314c6Sstsp 	for (i = 0; i < lsa_num_links(dst); i++) {
509abf314c6Sstsp 		rtr_link = get_rtr_link(dst, i);
510abf314c6Sstsp 		if (rtr_link->type == LINK_TYPE_TRANSIT_NET &&
511abf314c6Sstsp 		    rtr_link->nbr_rtr_id == parent->lsa->hdr.adv_rtr &&
512abf314c6Sstsp 		    rtr_link->nbr_iface_id == parent->lsa->hdr.ls_id) {
513f66469dfSclaudio 			lladdr = calc_nexthop_lladdr(dst, rtr_link, ifindex);
51485be3f1aSdenis 			vertex_nexthop_add(dst, parent, lladdr, ifindex);
515abf314c6Sstsp 		}
516abf314c6Sstsp 	}
517abf314c6Sstsp }
518abf314c6Sstsp 
519a1a4e97bSnorby void
calc_nexthop(struct vertex * dst,struct vertex * parent,struct area * area,struct lsa_rtr_link * rtr_link)5205b06b816Sstsp calc_nexthop(struct vertex *dst, struct vertex *parent,
5215b06b816Sstsp     struct area *area, struct lsa_rtr_link *rtr_link)
522a1a4e97bSnorby {
523a1a4e97bSnorby 	struct v_nexthop	*vn;
5245b06b816Sstsp 	struct in6_addr		*nexthop;
525a1a4e97bSnorby 
526a1a4e97bSnorby 	/* case 1 */
527a1a4e97bSnorby 	if (parent == spf_root) {
528a1a4e97bSnorby 		switch (dst->type) {
529a1a4e97bSnorby 		case LSA_TYPE_ROUTER:
5305b06b816Sstsp 			if (rtr_link->type != LINK_TYPE_POINTTOPOINT)
5315b06b816Sstsp 				fatalx("inconsistent SPF tree");
532f66469dfSclaudio 			nexthop = calc_nexthop_lladdr(dst, rtr_link,
53350089776Sclaudio 			    ntohl(rtr_link->iface_id));
534a1a4e97bSnorby 			break;
535a1a4e97bSnorby 		case LSA_TYPE_NETWORK:
5365b06b816Sstsp 			if (rtr_link->type != LINK_TYPE_TRANSIT_NET)
5375b06b816Sstsp 				fatalx("inconsistent SPF tree");
5385b06b816Sstsp 
5395b06b816Sstsp 			/* Next hop address cannot be determined yet,
5405b06b816Sstsp 			 * we only know the outgoing interface. */
5415b06b816Sstsp 			nexthop = NULL;
542a1a4e97bSnorby 			break;
543a1a4e97bSnorby 		default:
544a1a4e97bSnorby 			fatalx("calc_nexthop: invalid dst type");
545a1a4e97bSnorby 		}
5465b06b816Sstsp 
54785be3f1aSdenis 		vertex_nexthop_add(dst, parent, nexthop,
5485b06b816Sstsp 		    ntohl(rtr_link->iface_id));
5495b06b816Sstsp 		return;
550a1a4e97bSnorby 	}
551a1a4e97bSnorby 
552a1a4e97bSnorby 	/* case 2 */
553a1a4e97bSnorby 	if (parent->type == LSA_TYPE_NETWORK && dst->type == LSA_TYPE_ROUTER) {
554a1a4e97bSnorby 		TAILQ_FOREACH(vn, &parent->nexthop, entry) {
5555b06b816Sstsp 			if (vn->prev == spf_root)
5565b06b816Sstsp 				calc_nexthop_transit_nbr(dst, parent,
5575b06b816Sstsp 				    vn->ifindex);
5585b06b816Sstsp 			else
5595b06b816Sstsp 				/* dst is more than one transit net away */
56085be3f1aSdenis 				vertex_nexthop_add(dst, parent, &vn->nexthop,
5615b06b816Sstsp 				    vn->ifindex);
562a1a4e97bSnorby 		}
563a1a4e97bSnorby 		return;
564a1a4e97bSnorby 	}
565a1a4e97bSnorby 
566a1a4e97bSnorby 	/* case 3 */
567a1a4e97bSnorby 	TAILQ_FOREACH(vn, &parent->nexthop, entry)
56885be3f1aSdenis 	    vertex_nexthop_add(dst, parent, &vn->nexthop, vn->ifindex);
569a1a4e97bSnorby }
570a1a4e97bSnorby 
571a1a4e97bSnorby /* candidate list */
572a1a4e97bSnorby void
cand_list_init(void)573a1a4e97bSnorby cand_list_init(void)
574a1a4e97bSnorby {
575a1a4e97bSnorby 	TAILQ_INIT(&cand_list);
576a1a4e97bSnorby }
577a1a4e97bSnorby 
578a1a4e97bSnorby void
cand_list_add(struct vertex * v)579a1a4e97bSnorby cand_list_add(struct vertex *v)
580a1a4e97bSnorby {
581a1a4e97bSnorby 	struct vertex	*c = NULL;
582a1a4e97bSnorby 
583a1a4e97bSnorby 	TAILQ_FOREACH(c, &cand_list, cand) {
584a1a4e97bSnorby 		if (c->cost > v->cost) {
585a1a4e97bSnorby 			TAILQ_INSERT_BEFORE(c, v, cand);
586a1a4e97bSnorby 			return;
587a1a4e97bSnorby 		} else if (c->cost == v->cost && c->type == LSA_TYPE_ROUTER &&
588a1a4e97bSnorby 		    v->type == LSA_TYPE_NETWORK) {
589a1a4e97bSnorby 			TAILQ_INSERT_BEFORE(c, v, cand);
590a1a4e97bSnorby 			return;
591a1a4e97bSnorby 		}
592a1a4e97bSnorby 	}
593a1a4e97bSnorby 	TAILQ_INSERT_TAIL(&cand_list, v, cand);
594a1a4e97bSnorby }
595a1a4e97bSnorby 
596a1a4e97bSnorby struct vertex *
cand_list_pop(void)597a1a4e97bSnorby cand_list_pop(void)
598a1a4e97bSnorby {
599a1a4e97bSnorby 	struct vertex	*c;
600a1a4e97bSnorby 
601a1a4e97bSnorby 	if ((c = TAILQ_FIRST(&cand_list)) != NULL) {
602a1a4e97bSnorby 		TAILQ_REMOVE(&cand_list, c, cand);
603a1a4e97bSnorby 	}
604a1a4e97bSnorby 
605a1a4e97bSnorby 	return (c);
606a1a4e97bSnorby }
607a1a4e97bSnorby 
608a1a4e97bSnorby int
cand_list_present(struct vertex * v)609a1a4e97bSnorby cand_list_present(struct vertex *v)
610a1a4e97bSnorby {
611a1a4e97bSnorby 	struct vertex	*c;
612a1a4e97bSnorby 
613a1a4e97bSnorby 	TAILQ_FOREACH(c, &cand_list, cand) {
614a1a4e97bSnorby 		if (c == v)
615a1a4e97bSnorby 			return (1);
616a1a4e97bSnorby 	}
617a1a4e97bSnorby 
618a1a4e97bSnorby 	return (0);
619a1a4e97bSnorby }
620a1a4e97bSnorby 
621a1a4e97bSnorby void
cand_list_clr(void)622a1a4e97bSnorby cand_list_clr(void)
623a1a4e97bSnorby {
624a1a4e97bSnorby 	struct vertex *c;
625a1a4e97bSnorby 
626a1a4e97bSnorby 	while ((c = TAILQ_FIRST(&cand_list)) != NULL) {
627a1a4e97bSnorby 		TAILQ_REMOVE(&cand_list, c, cand);
628a1a4e97bSnorby 	}
629a1a4e97bSnorby }
630a1a4e97bSnorby 
631a1a4e97bSnorby /* timers */
632a1a4e97bSnorby void
spf_timer(int fd,short event,void * arg)633a1a4e97bSnorby spf_timer(int fd, short event, void *arg)
634a1a4e97bSnorby {
635a1a4e97bSnorby 	struct vertex		*v;
636a1a4e97bSnorby 	struct ospfd_conf	*conf = arg;
637a1a4e97bSnorby 	struct area		*area;
638a1a4e97bSnorby 	struct rt_node		*r;
639a1a4e97bSnorby 
640a1a4e97bSnorby 	switch (conf->spf_state) {
641a1a4e97bSnorby 	case SPF_IDLE:
642a1a4e97bSnorby 		fatalx("spf_timer: invalid state IDLE");
643a1a4e97bSnorby 	case SPF_HOLDQUEUE:
644a1a4e97bSnorby 		conf->spf_state = SPF_DELAY;
645a1a4e97bSnorby 		/* FALLTHROUGH */
646a1a4e97bSnorby 	case SPF_DELAY:
647a1a4e97bSnorby 		LIST_FOREACH(area, &conf->area_list, entry) {
648a1a4e97bSnorby 			if (area->dirty) {
649a1a4e97bSnorby 				/* invalidate RIB entries of this area */
650a1a4e97bSnorby 				rt_invalidate(area);
651a1a4e97bSnorby 
652a1a4e97bSnorby 				/* calculate SPF tree */
653a1a4e97bSnorby 				spf_calc(area);
654a1a4e97bSnorby 
655a1a4e97bSnorby 				/* calculate route table */
656a1a4e97bSnorby 				RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
657a1a4e97bSnorby 					rt_calc(v, area, conf);
658a1a4e97bSnorby 				}
659a1a4e97bSnorby 
660a1a4e97bSnorby 				area->dirty = 0;
661a1a4e97bSnorby 			}
662a1a4e97bSnorby 		}
663a1a4e97bSnorby 
664a1a4e97bSnorby 		/* calculate as-external routes, first invalidate them */
665a1a4e97bSnorby 		rt_invalidate(NULL);
666a1a4e97bSnorby 		RB_FOREACH(v, lsa_tree, &asext_tree) {
667a1a4e97bSnorby 			asext_calc(v);
668a1a4e97bSnorby 		}
669a1a4e97bSnorby 
670a1a4e97bSnorby 		RB_FOREACH(r, rt_tree, &rt) {
671a1a4e97bSnorby 			LIST_FOREACH(area, &conf->area_list, entry)
672a1a4e97bSnorby 				rde_summary_update(r, area);
673a1a4e97bSnorby 
674a1a4e97bSnorby 			if (r->d_type != DT_NET)
675a1a4e97bSnorby 				continue;
676a1a4e97bSnorby 
677a1a4e97bSnorby 			if (r->invalid)
678a1a4e97bSnorby 				rde_send_delete_kroute(r);
679a1a4e97bSnorby 			else
680a1a4e97bSnorby 				rde_send_change_kroute(r);
681a1a4e97bSnorby 		}
682a1a4e97bSnorby 
683a1a4e97bSnorby 		LIST_FOREACH(area, &conf->area_list, entry)
684a1a4e97bSnorby 			lsa_remove_invalid_sums(area);
685a1a4e97bSnorby 
686a1a4e97bSnorby 		start_spf_holdtimer(conf);
687a1a4e97bSnorby 		break;
688a1a4e97bSnorby 	case SPF_HOLD:
689a1a4e97bSnorby 		conf->spf_state = SPF_IDLE;
690a1a4e97bSnorby 		break;
691a1a4e97bSnorby 	default:
692a1a4e97bSnorby 		fatalx("spf_timer: unknown state");
693a1a4e97bSnorby 	}
694a1a4e97bSnorby }
695a1a4e97bSnorby 
696a1a4e97bSnorby void
start_spf_timer(void)697a1a4e97bSnorby start_spf_timer(void)
698a1a4e97bSnorby {
699a1a4e97bSnorby 	struct timeval	tv;
700a1a4e97bSnorby 
701a1a4e97bSnorby 	switch (rdeconf->spf_state) {
702a1a4e97bSnorby 	case SPF_IDLE:
703a1a4e97bSnorby 		timerclear(&tv);
704a1a4e97bSnorby 		tv.tv_sec = rdeconf->spf_delay;
705a1a4e97bSnorby 		rdeconf->spf_state = SPF_DELAY;
706a1a4e97bSnorby 		if (evtimer_add(&rdeconf->ev, &tv) == -1)
707a1a4e97bSnorby 			fatal("start_spf_timer");
708a1a4e97bSnorby 		break;
709a1a4e97bSnorby 	case SPF_DELAY:
710a1a4e97bSnorby 		/* ignore */
711a1a4e97bSnorby 		break;
712a1a4e97bSnorby 	case SPF_HOLD:
713a1a4e97bSnorby 		rdeconf->spf_state = SPF_HOLDQUEUE;
714a1a4e97bSnorby 		break;
715a1a4e97bSnorby 	case SPF_HOLDQUEUE:
716a1a4e97bSnorby 		/* ignore */
717a1a4e97bSnorby 		break;
718a1a4e97bSnorby 	default:
719a1a4e97bSnorby 		fatalx("start_spf_timer: invalid spf_state");
720a1a4e97bSnorby 	}
721a1a4e97bSnorby }
722a1a4e97bSnorby 
723a1a4e97bSnorby void
stop_spf_timer(struct ospfd_conf * conf)724a1a4e97bSnorby stop_spf_timer(struct ospfd_conf *conf)
725a1a4e97bSnorby {
726a1a4e97bSnorby 	if (evtimer_del(&conf->ev) == -1)
727a1a4e97bSnorby 		fatal("stop_spf_timer");
728a1a4e97bSnorby }
729a1a4e97bSnorby 
730a1a4e97bSnorby void
start_spf_holdtimer(struct ospfd_conf * conf)731a1a4e97bSnorby start_spf_holdtimer(struct ospfd_conf *conf)
732a1a4e97bSnorby {
733a1a4e97bSnorby 	struct timeval	tv;
734a1a4e97bSnorby 
735a1a4e97bSnorby 	switch (conf->spf_state) {
736a1a4e97bSnorby 	case SPF_DELAY:
737a1a4e97bSnorby 		timerclear(&tv);
738a1a4e97bSnorby 		tv.tv_sec = conf->spf_hold_time;
739a1a4e97bSnorby 		conf->spf_state = SPF_HOLD;
740a1a4e97bSnorby 		if (evtimer_add(&conf->ev, &tv) == -1)
741a1a4e97bSnorby 			fatal("start_spf_holdtimer");
742a1a4e97bSnorby 		break;
743a1a4e97bSnorby 	case SPF_IDLE:
744a1a4e97bSnorby 	case SPF_HOLD:
745a1a4e97bSnorby 	case SPF_HOLDQUEUE:
746a1a4e97bSnorby 		fatalx("start_spf_holdtimer: invalid state");
747a1a4e97bSnorby 	default:
7483af4e127Snorby 		fatalx("start_spf_holdtimer: unknown state");
749a1a4e97bSnorby 	}
750a1a4e97bSnorby }
751a1a4e97bSnorby 
752a1a4e97bSnorby /* route table */
753a1a4e97bSnorby void
rt_init(void)754a1a4e97bSnorby rt_init(void)
755a1a4e97bSnorby {
756a1a4e97bSnorby 	RB_INIT(&rt);
757a1a4e97bSnorby }
758a1a4e97bSnorby 
759a1a4e97bSnorby int
rt_compare(struct rt_node * a,struct rt_node * b)760a1a4e97bSnorby rt_compare(struct rt_node *a, struct rt_node *b)
761a1a4e97bSnorby {
762f8efa005Sclaudio 	int	i;
763f8efa005Sclaudio 
764f8efa005Sclaudio 	/* XXX maybe a & b need to be switched */
765f8efa005Sclaudio 	i = memcmp(&a->prefix, &b->prefix, sizeof(a->prefix));
766f8efa005Sclaudio 	if (i)
767f8efa005Sclaudio 		return (i);
768a1a4e97bSnorby 	if (a->prefixlen < b->prefixlen)
769a1a4e97bSnorby 		return (-1);
770a1a4e97bSnorby 	if (a->prefixlen > b->prefixlen)
771a1a4e97bSnorby 		return (1);
772a1a4e97bSnorby 	if (a->d_type > b->d_type)
773a1a4e97bSnorby 		return (-1);
774a1a4e97bSnorby 	if (a->d_type < b->d_type)
775a1a4e97bSnorby 		return (1);
776a1a4e97bSnorby 	return (0);
777a1a4e97bSnorby }
778a1a4e97bSnorby 
779a1a4e97bSnorby struct rt_node *
rt_find(struct in6_addr * prefix,u_int8_t prefixlen,enum dst_type d_type)780f8efa005Sclaudio rt_find(struct in6_addr *prefix, u_int8_t prefixlen, enum dst_type d_type)
781a1a4e97bSnorby {
782a1a4e97bSnorby 	struct rt_node	s;
783a1a4e97bSnorby 
784f8efa005Sclaudio 	s.prefix = *prefix;
785a1a4e97bSnorby 	s.prefixlen = prefixlen;
786a1a4e97bSnorby 	s.d_type = d_type;
787a1a4e97bSnorby 
788a1a4e97bSnorby 	return (RB_FIND(rt_tree, &rt, &s));
789a1a4e97bSnorby }
790a1a4e97bSnorby 
791a1a4e97bSnorby int
rt_insert(struct rt_node * r)792a1a4e97bSnorby rt_insert(struct rt_node *r)
793a1a4e97bSnorby {
794a1a4e97bSnorby 	if (RB_INSERT(rt_tree, &rt, r) != NULL) {
795a1a4e97bSnorby 		log_warnx("rt_insert failed for %s/%u",
796f8efa005Sclaudio 		    log_in6addr(&r->prefix), r->prefixlen);
797a1a4e97bSnorby 		free(r);
798a1a4e97bSnorby 		return (-1);
799a1a4e97bSnorby 	}
800a1a4e97bSnorby 
801a1a4e97bSnorby 	return (0);
802a1a4e97bSnorby }
803a1a4e97bSnorby 
804a1a4e97bSnorby int
rt_remove(struct rt_node * r)805a1a4e97bSnorby rt_remove(struct rt_node *r)
806a1a4e97bSnorby {
807a1a4e97bSnorby 	if (RB_REMOVE(rt_tree, &rt, r) == NULL) {
808a1a4e97bSnorby 		log_warnx("rt_remove failed for %s/%u",
809f8efa005Sclaudio 		    log_in6addr(&r->prefix), r->prefixlen);
810a1a4e97bSnorby 		return (-1);
811a1a4e97bSnorby 	}
812a1a4e97bSnorby 
813a1a4e97bSnorby 	rt_nexthop_clear(r);
814a1a4e97bSnorby 	free(r);
815a1a4e97bSnorby 	return (0);
816a1a4e97bSnorby }
817a1a4e97bSnorby 
818a1a4e97bSnorby void
rt_invalidate(struct area * area)819a1a4e97bSnorby rt_invalidate(struct area *area)
820a1a4e97bSnorby {
821a1a4e97bSnorby 	struct rt_node		*r, *nr;
822a1a4e97bSnorby 	struct rt_nexthop	*rn, *nrn;
823a1a4e97bSnorby 
824a1a4e97bSnorby 	for (r = RB_MIN(rt_tree, &rt); r != NULL; r = nr) {
825a1a4e97bSnorby 		nr = RB_NEXT(rt_tree, &rt, r);
826a1a4e97bSnorby 		if (area == NULL) {
827a1a4e97bSnorby 			/* look only at as_ext routes */
828a1a4e97bSnorby 			if (r->p_type != PT_TYPE1_EXT &&
829a1a4e97bSnorby 			    r->p_type != PT_TYPE2_EXT)
830a1a4e97bSnorby 				continue;
831a1a4e97bSnorby 		} else {
832a1a4e97bSnorby 			/* ignore all as_ext routes */
833a1a4e97bSnorby 			if (r->p_type == PT_TYPE1_EXT ||
834a1a4e97bSnorby 			    r->p_type == PT_TYPE2_EXT)
835a1a4e97bSnorby 				continue;
836a1a4e97bSnorby 
837a1a4e97bSnorby 			/* look only at routes matching the area */
838a1a4e97bSnorby 			if (r->area.s_addr != area->id.s_addr)
839a1a4e97bSnorby 				continue;
840a1a4e97bSnorby 		}
841a1a4e97bSnorby 		r->invalid = 1;
842a1a4e97bSnorby 		for (rn = TAILQ_FIRST(&r->nexthop); rn != NULL; rn = nrn) {
843a1a4e97bSnorby 			nrn = TAILQ_NEXT(rn, entry);
844a1a4e97bSnorby 			if (rn->invalid) {
845a1a4e97bSnorby 				TAILQ_REMOVE(&r->nexthop, rn, entry);
846a1a4e97bSnorby 				free(rn);
847a1a4e97bSnorby 			} else
848a1a4e97bSnorby 				rn->invalid = 1;
849a1a4e97bSnorby 		}
850a1a4e97bSnorby 		if (TAILQ_EMPTY(&r->nexthop))
851a1a4e97bSnorby 			rt_remove(r);
852a1a4e97bSnorby 	}
853a1a4e97bSnorby }
854a1a4e97bSnorby 
855a1a4e97bSnorby void
rt_nexthop_clear(struct rt_node * r)856a1a4e97bSnorby rt_nexthop_clear(struct rt_node *r)
857a1a4e97bSnorby {
858a1a4e97bSnorby 	struct rt_nexthop	*rn;
859a1a4e97bSnorby 
860a1a4e97bSnorby 	while ((rn = TAILQ_FIRST(&r->nexthop)) != NULL) {
861a1a4e97bSnorby 		TAILQ_REMOVE(&r->nexthop, rn, entry);
862a1a4e97bSnorby 		free(rn);
863a1a4e97bSnorby 	}
864a1a4e97bSnorby }
865a1a4e97bSnorby 
866a1a4e97bSnorby void
rt_nexthop_add(struct rt_node * r,struct v_nexthead * vnh,u_int16_t type,struct in_addr adv_rtr)867eb4e7b90Sdenis rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh, u_int16_t type,
868a1a4e97bSnorby     struct in_addr adv_rtr)
869a1a4e97bSnorby {
870a1a4e97bSnorby 	struct v_nexthop	*vn;
871a1a4e97bSnorby 	struct rt_nexthop	*rn;
872a1a4e97bSnorby 	struct timespec		 now;
873a1a4e97bSnorby 
874a1a4e97bSnorby 	TAILQ_FOREACH(vn, vnh, entry) {
875a1a4e97bSnorby 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
876f8efa005Sclaudio 			if (!IN6_ARE_ADDR_EQUAL(&rn->nexthop, &vn->nexthop))
877a1a4e97bSnorby 				continue;
878a1a4e97bSnorby 
879a1a4e97bSnorby 			rn->adv_rtr.s_addr = adv_rtr.s_addr;
880eb4e7b90Sdenis 			rn->connected = (type == LSA_TYPE_NETWORK &&
881eb4e7b90Sdenis 			    vn->prev == spf_root) ||
882eb4e7b90Sdenis 			    (IN6_IS_ADDR_UNSPECIFIED(&vn->nexthop));
883a1a4e97bSnorby 			rn->invalid = 0;
884a1a4e97bSnorby 
885a1a4e97bSnorby 			r->invalid = 0;
886a1a4e97bSnorby 			break;
887a1a4e97bSnorby 		}
888a1a4e97bSnorby 		if (rn)
889a1a4e97bSnorby 			continue;
890a1a4e97bSnorby 
891a1a4e97bSnorby 		if ((rn = calloc(1, sizeof(struct rt_nexthop))) == NULL)
892a1a4e97bSnorby 			fatal("rt_nexthop_add");
893a1a4e97bSnorby 
894a1a4e97bSnorby 		clock_gettime(CLOCK_MONOTONIC, &now);
895f8efa005Sclaudio 		rn->nexthop = vn->nexthop;
896712f52e1Sclaudio 		rn->ifindex = vn->ifindex;
897a1a4e97bSnorby 		rn->adv_rtr.s_addr = adv_rtr.s_addr;
898a1a4e97bSnorby 		rn->uptime = now.tv_sec;
89955dcbf22Sdenis 		rn->connected = (type == LSA_TYPE_NETWORK &&
90055dcbf22Sdenis 		    vn->prev == spf_root) ||
90155dcbf22Sdenis 		    (IN6_IS_ADDR_UNSPECIFIED(&vn->nexthop));
902a1a4e97bSnorby 		rn->invalid = 0;
903a1a4e97bSnorby 
904a1a4e97bSnorby 		r->invalid = 0;
905a1a4e97bSnorby 		TAILQ_INSERT_TAIL(&r->nexthop, rn, entry);
906a1a4e97bSnorby 	}
907a1a4e97bSnorby }
908a1a4e97bSnorby 
909a1a4e97bSnorby void
rt_clear(void)910a1a4e97bSnorby rt_clear(void)
911a1a4e97bSnorby {
912a1a4e97bSnorby 	struct rt_node	*r;
913a1a4e97bSnorby 
914a1a4e97bSnorby 	while ((r = RB_MIN(rt_tree, &rt)) != NULL)
915a1a4e97bSnorby 		rt_remove(r);
916a1a4e97bSnorby }
917a1a4e97bSnorby 
918a1a4e97bSnorby void
rt_dump(struct in_addr area,pid_t pid,u_int8_t r_type)919a1a4e97bSnorby rt_dump(struct in_addr area, pid_t pid, u_int8_t r_type)
920a1a4e97bSnorby {
921a1a4e97bSnorby 	static struct ctl_rt	 rtctl;
922a1a4e97bSnorby 	struct timespec		 now;
923a1a4e97bSnorby 	struct rt_node		*r;
924a1a4e97bSnorby 	struct rt_nexthop	*rn;
925a1a4e97bSnorby 
926a1a4e97bSnorby 	clock_gettime(CLOCK_MONOTONIC, &now);
927a1a4e97bSnorby 
928a1a4e97bSnorby 	RB_FOREACH(r, rt_tree, &rt) {
929a1a4e97bSnorby 		if (r->invalid)
930a1a4e97bSnorby 			continue;
931a1a4e97bSnorby 
932a1a4e97bSnorby 		if (r->area.s_addr != area.s_addr)
933a1a4e97bSnorby 			continue;
934a1a4e97bSnorby 
935a1a4e97bSnorby 		switch (r_type) {
936a1a4e97bSnorby 		case RIB_RTR:
937a1a4e97bSnorby 			if (r->d_type != DT_RTR)
938a1a4e97bSnorby 				continue;
939a1a4e97bSnorby 			break;
940a1a4e97bSnorby 		case RIB_NET:
941a1a4e97bSnorby 			if (r->d_type != DT_NET)
942a1a4e97bSnorby 				continue;
943a1a4e97bSnorby 			if (r->p_type == PT_TYPE1_EXT ||
944a1a4e97bSnorby 			    r->p_type == PT_TYPE2_EXT)
945a1a4e97bSnorby 				continue;
946a1a4e97bSnorby 			break;
947a1a4e97bSnorby 		case RIB_EXT:
948a1a4e97bSnorby 			if (r->p_type != PT_TYPE1_EXT &&
949a1a4e97bSnorby 			    r->p_type != PT_TYPE2_EXT)
950a1a4e97bSnorby 				continue;
951a1a4e97bSnorby 			break;
952a1a4e97bSnorby 		default:
953a1a4e97bSnorby 			fatalx("rt_dump: invalid RIB type");
954a1a4e97bSnorby 		}
955a1a4e97bSnorby 
95655dcbf22Sdenis 		memset(&rtctl, 0, sizeof(rtctl));
957f8efa005Sclaudio 		rtctl.prefix = r->prefix;
958a1a4e97bSnorby 		rtctl.area.s_addr = r->area.s_addr;
959a1a4e97bSnorby 		rtctl.cost = r->cost;
960a1a4e97bSnorby 		rtctl.cost2 = r->cost2;
961a1a4e97bSnorby 		rtctl.p_type = r->p_type;
962a1a4e97bSnorby 		rtctl.d_type = r->d_type;
963a1a4e97bSnorby 		rtctl.flags = r->flags;
964a1a4e97bSnorby 		rtctl.prefixlen = r->prefixlen;
96555dcbf22Sdenis 
96655dcbf22Sdenis 		TAILQ_FOREACH(rn, &r->nexthop, entry) {
96755dcbf22Sdenis 			if (rn->invalid)
96855dcbf22Sdenis 				continue;
96955dcbf22Sdenis 
97055dcbf22Sdenis 			rtctl.connected = rn->connected;
97155dcbf22Sdenis 			rtctl.nexthop = rn->nexthop;
97255dcbf22Sdenis 			rtctl.ifindex = rn->ifindex;
97355dcbf22Sdenis 			rtctl.adv_rtr.s_addr = rn->adv_rtr.s_addr;
974a1a4e97bSnorby 			rtctl.uptime = now.tv_sec - rn->uptime;
975a1a4e97bSnorby 
976a1a4e97bSnorby 			rde_imsg_compose_ospfe(IMSG_CTL_SHOW_RIB, 0, pid,
977a1a4e97bSnorby 			    &rtctl, sizeof(rtctl));
978a1a4e97bSnorby 		}
979a1a4e97bSnorby 	}
980a1a4e97bSnorby }
981a1a4e97bSnorby 
982a1a4e97bSnorby void
rt_update(struct in6_addr * prefix,u_int8_t prefixlen,struct v_nexthead * vnh,u_int16_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)983f8efa005Sclaudio rt_update(struct in6_addr *prefix, u_int8_t prefixlen, struct v_nexthead *vnh,
984eb4e7b90Sdenis      u_int16_t v_type, u_int32_t cost, u_int32_t cost2, struct in_addr area,
985a1a4e97bSnorby      struct in_addr adv_rtr, enum path_type p_type, enum dst_type d_type,
986a1a4e97bSnorby      u_int8_t flags, u_int32_t tag)
987a1a4e97bSnorby {
988a1a4e97bSnorby 	struct rt_node		*rte;
989a1a4e97bSnorby 	struct rt_nexthop	*rn;
990a1a4e97bSnorby 	int			 better = 0, equal = 0;
991a1a4e97bSnorby 
992a1a4e97bSnorby 	if (vnh == NULL || TAILQ_EMPTY(vnh))	/* XXX remove */
993a1a4e97bSnorby 		fatalx("rt_update: invalid nexthop");
994a1a4e97bSnorby 
995f8efa005Sclaudio 	if ((rte = rt_find(prefix, prefixlen, d_type)) == NULL) {
996a1a4e97bSnorby 		if ((rte = calloc(1, sizeof(struct rt_node))) == NULL)
997a1a4e97bSnorby 			fatal("rt_update");
998a1a4e97bSnorby 
999a1a4e97bSnorby 		TAILQ_INIT(&rte->nexthop);
1000f8efa005Sclaudio 		rte->prefix = *prefix;
1001a1a4e97bSnorby 		rte->prefixlen = prefixlen;
1002a1a4e97bSnorby 		rte->cost = cost;
1003a1a4e97bSnorby 		rte->cost2 = cost2;
1004a1a4e97bSnorby 		rte->area = area;
1005a1a4e97bSnorby 		rte->p_type = p_type;
1006a1a4e97bSnorby 		rte->d_type = d_type;
1007a1a4e97bSnorby 		rte->flags = flags;
1008a1a4e97bSnorby 		rte->ext_tag = tag;
1009a1a4e97bSnorby 
1010eb4e7b90Sdenis 		rt_nexthop_add(rte, vnh, v_type, adv_rtr);
1011a1a4e97bSnorby 
1012a1a4e97bSnorby 		rt_insert(rte);
1013a1a4e97bSnorby 	} else {
1014a1a4e97bSnorby 		/* order:
1015a1a4e97bSnorby 		 * 1. intra-area
1016a1a4e97bSnorby 		 * 2. inter-area
1017a1a4e97bSnorby 		 * 3. type 1 as ext
1018a1a4e97bSnorby 		 * 4. type 2 as ext
1019a1a4e97bSnorby 		 */
1020a1a4e97bSnorby 		if (rte->invalid)	/* everything is better than invalid */
1021a1a4e97bSnorby 			better = 1;
1022a1a4e97bSnorby 		else if (p_type < rte->p_type)
1023a1a4e97bSnorby 			better = 1;
1024a1a4e97bSnorby 		else if (p_type == rte->p_type)
1025a1a4e97bSnorby 			switch (p_type) {
1026a1a4e97bSnorby 			case PT_INTRA_AREA:
1027a1a4e97bSnorby 			case PT_INTER_AREA:
1028a1a4e97bSnorby 				if (cost < rte->cost)
1029a1a4e97bSnorby 					better = 1;
1030a1a4e97bSnorby 				else if (cost == rte->cost &&
1031a1a4e97bSnorby 				    rte->area.s_addr == area.s_addr)
1032a1a4e97bSnorby 					equal = 1;
1033a1a4e97bSnorby 				break;
1034a1a4e97bSnorby 			case PT_TYPE1_EXT:
1035a1a4e97bSnorby 				if (cost < rte->cost)
1036a1a4e97bSnorby 					better = 1;
1037a1a4e97bSnorby 				else if (cost == rte->cost)
1038a1a4e97bSnorby 					equal = 1;
1039a1a4e97bSnorby 				break;
1040a1a4e97bSnorby 			case PT_TYPE2_EXT:
1041a1a4e97bSnorby 				if (cost2 < rte->cost2)
1042a1a4e97bSnorby 					better = 1;
1043a1a4e97bSnorby 				else if (cost2 == rte->cost2 &&
1044a1a4e97bSnorby 				    cost < rte->cost)
1045a1a4e97bSnorby 					better = 1;
1046a1a4e97bSnorby 				else if (cost2 == rte->cost2 &&
1047a1a4e97bSnorby 				    cost == rte->cost)
1048a1a4e97bSnorby 					equal = 1;
1049a1a4e97bSnorby 				break;
1050a1a4e97bSnorby 			}
1051a1a4e97bSnorby 
1052a1a4e97bSnorby 		if (better) {
1053a1a4e97bSnorby 			TAILQ_FOREACH(rn, &rte->nexthop, entry)
1054a1a4e97bSnorby 				rn->invalid = 1;
1055a1a4e97bSnorby 
1056a1a4e97bSnorby 			rte->area = area;
1057a1a4e97bSnorby 			rte->cost = cost;
1058a1a4e97bSnorby 			rte->cost2 = cost2;
1059a1a4e97bSnorby 			rte->p_type = p_type;
1060a1a4e97bSnorby 			rte->flags = flags;
1061a1a4e97bSnorby 			rte->ext_tag = tag;
1062a1a4e97bSnorby 		}
1063a1a4e97bSnorby 
1064a1a4e97bSnorby 		if (equal || better)
1065eb4e7b90Sdenis 			rt_nexthop_add(rte, vnh, v_type, adv_rtr);
1066a1a4e97bSnorby 	}
1067a1a4e97bSnorby }
1068a1a4e97bSnorby 
1069a1a4e97bSnorby struct rt_node *
rt_lookup(enum dst_type type,struct in6_addr * addr)1070f8efa005Sclaudio rt_lookup(enum dst_type type, struct in6_addr *addr)
1071a1a4e97bSnorby {
1072a1a4e97bSnorby 	struct rt_node	*rn;
1073f8efa005Sclaudio 	struct in6_addr	 ina;
1074f8efa005Sclaudio 	u_int8_t	 i = 128;
1075a1a4e97bSnorby 
1076a1a4e97bSnorby 	if (type == DT_RTR) {
107757d36a00Sclaudio 		rn = rt_find(addr, 128, type);
1078a1a4e97bSnorby 		if (rn && rn->invalid == 0)
1079a1a4e97bSnorby 			return (rn);
1080a1a4e97bSnorby 		return (NULL);
1081a1a4e97bSnorby 	}
1082a1a4e97bSnorby 
1083a1a4e97bSnorby 	/* type == DT_NET */
1084a1a4e97bSnorby 	do {
1085f8efa005Sclaudio 		inet6applymask(&ina, addr, i);
1086f8efa005Sclaudio 		if ((rn = rt_find(&ina, i, type)) && rn->invalid == 0)
1087a1a4e97bSnorby 			return (rn);
1088a1a4e97bSnorby 	} while (i-- != 0);
1089a1a4e97bSnorby 
1090a1a4e97bSnorby 	return (NULL);
1091a1a4e97bSnorby }
1092a1a4e97bSnorby 
1093a1a4e97bSnorby /* router LSA links */
1094a1a4e97bSnorby struct lsa_rtr_link *
get_rtr_link(struct vertex * v,unsigned int idx)1095b930d91aSstsp get_rtr_link(struct vertex *v, unsigned int idx)
1096a1a4e97bSnorby {
1097a1a4e97bSnorby 	struct lsa_rtr_link	*rtr_link = NULL;
1098c78f753bSstsp 	unsigned int		 frag = 1;
1099c78f753bSstsp 	unsigned int		 frag_nlinks;
1100c78f753bSstsp 	unsigned int		 nlinks = 0;
1101b930d91aSstsp 	unsigned int		 i;
1102a1a4e97bSnorby 
1103a1a4e97bSnorby 	if (v->type != LSA_TYPE_ROUTER)
1104a1a4e97bSnorby 		fatalx("get_rtr_link: invalid LSA type");
1105a1a4e97bSnorby 
1106c78f753bSstsp 	/* Treat multiple Router-LSAs originated by the same router
1107c78f753bSstsp 	 * as an aggregate. */
1108c78f753bSstsp 	do {
1109b930d91aSstsp 		/* number of links validated earlier by lsa_check() */
1110c78f753bSstsp 		rtr_link = (struct lsa_rtr_link *)((char *)v->lsa +
1111c78f753bSstsp 		    sizeof(v->lsa->hdr) + sizeof(struct lsa_rtr));
1112c78f753bSstsp 		frag_nlinks = ((ntohs(v->lsa->hdr.len) -
1113c78f753bSstsp 		    sizeof(struct lsa_hdr) - sizeof(struct lsa_rtr)) /
1114c78f753bSstsp 		    sizeof(struct lsa_rtr_link));
1115c78f753bSstsp 		if (nlinks + frag_nlinks > idx) {
1116c78f753bSstsp 			for (i = 0; i < frag_nlinks; i++) {
1117c78f753bSstsp 				if (i + nlinks == idx)
1118a1a4e97bSnorby 					return (rtr_link);
1119b930d91aSstsp 				rtr_link++;
1120a1a4e97bSnorby 			}
1121c78f753bSstsp 		}
1122c78f753bSstsp 		nlinks += frag_nlinks;
1123c78f753bSstsp 		v = lsa_find_rtr_frag(v->area, htonl(v->adv_rtr), frag++);
1124c78f753bSstsp 	} while (v);
1125a1a4e97bSnorby 
1126eb4e7b90Sdenis 	fatalx("get_rtr_link: index not found");
1127a1a4e97bSnorby }
1128a1a4e97bSnorby 
1129a1a4e97bSnorby /* network LSA links */
1130a1a4e97bSnorby struct lsa_net_link *
get_net_link(struct vertex * v,unsigned int idx)1131574fa3b3Sstsp get_net_link(struct vertex *v, unsigned int idx)
1132a1a4e97bSnorby {
1133a1a4e97bSnorby 	struct lsa_net_link	*net_link = NULL;
1134a1a4e97bSnorby 	char			*buf = (char *)v->lsa;
1135eb4e7b90Sdenis 	unsigned int		 i, nlinks;
1136a1a4e97bSnorby 
1137a1a4e97bSnorby 	if (v->type != LSA_TYPE_NETWORK)
1138a1a4e97bSnorby 		fatalx("get_net_link: invalid LSA type");
1139a1a4e97bSnorby 
1140574fa3b3Sstsp 	net_link = (struct lsa_net_link *)(buf + sizeof(v->lsa->hdr) +
1141574fa3b3Sstsp 	    sizeof(struct lsa_net));
1142eb4e7b90Sdenis 
1143eb4e7b90Sdenis 	/* number of links validated earlier by lsa_check() */
1144eb4e7b90Sdenis 	nlinks = lsa_num_links(v);
1145eb4e7b90Sdenis 	for (i = 0; i < nlinks; i++) {
1146a1a4e97bSnorby 		if (i == idx)
1147a1a4e97bSnorby 			return (net_link);
1148574fa3b3Sstsp 		net_link++;
1149a1a4e97bSnorby 	}
1150a1a4e97bSnorby 
1151eb4e7b90Sdenis 	fatalx("get_net_link: index not found");
1152a1a4e97bSnorby }
1153a1a4e97bSnorby 
1154a1a4e97bSnorby /* misc */
1155a1a4e97bSnorby int
linked(struct vertex * w,struct vertex * v)1156a1a4e97bSnorby linked(struct vertex *w, struct vertex *v)
1157a1a4e97bSnorby {
1158a1a4e97bSnorby 	struct lsa_rtr_link	*rtr_link = NULL;
1159a1a4e97bSnorby 	struct lsa_net_link	*net_link = NULL;
1160ca30c715Sstsp 	unsigned int		 i;
1161a1a4e97bSnorby 
1162a1a4e97bSnorby 	switch (w->type) {
1163a1a4e97bSnorby 	case LSA_TYPE_ROUTER:
1164a1a4e97bSnorby 		for (i = 0; i < lsa_num_links(w); i++) {
1165a1a4e97bSnorby 			rtr_link = get_rtr_link(w, i);
1166a1a4e97bSnorby 			switch (v->type) {
1167a1a4e97bSnorby 			case LSA_TYPE_ROUTER:
1168a1a4e97bSnorby 				if (rtr_link->type == LINK_TYPE_POINTTOPOINT &&
1169ca30c715Sstsp 				    rtr_link->nbr_rtr_id == htonl(v->adv_rtr))
1170a1a4e97bSnorby 					return (1);
1171a1a4e97bSnorby 				break;
1172a1a4e97bSnorby 			case LSA_TYPE_NETWORK:
1173ca30c715Sstsp 				if (rtr_link->type == LINK_TYPE_TRANSIT_NET &&
1174ca30c715Sstsp 				    rtr_link->nbr_rtr_id == htonl(v->adv_rtr) &&
1175ca30c715Sstsp 				    rtr_link->nbr_iface_id == htonl(v->ls_id))
1176a1a4e97bSnorby 					return (1);
1177a1a4e97bSnorby 				break;
1178a1a4e97bSnorby 			default:
117901b10fc6Sstsp 				fatalx("linked: invalid type");
1180a1a4e97bSnorby 			}
1181a1a4e97bSnorby 		}
1182a1a4e97bSnorby 		return (0);
1183a1a4e97bSnorby 	case LSA_TYPE_NETWORK:
1184a1a4e97bSnorby 		for (i = 0; i < lsa_num_links(w); i++) {
1185a1a4e97bSnorby 			net_link = get_net_link(w, i);
1186a1a4e97bSnorby 			switch (v->type) {
1187a1a4e97bSnorby 			case LSA_TYPE_ROUTER:
1188ca30c715Sstsp 				if (net_link->att_rtr == htonl(v->adv_rtr))
1189a1a4e97bSnorby 					return (1);
1190a1a4e97bSnorby 				break;
1191a1a4e97bSnorby 			default:
119201b10fc6Sstsp 				fatalx("linked: invalid type");
1193a1a4e97bSnorby 			}
1194a1a4e97bSnorby 		}
1195a1a4e97bSnorby 		return (0);
1196a1a4e97bSnorby 	default:
119701b10fc6Sstsp 		fatalx("linked: invalid LSA type");
1198a1a4e97bSnorby 	}
1199a1a4e97bSnorby 
1200a1a4e97bSnorby 	return (0);
1201a1a4e97bSnorby }
1202