]>
Commit | Line | Data |
---|---|---|
c1f8dc91 OF |
1 | /* |
2 | * BIRD -- OSPF | |
3 | * | |
98ac6176 | 4 | * (c) 1999--2004 Ondrej Filip <feela@network.cz> |
70945cb6 OZ |
5 | * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> |
6 | * (c) 2009--2014 CZ.NIC z.s.p.o. | |
c1f8dc91 OF |
7 | * |
8 | * Can be freely distributed and used under the terms of the GNU GPL. | |
9 | */ | |
10 | ||
89755a86 OF |
11 | /** |
12 | * DOC: Open Shortest Path First (OSPF) | |
742029eb | 13 | * |
a7a7372a OZ |
14 | * The OSPF protocol is quite complicated and its complex implemenation is split |
15 | * to many files. In |ospf.c|, you will find mainly the interface for | |
16 | * communication with the core (e.g., reconfiguration hooks, shutdown and | |
17 | * initialisation and so on). File |iface.c| contains the interface state | |
18 | * machine and functions for allocation and deallocation of OSPF's interface | |
19 | * data structures. Source |neighbor.c| includes the neighbor state machine and | |
20 | * functions for election of Designated Router and Backup Designated router. In | |
21 | * |packet.c|, you will find various functions for sending and receiving generic | |
22 | * OSPF packets. There are also routines for authentication and checksumming. | |
23 | * In |hello.c|, there are routines for sending and receiving of hello packets | |
24 | * as well as functions for maintaining wait times and the inactivity timer. | |
25 | * Files |lsreq.c|, |lsack.c|, |dbdes.c| contain functions for sending and | |
26 | * receiving of link-state requests, link-state acknowledgements and database | |
27 | * descriptions respectively. In |lsupd.c|, there are functions for sending and | |
28 | * receiving of link-state updates and also the flooding algorithm. Source | |
29 | * |topology.c| is a place where routines for searching LSAs in the link-state | |
30 | * database, adding and deleting them reside, there also are functions for | |
31 | * originating of various types of LSAs (router LSA, net LSA, external LSA). | |
32 | * File |rt.c| contains routines for calculating the routing table. |lsalib.c| | |
33 | * is a set of various functions for working with the LSAs (endianity | |
34 | * conversions, calculation of checksum etc.). | |
89755a86 | 35 | * |
a7a7372a OZ |
36 | * One instance of the protocol is able to hold LSA databases for multiple OSPF |
37 | * areas, to exchange routing information between multiple neighbors and to | |
38 | * calculate the routing tables. The core structure is &ospf_proto to which | |
39 | * multiple &ospf_area and &ospf_iface structures are connected. &ospf_proto is | |
40 | * also connected to &top_hash_graph which is a dynamic hashing structure that | |
41 | * describes the link-state database. It allows fast search, addition and | |
42 | * deletion. Each LSA is kept in two pieces: header and body. Both of them are | |
baa5dd6c | 43 | * kept in the endianity of the CPU. |
86c84d76 | 44 | * |
a7a7372a OZ |
45 | * In OSPFv2 specification, it is implied that there is one IP prefix for each |
46 | * physical network/interface (unless it is an ptp link). But in modern systems, | |
47 | * there might be more independent IP prefixes associated with an interface. To | |
48 | * handle this situation, we have one &ospf_iface for each active IP prefix | |
49 | * (instead for each active iface); This behaves like virtual interface for the | |
50 | * purpose of OSPF. If we receive packet, we associate it with a proper virtual | |
51 | * interface mainly according to its source address. | |
48cff379 | 52 | * |
a7a7372a OZ |
53 | * OSPF keeps one socket per &ospf_iface. This allows us (compared to one socket |
54 | * approach) to evade problems with a limit of multicast groups per socket and | |
55 | * with sending multicast packets to appropriate interface in a portable way. | |
56 | * The socket is associated with underlying physical iface and should not | |
57 | * receive packets received on other ifaces (unfortunately, this is not true on | |
58 | * BSD). Generally, one packet can be received by more sockets (for example, if | |
59 | * there are more &ospf_iface on one physical iface), therefore we explicitly | |
60 | * filter received packets according to src/dst IP address and received iface. | |
48cff379 | 61 | * |
a7a7372a OZ |
62 | * Vlinks are implemented using particularly degenerate form of &ospf_iface, |
63 | * which has several exceptions: it does not have its iface or socket (it copies | |
64 | * these from 'parent' &ospf_iface) and it is present in iface list even when | |
65 | * down (it is not freed in ospf_iface_down()). | |
48cff379 | 66 | * |
86c84d76 | 67 | * The heart beat of ospf is ospf_disp(). It is called at regular intervals |
a7a7372a OZ |
68 | * (&ospf_proto->tick). It is responsible for aging and flushing of LSAs in the |
69 | * database, updating topology information in LSAs and for routing table | |
70 | * calculation. | |
742029eb | 71 | * |
a7a7372a OZ |
72 | * To every &ospf_iface, we connect one or more &ospf_neighbor's -- a structure |
73 | * containing many timers and queues for building adjacency and for exchange of | |
74 | * routing messages. | |
89755a86 | 75 | * |
a7a7372a OZ |
76 | * BIRD's OSPF implementation respects RFC2328 in every detail, but some of |
77 | * internal algorithms do differ. The RFC recommends making a snapshot of the | |
78 | * link-state database when a new adjacency is forming and sending the database | |
79 | * description packets based on the information in this snapshot. The database | |
80 | * can be quite large in some networks, so rather we walk through a &slist | |
81 | * structure which allows us to continue even if the actual LSA we were working | |
82 | * with is deleted. New LSAs are added at the tail of this &slist. | |
89755a86 | 83 | * |
a7a7372a OZ |
84 | * We also do not keep a separate OSPF routing table, because the core helps us |
85 | * by being able to recognize when a route is updated to an identical one and it | |
86 | * suppresses the update automatically. Due to this, we can flush all the routes | |
87 | * we have recalculated and also those we have deleted to the core's routing | |
88 | * table and the core will take care of the rest. This simplifies the process | |
725270cb | 89 | * and conserves memory. |
178a197a OZ |
90 | * |
91 | * Supported standards: | |
92 | * - RFC 2328 - main OSPFv2 standard | |
93 | * - RFC 5340 - main OSPFv3 standard | |
94 | * - RFC 3101 - OSPFv2 NSSA areas | |
1a2ad348 | 95 | * - RFC 3623 - OSPFv2 Graceful Restart |
4a3f5b36 | 96 | * - RFC 4576 - OSPFv2 VPN loop prevention |
1a2ad348 | 97 | * - RFC 5187 - OSPFv3 Graceful Restart |
5a50a989 | 98 | * - RFC 5250 - OSPFv2 Opaque LSAs |
d3f4f92b OZ |
99 | * - RFC 5709 - OSPFv2 HMAC-SHA Cryptographic Authentication |
100 | * - RFC 5838 - OSPFv3 Support of Address Families | |
101 | * - RFC 6549 - OSPFv2 Multi-Instance Extensions | |
102 | * - RFC 6987 - OSPF Stub Router Advertisement | |
0f40405f | 103 | * - RFC 7166 - OSPFv3 Authentication Trailer |
5a50a989 | 104 | * - RFC 7770 - OSPF Router Information LSA |
89755a86 OF |
105 | */ |
106 | ||
a3b70dc4 | 107 | #include <stdlib.h> |
c1f8dc91 OF |
108 | #include "ospf.h" |
109 | ||
14375237 | 110 | static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool); |
875cc073 OZ |
111 | static void ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool); |
112 | static void ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool); | |
f4a60a9b | 113 | static void ospf_reload_routes(struct channel *C); |
d83faf8d MM |
114 | static int ospf_rte_better(struct rte *new, struct rte *old); |
115 | static int ospf_rte_same(struct rte *new, struct rte *old); | |
b8f17cf1 OF |
116 | static void ospf_disp(timer *timer); |
117 | ||
98ac6176 OF |
118 | |
119 | static void | |
120 | add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac) | |
121 | { | |
70945cb6 OZ |
122 | struct ospf_proto *p = oa->po; |
123 | struct area_net_config *anc; | |
124 | struct area_net *an; | |
98ac6176 | 125 | |
d3f4f92b | 126 | fib_init(&oa->net_fib, p->p.pool, ospf_get_af(p), |
fe9f1a6d | 127 | sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL); |
d3f4f92b | 128 | fib_init(&oa->enet_fib, p->p.pool, ospf_get_af(p), |
fe9f1a6d | 129 | sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL); |
98ac6176 | 130 | |
70945cb6 OZ |
131 | WALK_LIST(anc, ac->net_list) |
132 | { | |
d44e686e | 133 | an = fib_get(&oa->net_fib, &anc->prefix); |
70945cb6 OZ |
134 | an->hidden = anc->hidden; |
135 | } | |
ed317862 | 136 | |
70945cb6 OZ |
137 | WALK_LIST(anc, ac->enet_list) |
138 | { | |
d44e686e | 139 | an = fib_get(&oa->enet_fib, &anc->prefix); |
70945cb6 OZ |
140 | an->hidden = anc->hidden; |
141 | an->tag = anc->tag; | |
142 | } | |
98ac6176 | 143 | } |
d83faf8d | 144 | |
d3f4f92b OZ |
145 | static inline uint |
146 | ospf_opts(struct ospf_proto *p) | |
147 | { | |
148 | if (ospf_is_v2(p)) | |
149 | return 0; | |
150 | ||
151 | return ((ospf_is_ip6(p) && !p->af_mc) ? OPT_V6 : 0) | | |
152 | (!p->stub_router ? OPT_R : 0) | (p->af_ext ? OPT_AF : 0); | |
153 | } | |
154 | ||
8e48831a | 155 | static void |
a7a7372a | 156 | ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac) |
8e48831a | 157 | { |
8e48831a OZ |
158 | struct ospf_area *oa; |
159 | ||
160 | OSPF_TRACE(D_EVENTS, "Adding area %R", ac->areaid); | |
161 | ||
70945cb6 OZ |
162 | oa = mb_allocz(p->p.pool, sizeof(struct ospf_area)); |
163 | add_tail(&p->area_list, NODE oa); | |
164 | p->areano++; | |
8e48831a OZ |
165 | |
166 | oa->ac = ac; | |
8e48831a OZ |
167 | oa->areaid = ac->areaid; |
168 | oa->rt = NULL; | |
70945cb6 | 169 | oa->po = p; |
fe9f1a6d | 170 | fib_init(&oa->rtr, p->p.pool, NET_IP4, sizeof(ort), OFFSETOF(ort, fn), 0, NULL); |
8e48831a OZ |
171 | add_area_nets(oa, ac); |
172 | ||
173 | if (oa->areaid == 0) | |
70945cb6 | 174 | p->backbone = oa; |
8e48831a | 175 | |
d3f4f92b | 176 | oa->options = ac->type | ospf_opts(p); |
a7a7372a OZ |
177 | |
178 | ospf_notify_rt_lsa(oa); | |
70945cb6 | 179 | } |
8e48831a | 180 | |
70945cb6 OZ |
181 | static void |
182 | ospf_flush_area(struct ospf_proto *p, u32 areaid) | |
183 | { | |
184 | struct top_hash_entry *en; | |
2918e610 | 185 | |
70945cb6 OZ |
186 | WALK_SLIST(en, p->lsal) |
187 | if ((LSA_SCOPE(en->lsa_type) == LSA_SCOPE_AREA) && (en->domain == areaid)) | |
188 | ospf_flush_lsa(p, en); | |
8e48831a OZ |
189 | } |
190 | ||
191 | static void | |
192 | ospf_area_remove(struct ospf_area *oa) | |
193 | { | |
70945cb6 | 194 | struct ospf_proto *p = oa->po; |
8e48831a OZ |
195 | OSPF_TRACE(D_EVENTS, "Removing area %R", oa->areaid); |
196 | ||
197 | /* We suppose that interfaces are already removed */ | |
70945cb6 | 198 | ospf_flush_area(p, oa->areaid); |
742029eb | 199 | |
8e48831a OZ |
200 | fib_free(&oa->rtr); |
201 | fib_free(&oa->net_fib); | |
ed317862 | 202 | fib_free(&oa->enet_fib); |
8e48831a | 203 | |
4160a9dd OZ |
204 | if (oa->translator_timer) |
205 | rfree(oa->translator_timer); | |
206 | ||
70945cb6 | 207 | p->areano--; |
8e48831a OZ |
208 | rem_node(NODE oa); |
209 | mb_free(oa); | |
210 | } | |
211 | ||
8e48831a | 212 | struct ospf_area * |
70945cb6 | 213 | ospf_find_area(struct ospf_proto *p, u32 aid) |
8e48831a OZ |
214 | { |
215 | struct ospf_area *oa; | |
70945cb6 | 216 | WALK_LIST(oa, p->area_list) |
8e48831a OZ |
217 | if (((struct ospf_area *) oa)->areaid == aid) |
218 | return oa; | |
219 | return NULL; | |
220 | } | |
221 | ||
222 | static struct ospf_iface * | |
70945cb6 | 223 | ospf_find_vlink(struct ospf_proto *p, u32 voa, u32 vid) |
8e48831a OZ |
224 | { |
225 | struct ospf_iface *ifa; | |
742029eb | 226 | WALK_LIST(ifa, p->iface_list) |
8e48831a OZ |
227 | if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa->areaid == voa) && (ifa->vid == vid)) |
228 | return ifa; | |
229 | return NULL; | |
230 | } | |
231 | ||
1a2ad348 OZ |
232 | static void |
233 | ospf_start_gr_recovery(struct ospf_proto *p) | |
234 | { | |
235 | OSPF_TRACE(D_EVENTS, "Graceful restart started"); | |
236 | ||
237 | p->gr_recovery = 1; | |
238 | p->gr_timeout = current_time() + (p->gr_time S); | |
239 | channel_graceful_restart_lock(p->p.main_channel); | |
240 | p->p.main_channel->gr_wait = 1; | |
241 | ||
242 | /* NOTE: We should get end of grace period from non-volatile storage */ | |
243 | } | |
244 | ||
245 | void | |
246 | ospf_stop_gr_recovery(struct ospf_proto *p) | |
247 | { | |
248 | p->gr_recovery = 0; | |
249 | p->gr_timeout = 0; | |
250 | channel_graceful_restart_unlock(p->p.main_channel); | |
251 | ||
252 | /* Reorigination of router/network LSAs is already scheduled */ | |
253 | ospf_mark_lsadb(p); | |
254 | ||
255 | /* | |
256 | * NOTE: We should move channel_graceful_restart_unlock() to the end of | |
257 | * ospf_disp() in order to have local LSA reorigination / LSAdb cleanup / | |
258 | * routing table recomputation before official end of GR. It does not matter | |
259 | * when we are single-threaded. | |
260 | */ | |
261 | } | |
262 | ||
c1f8dc91 | 263 | static int |
70945cb6 | 264 | ospf_start(struct proto *P) |
c1f8dc91 | 265 | { |
70945cb6 OZ |
266 | struct ospf_proto *p = (struct ospf_proto *) P; |
267 | struct ospf_config *c = (struct ospf_config *) (P->cf); | |
51cff78b | 268 | struct ospf_area_config *ac; |
f14032ef | 269 | |
70945cb6 OZ |
270 | p->router_id = proto_get_router_id(P->cf); |
271 | p->ospf2 = c->ospf2; | |
d3f4f92b OZ |
272 | p->af_ext = c->af_ext; |
273 | p->af_mc = c->af_mc; | |
70945cb6 OZ |
274 | p->rfc1583 = c->rfc1583; |
275 | p->stub_router = c->stub_router; | |
276 | p->merge_external = c->merge_external; | |
5a50a989 | 277 | p->instance_id = c->instance_id; |
70945cb6 | 278 | p->asbr = c->asbr; |
4a3f5b36 | 279 | p->vpn_pe = c->vpn_pe; |
70945cb6 | 280 | p->ecmp = c->ecmp; |
1a2ad348 OZ |
281 | p->gr_mode = c->gr_mode; |
282 | p->gr_time = c->gr_time; | |
70945cb6 | 283 | p->tick = c->tick; |
a6f79ca5 OZ |
284 | p->disp_timer = tm_new_init(P->pool, ospf_disp, p, p->tick S, 0); |
285 | tm_start(p->disp_timer, 100 MS); | |
70945cb6 OZ |
286 | p->lsab_size = 256; |
287 | p->lsab_used = 0; | |
288 | p->lsab = mb_alloc(P->pool, p->lsab_size); | |
4e276a89 | 289 | p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop)); |
70945cb6 OZ |
290 | init_list(&(p->iface_list)); |
291 | init_list(&(p->area_list)); | |
d3f4f92b | 292 | fib_init(&p->rtf, P->pool, ospf_get_af(p), sizeof(ort), OFFSETOF(ort, fn), 0, NULL); |
74c838a8 OZ |
293 | if (ospf_is_v3(p)) |
294 | idm_init(&p->idm, P->pool, 16); | |
70945cb6 | 295 | p->areano = 0; |
a7a7372a | 296 | p->gr = ospf_top_new(p, P->pool); |
70945cb6 | 297 | s_init_list(&(p->lsal)); |
5b1a92e6 | 298 | |
961671c0 | 299 | p->flood_event = ev_new_init(P->pool, ospf_flood_event, p); |
6f8bbaa1 | 300 | |
f8fefde3 OZ |
301 | p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 }; |
302 | p->log_lsa_tbf = (struct tbf){ .rate = 4, .burst = 20 }; | |
303 | ||
1a2ad348 OZ |
304 | /* Lock the channel when in GR recovery mode */ |
305 | if (p->p.gr_recovery && (p->gr_mode == OSPF_GR_ABLE)) | |
306 | ospf_start_gr_recovery(p); | |
307 | ||
2e10a170 | 308 | WALK_LIST(ac, c->area_list) |
a7a7372a | 309 | ospf_area_add(p, ac); |
6384c7d7 | 310 | |
48e5f32d | 311 | if (c->abr) |
70945cb6 | 312 | ospf_open_vlink_sk(p); |
48e5f32d | 313 | |
8e48831a OZ |
314 | /* Add all virtual links */ |
315 | struct ospf_iface_patt *ic; | |
316 | WALK_LIST(ic, c->vlink_list) | |
70945cb6 | 317 | ospf_iface_new_vlink(p, ic); |
6384c7d7 | 318 | |
c1f8dc91 OF |
319 | return PS_UP; |
320 | } | |
321 | ||
322 | static void | |
70945cb6 | 323 | ospf_dump(struct proto *P) |
c1f8dc91 | 324 | { |
70945cb6 | 325 | struct ospf_proto *p = (struct ospf_proto *) P; |
b332fcdf OF |
326 | struct ospf_iface *ifa; |
327 | struct ospf_neighbor *n; | |
c1f8dc91 | 328 | |
70945cb6 | 329 | OSPF_TRACE(D_EVENTS, "Area number: %d", p->areano); |
b332fcdf | 330 | |
70945cb6 | 331 | WALK_LIST(ifa, p->iface_list) |
b332fcdf | 332 | { |
48e5f32d | 333 | OSPF_TRACE(D_EVENTS, "Interface: %s", ifa->ifname); |
f14032ef | 334 | OSPF_TRACE(D_EVENTS, "state: %u", ifa->state); |
3aab39f5 OZ |
335 | OSPF_TRACE(D_EVENTS, "DR: %R", ifa->drid); |
336 | OSPF_TRACE(D_EVENTS, "BDR: %R", ifa->bdrid); | |
b332fcdf OF |
337 | WALK_LIST(n, ifa->neigh_list) |
338 | { | |
3aab39f5 | 339 | OSPF_TRACE(D_EVENTS, " neighbor %R in state %u", n->rid, n->state); |
b332fcdf OF |
340 | } |
341 | } | |
163b2073 | 342 | |
e81b440f | 343 | /* |
86c84d76 | 344 | OSPF_TRACE(D_EVENTS, "LSA graph dump start:"); |
70945cb6 | 345 | ospf_top_dump(p->gr, p); |
86c84d76 | 346 | OSPF_TRACE(D_EVENTS, "LSA graph dump finished"); |
e81b440f | 347 | */ |
c6c56264 | 348 | neigh_dump_all(); |
c1f8dc91 OF |
349 | } |
350 | ||
351 | static struct proto * | |
f4a60a9b | 352 | ospf_init(struct proto_config *CF) |
c1f8dc91 | 353 | { |
f4a60a9b OZ |
354 | struct ospf_config *cf = (struct ospf_config *) CF; |
355 | struct proto *P = proto_new(CF); | |
356 | ||
357 | P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); | |
70945cb6 | 358 | |
70945cb6 OZ |
359 | P->rt_notify = ospf_rt_notify; |
360 | P->if_notify = ospf_if_notify; | |
f4a60a9b | 361 | P->ifa_notify = cf->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3; |
14375237 | 362 | P->preexport = ospf_preexport; |
70945cb6 OZ |
363 | P->reload_routes = ospf_reload_routes; |
364 | P->make_tmp_attrs = ospf_make_tmp_attrs; | |
365 | P->store_tmp_attrs = ospf_store_tmp_attrs; | |
366 | P->rte_better = ospf_rte_better; | |
367 | P->rte_same = ospf_rte_same; | |
368 | ||
369 | return P; | |
c1f8dc91 OF |
370 | } |
371 | ||
bbd76b42 OF |
372 | /* If new is better return 1 */ |
373 | static int | |
374 | ospf_rte_better(struct rte *new, struct rte *old) | |
375 | { | |
2e10a170 OF |
376 | if (new->u.ospf.metric1 == LSINFINITY) |
377 | return 0; | |
bbd76b42 | 378 | |
98ac6176 OF |
379 | if(new->attrs->source < old->attrs->source) return 1; |
380 | if(new->attrs->source > old->attrs->source) return 0; | |
2e10a170 | 381 | |
98ac6176 | 382 | if(new->attrs->source == RTS_OSPF_EXT2) |
bbd76b42 | 383 | { |
98ac6176 OF |
384 | if(new->u.ospf.metric2 < old->u.ospf.metric2) return 1; |
385 | if(new->u.ospf.metric2 > old->u.ospf.metric2) return 0; | |
bbd76b42 | 386 | } |
d6a7e2fb | 387 | |
2e10a170 OF |
388 | if (new->u.ospf.metric1 < old->u.ospf.metric1) |
389 | return 1; | |
98ac6176 | 390 | |
2e10a170 | 391 | return 0; /* Old is shorter or same */ |
bbd76b42 OF |
392 | } |
393 | ||
00c1f79a OF |
394 | static int |
395 | ospf_rte_same(struct rte *new, struct rte *old) | |
396 | { | |
5919c66e MM |
397 | /* new->attrs == old->attrs always */ |
398 | return | |
399 | new->u.ospf.metric1 == old->u.ospf.metric1 && | |
400 | new->u.ospf.metric2 == old->u.ospf.metric2 && | |
c27b2449 OZ |
401 | new->u.ospf.tag == old->u.ospf.tag && |
402 | new->u.ospf.router_id == old->u.ospf.router_id; | |
5919c66e | 403 | } |
00c1f79a | 404 | |
70a38319 OF |
405 | |
406 | void | |
a7a7372a | 407 | ospf_schedule_rtcalc(struct ospf_proto *p) |
70a38319 | 408 | { |
70945cb6 | 409 | if (p->calcrt) |
2e10a170 | 410 | return; |
3df1e804 | 411 | |
be862406 | 412 | OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation"); |
70945cb6 | 413 | p->calcrt = 1; |
70a38319 OF |
414 | } |
415 | ||
f4a60a9b OZ |
416 | static void |
417 | ospf_reload_routes(struct channel *C) | |
f7574707 | 418 | { |
f4a60a9b | 419 | struct ospf_proto *p = (struct ospf_proto *) C->proto; |
f7574707 | 420 | |
f4a60a9b OZ |
421 | if (p->calcrt == 2) |
422 | return; | |
f7574707 | 423 | |
f4a60a9b | 424 | OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload"); |
70945cb6 | 425 | p->calcrt = 2; |
f7574707 OZ |
426 | } |
427 | ||
b8f17cf1 | 428 | |
86c84d76 | 429 | /** |
2918e610 | 430 | * ospf_disp - invokes routing table calculation, aging and also area_disp() |
70945cb6 OZ |
431 | * @timer: timer usually called every @ospf_proto->tick second, @timer->data |
432 | * point to @ospf_proto | |
86c84d76 | 433 | */ |
70945cb6 | 434 | static void |
b8f17cf1 OF |
435 | ospf_disp(timer * timer) |
436 | { | |
70945cb6 | 437 | struct ospf_proto *p = timer->data; |
3b16080c | 438 | |
1a2ad348 OZ |
439 | if (p->gr_recovery) |
440 | ospf_update_gr_recovery(p); | |
441 | ||
70945cb6 OZ |
442 | /* Originate or flush local topology LSAs */ |
443 | ospf_update_topology(p); | |
e59e310e | 444 | |
a7a7372a | 445 | /* Process LSA DB */ |
70945cb6 | 446 | ospf_update_lsadb(p); |
86c84d76 | 447 | |
e59e310e | 448 | /* Calculate routing table */ |
70945cb6 OZ |
449 | if (p->calcrt) |
450 | ospf_rt_spf(p); | |
70a38319 OF |
451 | } |
452 | ||
b8f17cf1 | 453 | |
6f58dc64 | 454 | /** |
14375237 | 455 | * ospf_preexport - accept or reject new route from nest's routing table |
a7a7372a | 456 | * @P: OSPF protocol instance |
6567e6cf | 457 | * @new: the new route |
baa5dd6c OF |
458 | * @attrs: list of attributes |
459 | * @pool: pool for allocation of attributes | |
6f58dc64 | 460 | * |
baa5dd6c OF |
461 | * Its quite simple. It does not accept our own routes and leaves the decision on |
462 | * import to the filters. | |
6f58dc64 | 463 | */ |
70945cb6 | 464 | static int |
14375237 | 465 | ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED) |
2d5b9992 | 466 | { |
70945cb6 OZ |
467 | struct ospf_proto *p = (struct ospf_proto *) P; |
468 | struct ospf_area *oa = ospf_main_area(p); | |
2e10a170 | 469 | rte *e = *new; |
2d5b9992 | 470 | |
feae132e | 471 | /* Reject our own routes */ |
70945cb6 | 472 | if (e->attrs->src->proto == P) |
feae132e | 473 | return -1; |
ba5e5940 | 474 | |
feae132e | 475 | /* Do not export routes to stub areas */ |
41b612c3 | 476 | if (oa_is_stub(oa)) |
feae132e | 477 | return -1; |
41b612c3 | 478 | |
feae132e | 479 | return 0; |
5919c66e | 480 | } |
2d5b9992 | 481 | |
875cc073 | 482 | static void |
5919c66e MM |
483 | ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool) |
484 | { | |
875cc073 OZ |
485 | rte_init_tmp_attrs(rt, pool, 4); |
486 | rte_make_tmp_attr(rt, EA_OSPF_METRIC1, EAF_TYPE_INT, rt->u.ospf.metric1); | |
487 | rte_make_tmp_attr(rt, EA_OSPF_METRIC2, EAF_TYPE_INT, rt->u.ospf.metric2); | |
488 | rte_make_tmp_attr(rt, EA_OSPF_TAG, EAF_TYPE_INT, rt->u.ospf.tag); | |
489 | rte_make_tmp_attr(rt, EA_OSPF_ROUTER_ID, EAF_TYPE_ROUTER_ID, rt->u.ospf.router_id); | |
5919c66e MM |
490 | } |
491 | ||
70945cb6 | 492 | static void |
875cc073 | 493 | ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool) |
5919c66e | 494 | { |
875cc073 | 495 | rte_init_tmp_attrs(rt, pool, 4); |
9aa77fcc OZ |
496 | rt->u.ospf.metric1 = rte_store_tmp_attr(rt, EA_OSPF_METRIC1); |
497 | rt->u.ospf.metric2 = rte_store_tmp_attr(rt, EA_OSPF_METRIC2); | |
498 | rt->u.ospf.tag = rte_store_tmp_attr(rt, EA_OSPF_TAG); | |
499 | rt->u.ospf.router_id = rte_store_tmp_attr(rt, EA_OSPF_ROUTER_ID); | |
2d5b9992 OF |
500 | } |
501 | ||
6f58dc64 | 502 | /** |
baa5dd6c | 503 | * ospf_shutdown - Finish of OSPF instance |
a7a7372a | 504 | * @P: OSPF protocol instance |
6f58dc64 | 505 | * |
baa5dd6c | 506 | * RFC does not define any action that should be taken before router |
6f58dc64 OF |
507 | * shutdown. To make my neighbors react as fast as possible, I send |
508 | * them hello packet with empty neighbor list. They should start | |
baa5dd6c | 509 | * their neighbor state machine with event %NEIGHBOR_1WAY. |
6f58dc64 | 510 | */ |
4bd28fb6 | 511 | static int |
70945cb6 | 512 | ospf_shutdown(struct proto *P) |
4bd28fb6 | 513 | { |
70945cb6 | 514 | struct ospf_proto *p = (struct ospf_proto *) P; |
4bd28fb6 | 515 | struct ospf_iface *ifa; |
70945cb6 | 516 | |
ee4880c8 | 517 | OSPF_TRACE(D_EVENTS, "Shutdown requested"); |
4bd28fb6 | 518 | |
1a2ad348 OZ |
519 | if ((P->down_code == PDC_CMD_GR_DOWN) && (p->gr_mode == OSPF_GR_ABLE)) |
520 | { | |
521 | /* Originate Grace LSAs */ | |
522 | WALK_LIST(ifa, p->iface_list) | |
523 | ospf_originate_gr_lsa(p, ifa); | |
524 | } | |
525 | else | |
526 | { | |
527 | /* Send to all my neighbors 1WAY */ | |
528 | WALK_LIST(ifa, p->iface_list) | |
529 | ospf_iface_shutdown(ifa); | |
530 | } | |
2e10a170 | 531 | |
57c574d8 | 532 | /* Cleanup locked rta entries */ |
600998fc | 533 | FIB_WALK(&p->rtf, ort, nf) |
57c574d8 | 534 | { |
600998fc | 535 | rta_free(nf->old_rta); |
57c574d8 OZ |
536 | } |
537 | FIB_WALK_END; | |
538 | ||
4bd28fb6 OF |
539 | return PS_DOWN; |
540 | } | |
541 | ||
3d15dcdb | 542 | static void |
70945cb6 | 543 | ospf_get_status(struct proto *P, byte * buf) |
e8085aba | 544 | { |
70945cb6 | 545 | struct ospf_proto *p = (struct ospf_proto *) P; |
e8085aba | 546 | |
70945cb6 | 547 | if (p->p.proto_state == PS_DOWN) |
2e10a170 | 548 | buf[0] = 0; |
4414d9a5 OF |
549 | else |
550 | { | |
551 | struct ospf_iface *ifa; | |
552 | struct ospf_neighbor *n; | |
2e10a170 | 553 | int adj = 0; |
4414d9a5 | 554 | |
70945cb6 | 555 | WALK_LIST(ifa, p->iface_list) |
2e10a170 OF |
556 | WALK_LIST(n, ifa->neigh_list) if (n->state == NEIGHBOR_FULL) |
557 | adj = 1; | |
4414d9a5 | 558 | |
2e10a170 OF |
559 | if (adj == 0) |
560 | strcpy(buf, "Alone"); | |
561 | else | |
562 | strcpy(buf, "Running"); | |
4414d9a5 OF |
563 | } |
564 | } | |
565 | ||
f7c0525e | 566 | static void |
13c0be19 | 567 | ospf_get_route_info(rte * rte, byte * buf) |
f7c0525e | 568 | { |
98ac6176 | 569 | char *type = "<bug>"; |
f7c0525e | 570 | |
48e5f32d | 571 | switch (rte->attrs->source) |
05dbc97b | 572 | { |
70945cb6 OZ |
573 | case RTS_OSPF: |
574 | type = "I"; | |
575 | break; | |
576 | case RTS_OSPF_IA: | |
577 | type = "IA"; | |
578 | break; | |
579 | case RTS_OSPF_EXT1: | |
580 | type = "E1"; | |
581 | break; | |
582 | case RTS_OSPF_EXT2: | |
583 | type = "E2"; | |
584 | break; | |
05dbc97b | 585 | } |
98ac6176 OF |
586 | |
587 | buf += bsprintf(buf, " %s", type); | |
d6a7e2fb | 588 | buf += bsprintf(buf, " (%d/%d", rte->pref, rte->u.ospf.metric1); |
98ac6176 | 589 | if (rte->attrs->source == RTS_OSPF_EXT2) |
2e10a170 | 590 | buf += bsprintf(buf, "/%d", rte->u.ospf.metric2); |
d6a7e2fb | 591 | buf += bsprintf(buf, ")"); |
41b612c3 | 592 | if ((rte->attrs->source == RTS_OSPF_EXT1 || rte->attrs->source == RTS_OSPF_EXT2) && rte->u.ospf.tag) |
a7a3a0a3 | 593 | { |
23df5e4c | 594 | buf += bsprintf(buf, " [%x]", rte->u.ospf.tag); |
a7a3a0a3 | 595 | } |
c27b2449 OZ |
596 | if (rte->u.ospf.router_id) |
597 | buf += bsprintf(buf, " [%R]", rte->u.ospf.router_id); | |
f7c0525e OF |
598 | } |
599 | ||
5919c66e | 600 | static int |
aebe06b4 | 601 | ospf_get_attr(eattr * a, byte * buf, int buflen UNUSED) |
5919c66e MM |
602 | { |
603 | switch (a->id) | |
2e10a170 OF |
604 | { |
605 | case EA_OSPF_METRIC1: | |
606 | bsprintf(buf, "metric1"); | |
607 | return GA_NAME; | |
608 | case EA_OSPF_METRIC2: | |
609 | bsprintf(buf, "metric2"); | |
610 | return GA_NAME; | |
611 | case EA_OSPF_TAG: | |
126683fe | 612 | bsprintf(buf, "tag: 0x%08x", a->u.data); |
2e10a170 | 613 | return GA_FULL; |
126683fe OZ |
614 | case EA_OSPF_ROUTER_ID: |
615 | bsprintf(buf, "router_id"); | |
616 | return GA_NAME; | |
2e10a170 OF |
617 | default: |
618 | return GA_UNKNOWN; | |
619 | } | |
5919c66e | 620 | } |
6f58dc64 | 621 | |
8e48831a OZ |
622 | static void |
623 | ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) | |
edc34dc9 | 624 | { |
70945cb6 OZ |
625 | struct ospf_proto *p = oa->po; |
626 | struct ospf_area_config *oac = oa->ac; | |
bf8d7bba | 627 | struct ospf_iface *ifa, *ifx; |
70945cb6 | 628 | |
8e48831a | 629 | oa->ac = nac; |
d3f4f92b | 630 | oa->options = nac->type | ospf_opts(p); |
8e48831a | 631 | |
70945cb6 OZ |
632 | if (nac->type != oac->type) |
633 | { | |
bf8d7bba OZ |
634 | log(L_INFO "%s: Restarting area %R", p->p.name, oa->areaid); |
635 | ||
636 | /* Remove area interfaces, will be re-added later */ | |
637 | WALK_LIST_DELSAFE(ifa, ifx, p->iface_list) | |
70945cb6 | 638 | if (ifa->oa == oa) |
bf8d7bba OZ |
639 | { |
640 | ospf_iface_shutdown(ifa); | |
641 | ospf_iface_remove(ifa); | |
642 | } | |
643 | ||
644 | /* Flush area LSAs */ | |
645 | ospf_flush_area(p, oa->areaid); | |
70945cb6 | 646 | } |
8e48831a OZ |
647 | |
648 | /* Handle net_list */ | |
649 | fib_free(&oa->net_fib); | |
ed317862 | 650 | fib_free(&oa->enet_fib); |
8e48831a OZ |
651 | add_area_nets(oa, nac); |
652 | ||
653 | /* No need to handle stubnet_list */ | |
654 | ||
655 | oa->marked = 0; | |
70945cb6 | 656 | ospf_notify_rt_lsa(oa); |
edc34dc9 | 657 | } |
5919c66e | 658 | |
6f58dc64 OF |
659 | /** |
660 | * ospf_reconfigure - reconfiguration hook | |
a7a7372a | 661 | * @P: current instance of protocol (with old configuration) |
6f58dc64 OF |
662 | * @c: new configuration requested by user |
663 | * | |
baa5dd6c | 664 | * This hook tries to be a little bit intelligent. Instance of OSPF |
6f58dc64 | 665 | * will survive change of many constants like hello interval, |
baa5dd6c | 666 | * password change, addition or deletion of some neighbor on |
6f58dc64 OF |
667 | * nonbroadcast network, cost of interface, etc. |
668 | */ | |
80787d41 | 669 | static int |
f4a60a9b | 670 | ospf_reconfigure(struct proto *P, struct proto_config *CF) |
80787d41 | 671 | { |
70945cb6 OZ |
672 | struct ospf_proto *p = (struct ospf_proto *) P; |
673 | struct ospf_config *old = (struct ospf_config *) (P->cf); | |
f4a60a9b | 674 | struct ospf_config *new = (struct ospf_config *) CF; |
bf8d7bba | 675 | struct ospf_area_config *oac, *nac; |
8e48831a OZ |
676 | struct ospf_area *oa, *oax; |
677 | struct ospf_iface *ifa, *ifx; | |
678 | struct ospf_iface_patt *ip; | |
679 | ||
f4a60a9b | 680 | if (proto_get_router_id(CF) != p->router_id) |
79b4e12e OZ |
681 | return 0; |
682 | ||
23c212e7 OZ |
683 | if (p->ospf2 != new->ospf2) |
684 | return 0; | |
685 | ||
70945cb6 | 686 | if (p->rfc1583 != new->rfc1583) |
d82fc18d OZ |
687 | return 0; |
688 | ||
5a50a989 OZ |
689 | if (p->instance_id != new->instance_id) |
690 | return 0; | |
691 | ||
8e48831a OZ |
692 | if (old->abr != new->abr) |
693 | return 0; | |
edc34dc9 | 694 | |
bf8d7bba OZ |
695 | if (p->areano == 1) |
696 | { | |
697 | oac = HEAD(old->area_list); | |
698 | nac = HEAD(new->area_list); | |
699 | ||
700 | if (oac->type != nac->type) | |
701 | return 0; | |
702 | } | |
703 | ||
4a3f5b36 OZ |
704 | if (old->vpn_pe != new->vpn_pe) |
705 | return 0; | |
706 | ||
d3f4f92b OZ |
707 | if ((p->af_ext != new->af_ext) || (p->af_mc != new->af_mc)) |
708 | return 0; | |
709 | ||
f4a60a9b OZ |
710 | if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) |
711 | return 0; | |
712 | ||
70945cb6 OZ |
713 | p->stub_router = new->stub_router; |
714 | p->merge_external = new->merge_external; | |
715 | p->asbr = new->asbr; | |
716 | p->ecmp = new->ecmp; | |
1a2ad348 OZ |
717 | p->gr_mode = new->gr_mode; |
718 | p->gr_time = new->gr_time; | |
70945cb6 | 719 | p->tick = new->tick; |
02552526 | 720 | p->disp_timer->recurrent = p->tick S; |
bf8d7bba | 721 | tm_start(p->disp_timer, 10 MS); |
52d61a84 | 722 | |
8e48831a | 723 | /* Mark all areas and ifaces */ |
70945cb6 | 724 | WALK_LIST(oa, p->area_list) |
8e48831a | 725 | oa->marked = 1; |
edc34dc9 | 726 | |
70945cb6 | 727 | WALK_LIST(ifa, p->iface_list) |
8e48831a | 728 | ifa->marked = 1; |
2e10a170 | 729 | |
8e48831a OZ |
730 | /* Add and update areas */ |
731 | WALK_LIST(nac, new->area_list) | |
edc34dc9 | 732 | { |
70945cb6 | 733 | oa = ospf_find_area(p, nac->areaid); |
8e48831a OZ |
734 | if (oa) |
735 | ospf_area_reconfigure(oa, nac); | |
736 | else | |
a7a7372a | 737 | ospf_area_add(p, nac); |
8e48831a | 738 | } |
c926eee7 | 739 | |
70945cb6 OZ |
740 | /* Add and update interfaces */ |
741 | ospf_reconfigure_ifaces(p); | |
742 | ||
8e48831a OZ |
743 | /* Add and update vlinks */ |
744 | WALK_LIST(ip, new->vlink_list) | |
745 | { | |
70945cb6 | 746 | ifa = ospf_find_vlink(p, ip->voa, ip->vid); |
8e48831a OZ |
747 | if (ifa) |
748 | ospf_iface_reconfigure(ifa, ip); | |
749 | else | |
70945cb6 | 750 | ospf_iface_new_vlink(p, ip); |
8e48831a | 751 | } |
edc34dc9 | 752 | |
8e48831a | 753 | /* Delete remaining ifaces and areas */ |
70945cb6 | 754 | WALK_LIST_DELSAFE(ifa, ifx, p->iface_list) |
8e48831a | 755 | if (ifa->marked) |
edc34dc9 | 756 | { |
8e48831a OZ |
757 | ospf_iface_shutdown(ifa); |
758 | ospf_iface_remove(ifa); | |
edc34dc9 OF |
759 | } |
760 | ||
70945cb6 | 761 | WALK_LIST_DELSAFE(oa, oax, p->area_list) |
8e48831a OZ |
762 | if (oa->marked) |
763 | ospf_area_remove(oa); | |
edc34dc9 | 764 | |
a7a7372a | 765 | ospf_schedule_rtcalc(p); |
742029eb | 766 | |
8e48831a | 767 | return 1; |
80787d41 | 768 | } |
a783e259 | 769 | |
8e48831a | 770 | |
a783e259 | 771 | void |
70945cb6 | 772 | ospf_sh_neigh(struct proto *P, char *iff) |
a783e259 | 773 | { |
70945cb6 | 774 | struct ospf_proto *p = (struct ospf_proto *) P; |
e81b440f | 775 | struct ospf_iface *ifa = NULL; |
a783e259 | 776 | struct ospf_neighbor *n; |
3488634c | 777 | |
70945cb6 | 778 | if (p->p.proto_state != PS_UP) |
3488634c | 779 | { |
70945cb6 | 780 | cli_msg(-1013, "%s: is not up", p->p.name); |
2e10a170 | 781 | cli_msg(0, ""); |
3488634c OF |
782 | return; |
783 | } | |
2e10a170 | 784 | |
70945cb6 | 785 | cli_msg(-1013, "%s:", p->p.name); |
cf31112f OZ |
786 | cli_msg(-1013, "%-12s\t%3s\t%-15s\t%-5s\t%-10s %-12s", "Router ID", "Pri", |
787 | " State", "DTime", "Interface", "Router IP"); | |
70945cb6 | 788 | WALK_LIST(ifa, p->iface_list) |
48e5f32d | 789 | if ((iff == NULL) || patmatch(iff, ifa->ifname)) |
610bb3cf OZ |
790 | WALK_LIST(n, ifa->neigh_list) |
791 | ospf_sh_neigh_info(n); | |
2e10a170 | 792 | cli_msg(0, ""); |
a783e259 | 793 | } |
4ab4e977 OF |
794 | |
795 | void | |
70945cb6 | 796 | ospf_sh(struct proto *P) |
4ab4e977 | 797 | { |
70945cb6 | 798 | struct ospf_proto *p = (struct ospf_proto *) P; |
4ab4e977 | 799 | struct ospf_area *oa; |
4ab4e977 OF |
800 | struct ospf_iface *ifa; |
801 | struct ospf_neighbor *n; | |
98ac6176 | 802 | int ifano, nno, adjno, firstfib; |
4ab4e977 | 803 | |
70945cb6 | 804 | if (p->p.proto_state != PS_UP) |
3488634c | 805 | { |
70945cb6 | 806 | cli_msg(-1014, "%s: is not up", p->p.name); |
2e10a170 | 807 | cli_msg(0, ""); |
3488634c OF |
808 | return; |
809 | } | |
810 | ||
70945cb6 | 811 | cli_msg(-1014, "%s:", p->p.name); |
f8fefde3 | 812 | cli_msg(-1014, "RFC1583 compatibility: %s", (p->rfc1583 ? "enabled" : "disabled")); |
70945cb6 OZ |
813 | cli_msg(-1014, "Stub router: %s", (p->stub_router ? "Yes" : "No")); |
814 | cli_msg(-1014, "RT scheduler tick: %d", p->tick); | |
815 | cli_msg(-1014, "Number of areas: %u", p->areano); | |
816 | cli_msg(-1014, "Number of LSAs in DB:\t%u", p->gr->hash_entries); | |
2e10a170 | 817 | |
70945cb6 | 818 | WALK_LIST(oa, p->area_list) |
4ab4e977 | 819 | { |
3aab39f5 | 820 | cli_msg(-1014, "\tArea: %R (%u) %s", oa->areaid, oa->areaid, |
2e10a170 OF |
821 | oa->areaid == 0 ? "[BACKBONE]" : ""); |
822 | ifano = 0; | |
823 | nno = 0; | |
824 | adjno = 0; | |
70945cb6 | 825 | WALK_LIST(ifa, p->iface_list) |
4ab4e977 | 826 | { |
2e10a170 | 827 | if (oa == ifa->oa) |
4ab4e977 | 828 | { |
3b16080c | 829 | ifano++; |
742029eb OZ |
830 | WALK_LIST(n, ifa->neigh_list) |
831 | { | |
3b16080c OF |
832 | nno++; |
833 | if (n->state == NEIGHBOR_FULL) | |
834 | adjno++; | |
742029eb | 835 | } |
4ab4e977 OF |
836 | } |
837 | } | |
ed317862 OZ |
838 | |
839 | cli_msg(-1014, "\t\tStub:\t%s", oa_is_stub(oa) ? "Yes" : "No"); | |
840 | cli_msg(-1014, "\t\tNSSA:\t%s", oa_is_nssa(oa) ? "Yes" : "No"); | |
3b16080c | 841 | cli_msg(-1014, "\t\tTransit:\t%s", oa->trcap ? "Yes" : "No"); |
ed317862 OZ |
842 | |
843 | if (oa_is_nssa(oa)) | |
844 | cli_msg(-1014, "\t\tNSSA translation:\t%s%s", oa->translate ? "Yes" : "No", | |
845 | oa->translate == TRANS_WAIT ? " (run down)" : ""); | |
2e10a170 | 846 | cli_msg(-1014, "\t\tNumber of interfaces:\t%u", ifano); |
2e10a170 OF |
847 | cli_msg(-1014, "\t\tNumber of neighbors:\t%u", nno); |
848 | cli_msg(-1014, "\t\tNumber of adjacent neighbors:\t%u", adjno); | |
98ac6176 OF |
849 | |
850 | firstfib = 1; | |
600998fc | 851 | FIB_WALK(&oa->net_fib, struct area_net, anet) |
c926eee7 | 852 | { |
98ac6176 | 853 | if(firstfib) |
c926eee7 | 854 | { |
742029eb OZ |
855 | cli_msg(-1014, "\t\tArea networks:"); |
856 | firstfib = 0; | |
c926eee7 | 857 | } |
fe9f1a6d | 858 | cli_msg(-1014, "\t\t\t%1N\t%s\t%s", anet->fn.addr, |
98ac6176 | 859 | anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : ""); |
c926eee7 | 860 | } |
98ac6176 | 861 | FIB_WALK_END; |
ed317862 OZ |
862 | |
863 | firstfib = 1; | |
600998fc | 864 | FIB_WALK(&oa->enet_fib, struct area_net, anet) |
ed317862 | 865 | { |
ed317862 OZ |
866 | if(firstfib) |
867 | { | |
742029eb OZ |
868 | cli_msg(-1014, "\t\tArea external networks:"); |
869 | firstfib = 0; | |
ed317862 | 870 | } |
fe9f1a6d | 871 | cli_msg(-1014, "\t\t\t%1N\t%s\t%s", anet->fn.addr, |
ed317862 OZ |
872 | anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : ""); |
873 | } | |
874 | FIB_WALK_END; | |
875 | ||
4ab4e977 | 876 | } |
2e10a170 | 877 | cli_msg(0, ""); |
4ab4e977 OF |
878 | } |
879 | ||
c4f0f014 | 880 | void |
70945cb6 | 881 | ospf_sh_iface(struct proto *P, char *iff) |
c4f0f014 | 882 | { |
70945cb6 | 883 | struct ospf_proto *p = (struct ospf_proto *) P; |
e81b440f | 884 | struct ospf_iface *ifa = NULL; |
c4f0f014 | 885 | |
70945cb6 | 886 | if (p->p.proto_state != PS_UP) |
3488634c | 887 | { |
70945cb6 | 888 | cli_msg(-1015, "%s: is not up", p->p.name); |
2e10a170 | 889 | cli_msg(0, ""); |
3488634c OF |
890 | return; |
891 | } | |
892 | ||
70945cb6 OZ |
893 | cli_msg(-1015, "%s:", p->p.name); |
894 | WALK_LIST(ifa, p->iface_list) | |
48e5f32d | 895 | if ((iff == NULL) || patmatch(iff, ifa->ifname)) |
610bb3cf | 896 | ospf_iface_info(ifa); |
2e10a170 | 897 | cli_msg(0, ""); |
c4f0f014 OF |
898 | } |
899 | ||
b66abe8e OZ |
900 | /* lsa_compare_for_state() - Compare function for 'show ospf state' |
901 | * | |
902 | * First we want to separate network-LSAs and other LSAs (because network-LSAs | |
a3b70dc4 OZ |
903 | * will be presented as network nodes and other LSAs together as router nodes) |
904 | * Network-LSAs are sorted according to network prefix, other LSAs are sorted | |
905 | * according to originating router id (to get all LSA needed to represent one | |
906 | * router node together). Then, according to LSA type, ID and age. | |
b66abe8e OZ |
907 | * |
908 | * For OSPFv3, we have to handle also Prefix-LSAs. We would like to put each | |
909 | * immediately after the referenced LSA. We will make faked LSA based on ref_ | |
910 | * values | |
a3b70dc4 | 911 | */ |
b66abe8e OZ |
912 | |
913 | static struct ospf_lsa_header * | |
914 | fake_lsa_from_prefix_lsa(struct ospf_lsa_header *dst, struct ospf_lsa_header *src, | |
915 | struct ospf_lsa_prefix *px) | |
916 | { | |
917 | dst->age = src->age; | |
70945cb6 | 918 | dst->type_raw = px->ref_type; |
b66abe8e OZ |
919 | dst->id = px->ref_id; |
920 | dst->rt = px->ref_rt; | |
921 | dst->sn = src->sn; | |
922 | ||
923 | return dst; | |
924 | } | |
925 | ||
70945cb6 OZ |
926 | |
927 | static int lsa_compare_ospf3; | |
98955023 | 928 | |
a3b70dc4 | 929 | static int |
b66abe8e | 930 | lsa_compare_for_state(const void *p1, const void *p2) |
a3b70dc4 | 931 | { |
70945cb6 OZ |
932 | struct top_hash_entry *he1 = * (struct top_hash_entry **) p1; |
933 | struct top_hash_entry *he2 = * (struct top_hash_entry **) p2; | |
a3b70dc4 OZ |
934 | struct ospf_lsa_header *lsa1 = &(he1->lsa); |
935 | struct ospf_lsa_header *lsa2 = &(he2->lsa); | |
70945cb6 OZ |
936 | struct ospf_lsa_header lsatmp1, lsatmp2; |
937 | u16 lsa1_type = he1->lsa_type; | |
938 | u16 lsa2_type = he2->lsa_type; | |
b66abe8e | 939 | |
8249ad9b OZ |
940 | if (he1->domain < he2->domain) |
941 | return -1; | |
942 | if (he1->domain > he2->domain) | |
943 | return 1; | |
b66abe8e | 944 | |
b66abe8e | 945 | |
70945cb6 OZ |
946 | /* px1 or px2 assumes OSPFv3 */ |
947 | int px1 = (lsa1_type == LSA_T_PREFIX); | |
948 | int px2 = (lsa2_type == LSA_T_PREFIX); | |
b66abe8e OZ |
949 | |
950 | if (px1) | |
70945cb6 | 951 | { |
b66abe8e | 952 | lsa1 = fake_lsa_from_prefix_lsa(&lsatmp1, lsa1, he1->lsa_body); |
70945cb6 OZ |
953 | lsa1_type = lsa1->type_raw; /* FIXME: handle unknown ref_type */ |
954 | } | |
b66abe8e OZ |
955 | |
956 | if (px2) | |
70945cb6 | 957 | { |
b66abe8e | 958 | lsa2 = fake_lsa_from_prefix_lsa(&lsatmp2, lsa2, he2->lsa_body); |
70945cb6 OZ |
959 | lsa2_type = lsa2->type_raw; |
960 | } | |
961 | ||
b66abe8e | 962 | |
70945cb6 OZ |
963 | int nt1 = (lsa1_type == LSA_T_NET); |
964 | int nt2 = (lsa2_type == LSA_T_NET); | |
a3b70dc4 | 965 | |
a3b70dc4 OZ |
966 | if (nt1 != nt2) |
967 | return nt1 - nt2; | |
968 | ||
969 | if (nt1) | |
970 | { | |
70945cb6 OZ |
971 | /* In OSPFv3, networks are named based on ID of DR */ |
972 | if (lsa_compare_ospf3) | |
973 | { | |
974 | if (lsa1->rt < lsa2->rt) | |
975 | return -1; | |
976 | if (lsa1->rt > lsa2->rt) | |
977 | return 1; | |
978 | } | |
b66abe8e OZ |
979 | |
980 | /* For OSPFv2, this is IP of the network, | |
981 | for OSPFv3, this is interface ID */ | |
8249ad9b OZ |
982 | if (lsa1->id < lsa2->id) |
983 | return -1; | |
984 | if (lsa1->id > lsa2->id) | |
985 | return 1; | |
b66abe8e | 986 | |
b66abe8e OZ |
987 | if (px1 != px2) |
988 | return px1 - px2; | |
b66abe8e OZ |
989 | |
990 | return lsa1->sn - lsa2->sn; | |
a3b70dc4 | 991 | } |
742029eb | 992 | else |
b66abe8e | 993 | { |
8249ad9b OZ |
994 | if (lsa1->rt < lsa2->rt) |
995 | return -1; | |
996 | if (lsa1->rt > lsa2->rt) | |
997 | return 1; | |
b66abe8e | 998 | |
70945cb6 | 999 | if (lsa1_type < lsa2_type) |
8249ad9b | 1000 | return -1; |
70945cb6 | 1001 | if (lsa1_type > lsa2_type) |
8249ad9b OZ |
1002 | return 1; |
1003 | ||
1004 | if (lsa1->id < lsa2->id) | |
1005 | return -1; | |
1006 | if (lsa1->id > lsa2->id) | |
1007 | return 1; | |
b66abe8e | 1008 | |
b66abe8e OZ |
1009 | if (px1 != px2) |
1010 | return px1 - px2; | |
742029eb | 1011 | |
b66abe8e OZ |
1012 | return lsa1->sn - lsa2->sn; |
1013 | } | |
a3b70dc4 | 1014 | } |
b66abe8e | 1015 | |
0ea8fb4a OZ |
1016 | static int |
1017 | ext_compare_for_state(const void *p1, const void *p2) | |
1018 | { | |
1019 | struct top_hash_entry * he1 = * (struct top_hash_entry **) p1; | |
1020 | struct top_hash_entry * he2 = * (struct top_hash_entry **) p2; | |
1021 | struct ospf_lsa_header *lsa1 = &(he1->lsa); | |
1022 | struct ospf_lsa_header *lsa2 = &(he2->lsa); | |
1023 | ||
8249ad9b OZ |
1024 | if (lsa1->rt < lsa2->rt) |
1025 | return -1; | |
1026 | if (lsa1->rt > lsa2->rt) | |
1027 | return 1; | |
1028 | ||
1029 | if (lsa1->id < lsa2->id) | |
1030 | return -1; | |
1031 | if (lsa1->id > lsa2->id) | |
1032 | return 1; | |
0ea8fb4a | 1033 | |
0ea8fb4a OZ |
1034 | return lsa1->sn - lsa2->sn; |
1035 | } | |
1036 | ||
1037 | static inline void | |
1038 | show_lsa_distance(struct top_hash_entry *he) | |
1039 | { | |
1040 | if (he->color == INSPF) | |
1041 | cli_msg(-1016, "\t\tdistance %u", he->dist); | |
1042 | else | |
1043 | cli_msg(-1016, "\t\tunreachable"); | |
1044 | } | |
1045 | ||
a3b70dc4 | 1046 | static inline void |
70945cb6 | 1047 | show_lsa_router(struct ospf_proto *p, struct top_hash_entry *he, int verbose) |
a3b70dc4 | 1048 | { |
70945cb6 | 1049 | struct ospf_lsa_rt_walk rtl; |
0ea8fb4a | 1050 | |
70945cb6 OZ |
1051 | cli_msg(-1016, ""); |
1052 | cli_msg(-1016, "\trouter %R", he->lsa.rt); | |
1053 | show_lsa_distance(he); | |
0ea8fb4a | 1054 | |
70945cb6 OZ |
1055 | lsa_walk_rt_init(p, he, &rtl); |
1056 | while (lsa_walk_rt(&rtl)) | |
1057 | if (rtl.type == LSART_VLNK) | |
1058 | cli_msg(-1016, "\t\tvlink %R metric %u", rtl.id, rtl.metric); | |
a3b70dc4 | 1059 | |
70945cb6 OZ |
1060 | lsa_walk_rt_init(p, he, &rtl); |
1061 | while (lsa_walk_rt(&rtl)) | |
1062 | if (rtl.type == LSART_PTP) | |
1063 | cli_msg(-1016, "\t\trouter %R metric %u", rtl.id, rtl.metric); | |
a3b70dc4 | 1064 | |
70945cb6 OZ |
1065 | lsa_walk_rt_init(p, he, &rtl); |
1066 | while (lsa_walk_rt(&rtl)) | |
1067 | if (rtl.type == LSART_NET) | |
a3b70dc4 | 1068 | { |
70945cb6 | 1069 | if (ospf_is_v2(p)) |
a3b70dc4 | 1070 | { |
70945cb6 OZ |
1071 | /* In OSPFv2, we try to find network-LSA to get prefix/pxlen */ |
1072 | struct top_hash_entry *net_he = ospf_hash_find_net2(p->gr, he->domain, rtl.id); | |
b66abe8e | 1073 | |
a7a7372a | 1074 | if (net_he && (net_he->lsa.age < LSA_MAXAGE)) |
70945cb6 OZ |
1075 | { |
1076 | struct ospf_lsa_header *net_lsa = &(net_he->lsa); | |
1077 | struct ospf_lsa_net *net_ln = net_he->lsa_body; | |
1078 | ||
742029eb | 1079 | cli_msg(-1016, "\t\tnetwork %I/%d metric %u", |
70945cb6 OZ |
1080 | ipa_from_u32(net_lsa->id & net_ln->optx), |
1081 | u32_masklen(net_ln->optx), rtl.metric); | |
1082 | } | |
1083 | else | |
1084 | cli_msg(-1016, "\t\tnetwork [%R] metric %u", rtl.id, rtl.metric); | |
a3b70dc4 OZ |
1085 | } |
1086 | else | |
70945cb6 | 1087 | cli_msg(-1016, "\t\tnetwork [%R-%u] metric %u", rtl.id, rtl.nif, rtl.metric); |
a3b70dc4 OZ |
1088 | } |
1089 | ||
70945cb6 OZ |
1090 | if (ospf_is_v2(p) && verbose) |
1091 | { | |
1092 | lsa_walk_rt_init(p, he, &rtl); | |
1093 | while (lsa_walk_rt(&rtl)) | |
1094 | if (rtl.type == LSART_STUB) | |
1095 | cli_msg(-1016, "\t\tstubnet %I/%d metric %u", | |
1096 | ipa_from_u32(rtl.id), u32_masklen(rtl.data), rtl.metric); | |
1097 | } | |
a3b70dc4 OZ |
1098 | } |
1099 | ||
1100 | static inline void | |
70945cb6 | 1101 | show_lsa_network(struct top_hash_entry *he, int ospf2) |
a3b70dc4 OZ |
1102 | { |
1103 | struct ospf_lsa_header *lsa = &(he->lsa); | |
1104 | struct ospf_lsa_net *ln = he->lsa_body; | |
a3b70dc4 OZ |
1105 | u32 i; |
1106 | ||
70945cb6 OZ |
1107 | if (ospf2) |
1108 | { | |
1109 | cli_msg(-1016, ""); | |
1110 | cli_msg(-1016, "\tnetwork %I/%d", ipa_from_u32(lsa->id & ln->optx), u32_masklen(ln->optx)); | |
1111 | cli_msg(-1016, "\t\tdr %R", lsa->rt); | |
1112 | } | |
1113 | else | |
1114 | { | |
1115 | cli_msg(-1016, ""); | |
1116 | cli_msg(-1016, "\tnetwork [%R-%u]", lsa->rt, lsa->id); | |
1117 | } | |
a3b70dc4 | 1118 | |
0ea8fb4a OZ |
1119 | show_lsa_distance(he); |
1120 | ||
c3226991 | 1121 | for (i = 0; i < lsa_net_count(lsa); i++) |
b66abe8e | 1122 | cli_msg(-1016, "\t\trouter %R", ln->routers[i]); |
a3b70dc4 OZ |
1123 | } |
1124 | ||
1125 | static inline void | |
d3f4f92b | 1126 | show_lsa_sum_net(struct top_hash_entry *he, int ospf2, int af) |
a3b70dc4 | 1127 | { |
fe9f1a6d | 1128 | net_addr net; |
b66abe8e | 1129 | u8 pxopts; |
70945cb6 | 1130 | u32 metric; |
b66abe8e | 1131 | |
d3f4f92b | 1132 | lsa_parse_sum_net(he, ospf2, af, &net, &pxopts, &metric); |
fe9f1a6d | 1133 | cli_msg(-1016, "\t\txnetwork %N metric %u", &net, metric); |
a3b70dc4 OZ |
1134 | } |
1135 | ||
1136 | static inline void | |
70945cb6 | 1137 | show_lsa_sum_rt(struct top_hash_entry *he, int ospf2) |
a3b70dc4 | 1138 | { |
70945cb6 | 1139 | u32 metric; |
d494df63 | 1140 | u32 dst_rid; |
70945cb6 | 1141 | u32 options; |
b66abe8e | 1142 | |
70945cb6 OZ |
1143 | lsa_parse_sum_rt(he, ospf2, &dst_rid, &metric, &options); |
1144 | cli_msg(-1016, "\t\txrouter %R metric %u", dst_rid, metric); | |
a3b70dc4 OZ |
1145 | } |
1146 | ||
1147 | ||
1148 | static inline void | |
d3f4f92b | 1149 | show_lsa_external(struct top_hash_entry *he, int ospf2, int af) |
a3b70dc4 | 1150 | { |
70945cb6 | 1151 | struct ospf_lsa_ext_local rt; |
7fd4143e | 1152 | char str_via[IPA_MAX_TEXT_LENGTH + 8] = ""; |
a3b70dc4 | 1153 | char str_tag[16] = ""; |
b66abe8e | 1154 | |
70945cb6 | 1155 | if (he->lsa_type == LSA_T_EXT) |
41b612c3 | 1156 | he->domain = 0; /* Unmark the LSA */ |
0ea8fb4a | 1157 | |
d3f4f92b | 1158 | lsa_parse_ext(he, ospf2, af, &rt); |
742029eb | 1159 | |
70945cb6 OZ |
1160 | if (rt.fbit) |
1161 | bsprintf(str_via, " via %I", rt.fwaddr); | |
a3b70dc4 | 1162 | |
70945cb6 OZ |
1163 | if (rt.tag) |
1164 | bsprintf(str_tag, " tag %08x", rt.tag); | |
a3b70dc4 | 1165 | |
fe9f1a6d | 1166 | cli_msg(-1016, "\t\t%s %N metric%s %u%s%s", |
70945cb6 | 1167 | (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external", |
fe9f1a6d | 1168 | &rt.net, rt.ebit ? "2" : "", rt.metric, str_via, str_tag); |
a3b70dc4 OZ |
1169 | } |
1170 | ||
b66abe8e | 1171 | static inline void |
d3f4f92b | 1172 | show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode, int af) |
b66abe8e | 1173 | { |
b66abe8e | 1174 | struct ospf_lsa_prefix *px = he->lsa_body; |
b66abe8e OZ |
1175 | u32 *buf; |
1176 | int i; | |
1177 | ||
0ea8fb4a | 1178 | /* We check whether given prefix-LSA is related to the current node */ |
70945cb6 | 1179 | if ((px->ref_type != cnode->lsa.type_raw) || (px->ref_rt != cnode->lsa.rt)) |
0ea8fb4a OZ |
1180 | return; |
1181 | ||
1182 | if ((px->ref_type == LSA_T_RT) && (px->ref_id != 0)) | |
1183 | return; | |
1184 | ||
70945cb6 | 1185 | if ((px->ref_type == LSA_T_NET) && (px->ref_id != cnode->lsa.id)) |
b66abe8e OZ |
1186 | return; |
1187 | ||
1188 | buf = px->rest; | |
1189 | for (i = 0; i < px->pxcount; i++) | |
fe9f1a6d OZ |
1190 | { |
1191 | net_addr net; | |
1192 | u8 pxopts; | |
1193 | u16 metric; | |
b66abe8e | 1194 | |
d3f4f92b | 1195 | buf = ospf3_get_prefix(buf, af, &net, &pxopts, &metric); |
fe9f1a6d OZ |
1196 | |
1197 | if (px->ref_type == LSA_T_RT) | |
1198 | cli_msg(-1016, "\t\tstubnet %N metric %u", &net, metric); | |
1199 | else | |
1200 | cli_msg(-1016, "\t\taddress %N", &net); | |
1201 | } | |
b66abe8e | 1202 | } |
b66abe8e | 1203 | |
a3b70dc4 | 1204 | void |
70945cb6 | 1205 | ospf_sh_state(struct proto *P, int verbose, int reachable) |
a3b70dc4 | 1206 | { |
70945cb6 OZ |
1207 | struct ospf_proto *p = (struct ospf_proto *) P; |
1208 | int ospf2 = ospf_is_v2(p); | |
d3f4f92b | 1209 | int af = ospf_get_af(p); |
70945cb6 | 1210 | uint i, ix, j1, jx; |
a3b70dc4 | 1211 | u32 last_area = 0xFFFFFFFF; |
b49e6f5a | 1212 | |
70945cb6 | 1213 | if (p->p.proto_state != PS_UP) |
a3b70dc4 | 1214 | { |
70945cb6 | 1215 | cli_msg(-1016, "%s: is not up", p->p.name); |
a3b70dc4 OZ |
1216 | cli_msg(0, ""); |
1217 | return; | |
1218 | } | |
1219 | ||
742029eb | 1220 | /* We store interesting area-scoped LSAs in array hea and |
0ea8fb4a OZ |
1221 | global-scoped (LSA_T_EXT) LSAs in array hex */ |
1222 | ||
e87a95d9 | 1223 | uint num = p->gr->hash_entries; |
0ea8fb4a OZ |
1224 | struct top_hash_entry *hea[num]; |
1225 | struct top_hash_entry *hex[verbose ? num : 0]; | |
a3b70dc4 | 1226 | struct top_hash_entry *he; |
70945cb6 | 1227 | struct top_hash_entry *cnode = NULL; |
a3b70dc4 | 1228 | |
70945cb6 OZ |
1229 | j1 = jx = 0; |
1230 | WALK_SLIST(he, p->lsal) | |
b66abe8e OZ |
1231 | { |
1232 | int accept; | |
1233 | ||
70945cb6 OZ |
1234 | if (he->lsa.age == LSA_MAXAGE) |
1235 | continue; | |
b66abe8e | 1236 | |
70945cb6 OZ |
1237 | switch (he->lsa_type) |
1238 | { | |
1239 | case LSA_T_RT: | |
1240 | case LSA_T_NET: | |
1241 | accept = 1; | |
1242 | break; | |
b66abe8e | 1243 | |
70945cb6 OZ |
1244 | case LSA_T_SUM_NET: |
1245 | case LSA_T_SUM_RT: | |
1246 | case LSA_T_NSSA: | |
1247 | case LSA_T_PREFIX: | |
1248 | accept = verbose; | |
1249 | break; | |
1250 | ||
1251 | case LSA_T_EXT: | |
1252 | if (verbose) | |
1253 | { | |
1254 | he->domain = 1; /* Abuse domain field to mark the LSA */ | |
1255 | hex[jx++] = he; | |
b66abe8e | 1256 | } |
d4cebc6b | 1257 | /* fallthrough */ |
70945cb6 OZ |
1258 | default: |
1259 | accept = 0; | |
1260 | } | |
a3b70dc4 | 1261 | |
b66abe8e OZ |
1262 | if (accept) |
1263 | hea[j1++] = he; | |
b66abe8e OZ |
1264 | } |
1265 | ||
70945cb6 | 1266 | ASSERT(j1 <= num && jx <= num); |
a3b70dc4 | 1267 | |
70945cb6 | 1268 | lsa_compare_ospf3 = !ospf2; |
b66abe8e | 1269 | qsort(hea, j1, sizeof(struct top_hash_entry *), lsa_compare_for_state); |
0ea8fb4a | 1270 | qsort(hex, jx, sizeof(struct top_hash_entry *), ext_compare_for_state); |
a3b70dc4 | 1271 | |
0ea8fb4a OZ |
1272 | /* |
1273 | * This code is a bit tricky, we have a primary LSAs (router and | |
1274 | * network) that are presented as a node, and secondary LSAs that | |
1275 | * are presented as a part of a primary node. cnode represents an | |
1276 | * currently opened node (whose header was presented). The LSAs are | |
1277 | * sorted to get secondary LSAs just after related primary LSA (if | |
1278 | * available). We present secondary LSAs only when related primary | |
1279 | * LSA is opened. | |
1280 | * | |
1281 | * AS-external LSAs are stored separately as they might be presented | |
1282 | * several times (for each area when related ASBR is opened). When | |
1283 | * the node is closed, related external routes are presented. We | |
1284 | * also have to take into account that in OSPFv3, there might be | |
1285 | * more router-LSAs and only the first should be considered as a | |
1286 | * primary. This is handled by not closing old router-LSA when next | |
1287 | * one is processed (which is not opened because there is already | |
1288 | * one opened). | |
1289 | */ | |
1290 | ||
1291 | ix = 0; | |
b66abe8e | 1292 | for (i = 0; i < j1; i++) |
a3b70dc4 | 1293 | { |
0ea8fb4a | 1294 | he = hea[i]; |
a3b70dc4 | 1295 | |
0ea8fb4a OZ |
1296 | /* If there is no opened node, we open the LSA (if appropriate) or skip to the next one */ |
1297 | if (!cnode) | |
a3b70dc4 | 1298 | { |
70945cb6 | 1299 | if (((he->lsa_type == LSA_T_RT) || (he->lsa_type == LSA_T_NET)) |
0ea8fb4a OZ |
1300 | && ((he->color == INSPF) || !reachable)) |
1301 | { | |
70945cb6 | 1302 | cnode = he; |
0ea8fb4a OZ |
1303 | |
1304 | if (he->domain != last_area) | |
1305 | { | |
1306 | cli_msg(-1016, ""); | |
1307 | cli_msg(-1016, "area %R", he->domain); | |
1308 | last_area = he->domain; | |
1309 | ix = 0; | |
1310 | } | |
1311 | } | |
1312 | else | |
1313 | continue; | |
a3b70dc4 OZ |
1314 | } |
1315 | ||
70945cb6 | 1316 | ASSERT(cnode && (he->domain == last_area) && (he->lsa.rt == cnode->lsa.rt)); |
0ea8fb4a | 1317 | |
70945cb6 | 1318 | switch (he->lsa_type) |
a3b70dc4 | 1319 | { |
70945cb6 OZ |
1320 | case LSA_T_RT: |
1321 | if (he->lsa.id == cnode->lsa.id) | |
1322 | show_lsa_router(p, he, verbose); | |
1323 | break; | |
a3b70dc4 | 1324 | |
70945cb6 OZ |
1325 | case LSA_T_NET: |
1326 | show_lsa_network(he, ospf2); | |
1327 | break; | |
a3b70dc4 | 1328 | |
70945cb6 OZ |
1329 | case LSA_T_SUM_NET: |
1330 | if (cnode->lsa_type == LSA_T_RT) | |
d3f4f92b | 1331 | show_lsa_sum_net(he, ospf2, af); |
70945cb6 | 1332 | break; |
a3b70dc4 | 1333 | |
70945cb6 OZ |
1334 | case LSA_T_SUM_RT: |
1335 | if (cnode->lsa_type == LSA_T_RT) | |
1336 | show_lsa_sum_rt(he, ospf2); | |
1337 | break; | |
b66abe8e | 1338 | |
70945cb6 OZ |
1339 | case LSA_T_EXT: |
1340 | case LSA_T_NSSA: | |
d3f4f92b | 1341 | show_lsa_external(he, ospf2, af); |
70945cb6 | 1342 | break; |
0ea8fb4a | 1343 | |
70945cb6 | 1344 | case LSA_T_PREFIX: |
d3f4f92b | 1345 | show_lsa_prefix(he, cnode, af); |
70945cb6 | 1346 | break; |
a3b70dc4 | 1347 | } |
b66abe8e | 1348 | |
0ea8fb4a OZ |
1349 | /* In these cases, we close the current node */ |
1350 | if ((i+1 == j1) | |
1351 | || (hea[i+1]->domain != last_area) | |
70945cb6 OZ |
1352 | || (hea[i+1]->lsa.rt != cnode->lsa.rt) |
1353 | || (hea[i+1]->lsa_type == LSA_T_NET)) | |
0ea8fb4a | 1354 | { |
70945cb6 | 1355 | while ((ix < jx) && (hex[ix]->lsa.rt < cnode->lsa.rt)) |
0ea8fb4a OZ |
1356 | ix++; |
1357 | ||
70945cb6 | 1358 | while ((ix < jx) && (hex[ix]->lsa.rt == cnode->lsa.rt)) |
d3f4f92b | 1359 | show_lsa_external(hex[ix++], ospf2, af); |
0ea8fb4a OZ |
1360 | |
1361 | cnode = NULL; | |
1362 | } | |
1363 | } | |
1364 | ||
1365 | int hdr = 0; | |
1366 | u32 last_rt = 0xFFFFFFFF; | |
1367 | for (ix = 0; ix < jx; ix++) | |
1368 | { | |
1369 | he = hex[ix]; | |
1370 | ||
1371 | /* If it is still marked, we show it now. */ | |
1372 | if (he->domain) | |
1373 | { | |
1374 | he->domain = 0; | |
1375 | ||
1376 | if ((he->color != INSPF) && reachable) | |
1377 | continue; | |
1378 | ||
1379 | if (!hdr) | |
1380 | { | |
1381 | cli_msg(-1016, ""); | |
1382 | cli_msg(-1016, "other ASBRs"); | |
1383 | hdr = 1; | |
1384 | } | |
1385 | ||
1386 | if (he->lsa.rt != last_rt) | |
1387 | { | |
1388 | cli_msg(-1016, ""); | |
1389 | cli_msg(-1016, "\trouter %R", he->lsa.rt); | |
1390 | last_rt = he->lsa.rt; | |
1391 | } | |
1392 | ||
d3f4f92b | 1393 | show_lsa_external(he, ospf2, af); |
0ea8fb4a | 1394 | } |
a3b70dc4 | 1395 | } |
0ea8fb4a | 1396 | |
a3b70dc4 OZ |
1397 | cli_msg(0, ""); |
1398 | } | |
1399 | ||
98899244 OZ |
1400 | |
1401 | static int | |
1402 | lsa_compare_for_lsadb(const void *p1, const void *p2) | |
1403 | { | |
1404 | struct top_hash_entry * he1 = * (struct top_hash_entry **) p1; | |
1405 | struct top_hash_entry * he2 = * (struct top_hash_entry **) p2; | |
1406 | struct ospf_lsa_header *lsa1 = &(he1->lsa); | |
1407 | struct ospf_lsa_header *lsa2 = &(he2->lsa); | |
70945cb6 OZ |
1408 | int sc1 = LSA_SCOPE(he1->lsa_type); |
1409 | int sc2 = LSA_SCOPE(he2->lsa_type); | |
98899244 OZ |
1410 | |
1411 | if (sc1 != sc2) | |
1412 | return sc2 - sc1; | |
1413 | ||
1414 | if (he1->domain != he2->domain) | |
1415 | return he1->domain - he2->domain; | |
1416 | ||
1417 | if (lsa1->rt != lsa2->rt) | |
1418 | return lsa1->rt - lsa2->rt; | |
742029eb | 1419 | |
98899244 OZ |
1420 | if (lsa1->id != lsa2->id) |
1421 | return lsa1->id - lsa2->id; | |
1422 | ||
70945cb6 OZ |
1423 | if (he1->lsa_type != he2->lsa_type) |
1424 | return he1->lsa_type - he2->lsa_type; | |
98899244 OZ |
1425 | |
1426 | return lsa1->sn - lsa2->sn; | |
1427 | } | |
1428 | ||
1429 | void | |
20ab192b | 1430 | ospf_sh_lsadb(struct lsadb_show_data *ld) |
98899244 | 1431 | { |
c0e958e0 | 1432 | struct ospf_proto *p = ld->proto; |
a7a7372a OZ |
1433 | uint num = p->gr->hash_entries; |
1434 | uint i, j; | |
98899244 OZ |
1435 | int last_dscope = -1; |
1436 | u32 last_domain = 0; | |
70945cb6 | 1437 | u16 type_mask = ospf_is_v2(p) ? 0x00ff : 0xffff; /* see lsa_etype() */ |
98899244 | 1438 | |
70945cb6 | 1439 | if (p->p.proto_state != PS_UP) |
98899244 | 1440 | { |
70945cb6 | 1441 | cli_msg(-1017, "%s: is not up", p->p.name); |
98899244 OZ |
1442 | cli_msg(0, ""); |
1443 | return; | |
1444 | } | |
1445 | ||
20ab192b | 1446 | if (ld->router == SH_ROUTER_SELF) |
70945cb6 | 1447 | ld->router = p->router_id; |
20ab192b | 1448 | |
0ea8fb4a | 1449 | struct top_hash_entry *hea[num]; |
98899244 OZ |
1450 | struct top_hash_entry *he; |
1451 | ||
1452 | j = 0; | |
70945cb6 OZ |
1453 | WALK_SLIST(he, p->lsal) |
1454 | if (he->lsa_body) | |
1455 | hea[j++] = he; | |
98899244 | 1456 | |
70945cb6 | 1457 | ASSERT(j <= num); |
98899244 OZ |
1458 | |
1459 | qsort(hea, j, sizeof(struct top_hash_entry *), lsa_compare_for_lsadb); | |
1460 | ||
1461 | for (i = 0; i < j; i++) | |
1462 | { | |
1463 | struct ospf_lsa_header *lsa = &(hea[i]->lsa); | |
70945cb6 OZ |
1464 | u16 lsa_type = lsa->type_raw & type_mask; |
1465 | u16 dscope = LSA_SCOPE(hea[i]->lsa_type); | |
742029eb | 1466 | |
70945cb6 | 1467 | /* Hack: 1 is used for LSA_SCOPE_LINK, fixed by & 0xf000 */ |
20ab192b OZ |
1468 | if (ld->scope && (dscope != (ld->scope & 0xf000))) |
1469 | continue; | |
1470 | ||
1471 | if ((ld->scope == LSA_SCOPE_AREA) && (hea[i]->domain != ld->area)) | |
1472 | continue; | |
1473 | ||
70945cb6 OZ |
1474 | /* For user convenience ignore high nibble */ |
1475 | if (ld->type && ((lsa_type & 0x0fff) != (ld->type & 0x0fff))) | |
20ab192b OZ |
1476 | continue; |
1477 | ||
1478 | if (ld->lsid && (lsa->id != ld->lsid)) | |
1479 | continue; | |
1480 | ||
1481 | if (ld->router && (lsa->rt != ld->router)) | |
1482 | continue; | |
742029eb | 1483 | |
98899244 OZ |
1484 | if ((dscope != last_dscope) || (hea[i]->domain != last_domain)) |
1485 | { | |
98899244 OZ |
1486 | cli_msg(-1017, ""); |
1487 | switch (dscope) | |
1488 | { | |
70945cb6 OZ |
1489 | case LSA_SCOPE_AS: |
1490 | cli_msg(-1017, "Global"); | |
1491 | break; | |
1492 | ||
1493 | case LSA_SCOPE_AREA: | |
1494 | cli_msg(-1017, "Area %R", hea[i]->domain); | |
1495 | break; | |
1496 | ||
1497 | case LSA_SCOPE_LINK: | |
1498 | { | |
1499 | struct iface *ifa = if_find_by_index(hea[i]->domain); | |
1500 | cli_msg(-1017, "Link %s", (ifa != NULL) ? ifa->name : "?"); | |
1501 | } | |
1502 | break; | |
98899244 OZ |
1503 | } |
1504 | cli_msg(-1017, ""); | |
6f8bbaa1 | 1505 | cli_msg(-1017," Type LS ID Router Sequence Age Checksum"); |
98899244 OZ |
1506 | |
1507 | last_dscope = dscope; | |
1508 | last_domain = hea[i]->domain; | |
1509 | } | |
1510 | ||
6f8bbaa1 OZ |
1511 | cli_msg(-1017," %04x %-15R %-15R %08x %5u %04x", |
1512 | lsa_type, lsa->id, lsa->rt, lsa->sn, lsa->age, lsa->checksum); | |
98899244 OZ |
1513 | } |
1514 | cli_msg(0, ""); | |
1515 | } | |
1516 | ||
1517 | ||
80787d41 | 1518 | struct protocol proto_ospf = { |
70945cb6 OZ |
1519 | .name = "OSPF", |
1520 | .template = "ospf%d", | |
ee7e2ffd | 1521 | .class = PROTOCOL_OSPF, |
70945cb6 | 1522 | .preference = DEF_PREF_OSPF, |
f4a60a9b OZ |
1523 | .channel_mask = NB_IP, |
1524 | .proto_size = sizeof(struct ospf_proto), | |
2bbc3083 | 1525 | .config_size = sizeof(struct ospf_config), |
70945cb6 OZ |
1526 | .init = ospf_init, |
1527 | .dump = ospf_dump, | |
1528 | .start = ospf_start, | |
1529 | .shutdown = ospf_shutdown, | |
1530 | .reconfigure = ospf_reconfigure, | |
1531 | .get_status = ospf_get_status, | |
1532 | .get_attr = ospf_get_attr, | |
1533 | .get_route_info = ospf_get_route_info | |
80787d41 | 1534 | }; |