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