]>
Commit | Line | Data |
---|---|---|
f8d44b01 JMM |
1 | /* |
2 | * BIRD -- Route Display Routines | |
3 | * | |
4 | * (c) 1998--2000 Martin Mares <mj@ucw.cz> | |
5 | * (c) 2017 Jan Moskyto Matejka <mq@jmq.cz> | |
6 | * | |
7 | * Can be freely distributed and used under the terms of the GNU GPL. | |
8 | */ | |
9 | ||
10 | #undef LOCAL_DEBUG | |
11 | ||
12 | #include "nest/bird.h" | |
13 | #include "nest/route.h" | |
14 | #include "nest/protocol.h" | |
15 | #include "nest/cli.h" | |
16 | #include "nest/iface.h" | |
17 | #include "filter/filter.h" | |
9ac16df3 | 18 | #include "filter/data.h" |
cc75b3e1 | 19 | #include "sysdep/unix/krt.h" |
f8d44b01 JMM |
20 | |
21 | static void | |
22 | rt_show_table(struct cli *c, struct rt_show_data *d) | |
23 | { | |
24 | /* No table blocks in 'show route count' */ | |
25 | if (d->stats == 2) | |
26 | return; | |
27 | ||
28 | if (d->last_table) cli_printf(c, -1007, ""); | |
29 | cli_printf(c, -1007, "Table %s:", d->tab->table->name); | |
30 | d->last_table = d->tab; | |
31 | } | |
32 | ||
cc75b3e1 OZ |
33 | static inline struct krt_proto * |
34 | rt_show_get_kernel(struct rt_show_data *d) | |
35 | { | |
36 | struct proto_config *krt = d->tab->table->config->krt_attached; | |
37 | return krt ? (struct krt_proto *) krt->proto : NULL; | |
38 | } | |
39 | ||
f8d44b01 | 40 | static void |
498d8145 | 41 | rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary) |
f8d44b01 JMM |
42 | { |
43 | byte from[IPA_MAX_TEXT_LENGTH+8]; | |
44 | byte tm[TM_DATETIME_BUFFER_SIZE], info[256]; | |
45 | rta *a = e->attrs; | |
cc75b3e1 | 46 | int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0; |
13c0be19 | 47 | void (*get_route_info)(struct rte *, byte *buf); |
f8d44b01 JMM |
48 | struct nexthop *nh; |
49 | ||
f047271c | 50 | tm_format_time(tm, &config->tf_route, e->lastmod); |
f8d44b01 JMM |
51 | if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw)) |
52 | bsprintf(from, " from %I", a->from); | |
53 | else | |
54 | from[0] = 0; | |
55 | ||
13c0be19 | 56 | /* Need to normalize the extended attributes */ |
875cc073 | 57 | if (d->verbose && !rta_is_cached(a) && a->eattrs) |
13c0be19 | 58 | ea_normalize(a->eattrs); |
875cc073 | 59 | |
5cff1d5f | 60 | get_route_info = e->src->proto->proto->get_route_info; |
f8d44b01 | 61 | if (get_route_info) |
13c0be19 | 62 | get_route_info(e, info); |
f8d44b01 | 63 | else |
eb937358 | 64 | bsprintf(info, " (%d)", a->pref); |
f8d44b01 JMM |
65 | |
66 | if (d->last_table != d->tab) | |
67 | rt_show_table(c, d); | |
68 | ||
eb95b5ec | 69 | cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest), |
5cff1d5f | 70 | e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); |
f8d44b01 JMM |
71 | |
72 | if (a->dest == RTD_UNICAST) | |
73 | for (nh = &(a->nh); nh; nh = nh->next) | |
74 | { | |
114be2af | 75 | char mpls[MPLS_MAX_LABEL_STRING], *lsp = mpls; |
a1f5e514 | 76 | char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : ""; |
eb95b5ec | 77 | char weight[16] = ""; |
f8d44b01 JMM |
78 | |
79 | if (nh->labels) | |
a82f692e | 80 | { |
f8d44b01 JMM |
81 | lsp += bsprintf(lsp, " mpls %d", nh->label[0]); |
82 | for (int i=1;i<nh->labels; i++) | |
83 | lsp += bsprintf(lsp, "/%d", nh->label[i]); | |
84 | } | |
85 | *lsp = '\0'; | |
86 | ||
87 | if (a->nh.next) | |
eb95b5ec OZ |
88 | bsprintf(weight, " weight %d", nh->weight + 1); |
89 | ||
90 | if (ipa_nonzero(nh->gw)) | |
91 | cli_printf(c, -1007, "\tvia %I on %s%s%s%s", | |
92 | nh->gw, nh->iface->name, mpls, onlink, weight); | |
f8d44b01 | 93 | else |
eb95b5ec OZ |
94 | cli_printf(c, -1007, "\tdev %s%s%s", |
95 | nh->iface->name, mpls, onlink, weight); | |
f8d44b01 JMM |
96 | } |
97 | ||
98 | if (d->verbose) | |
13c0be19 | 99 | rta_show(c, a); |
f8d44b01 JMM |
100 | } |
101 | ||
102 | static void | |
103 | rt_show_net(struct cli *c, net *n, struct rt_show_data *d) | |
104 | { | |
105 | rte *e, *ee; | |
333ddd4f | 106 | byte ia[NET_MAX_TEXT_LENGTH+16+1]; |
f8d44b01 | 107 | struct channel *ec = d->tab->export_channel; |
bbe49ae5 MM |
108 | |
109 | /* The Clang static analyzer complains that ec may be NULL. | |
110 | * It should be ensured to be not NULL by rt_show_prepare_tables() */ | |
b12442c9 | 111 | ASSUME(!d->export_mode || ec); |
bbe49ae5 | 112 | |
f8d44b01 | 113 | int first = 1; |
61375bd0 | 114 | int first_show = 1; |
333ddd4f | 115 | int last_label = 0; |
f8d44b01 JMM |
116 | int pass = 0; |
117 | ||
f8d44b01 JMM |
118 | for (e = n->routes; e; e = e->next) |
119 | { | |
120 | if (rte_is_filtered(e) != d->filtered) | |
121 | continue; | |
122 | ||
123 | d->rt_counter++; | |
124 | d->net_counter += first; | |
125 | first = 0; | |
126 | ||
127 | if (pass) | |
128 | continue; | |
129 | ||
130 | ee = e; | |
f8d44b01 JMM |
131 | |
132 | /* Export channel is down, do not try to export routes to it */ | |
133 | if (ec && (ec->export_state == ES_DOWN)) | |
134 | goto skip; | |
135 | ||
5ea39eaa OZ |
136 | if (d->export_mode == RSEM_EXPORTED) |
137 | { | |
138 | if (!bmap_test(&ec->export_map, ee->id)) | |
139 | goto skip; | |
140 | ||
141 | // if (ec->ra_mode != RA_ANY) | |
142 | // pass = 1; | |
143 | } | |
144 | else if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED)) | |
a82f692e | 145 | { |
5ea39eaa | 146 | /* Special case for merged export */ |
f8d44b01 | 147 | rte *rt_free; |
13c0be19 | 148 | e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1); |
f8d44b01 JMM |
149 | pass = 1; |
150 | ||
151 | if (!e) | |
152 | { e = ee; goto skip; } | |
153 | } | |
154 | else if (d->export_mode) | |
155 | { | |
156 | struct proto *ep = ec->proto; | |
d429bc5c | 157 | int ic = ep->preexport ? ep->preexport(ec, e) : 0; |
f8d44b01 JMM |
158 | |
159 | if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED) | |
160 | pass = 1; | |
161 | ||
162 | if (ic < 0) | |
163 | goto skip; | |
164 | ||
165 | if (d->export_mode > RSEM_PREEXPORT) | |
166 | { | |
167 | /* | |
168 | * FIXME - This shows what should be exported according to current | |
169 | * filters, but not what was really exported. 'configure soft' | |
170 | * command may change the export filter and do not update routes. | |
171 | */ | |
172 | int do_export = (ic > 0) || | |
13c0be19 | 173 | (f_run(ec->out_filter, &e, c->show_pool, FF_SILENT) <= F_ACCEPT); |
f8d44b01 JMM |
174 | |
175 | if (do_export != (d->export_mode == RSEM_EXPORT)) | |
176 | goto skip; | |
177 | ||
178 | if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_ACCEPTED)) | |
179 | pass = 1; | |
180 | } | |
181 | } | |
182 | ||
5cff1d5f | 183 | if (d->show_protocol && (d->show_protocol != e->src->proto)) |
f8d44b01 JMM |
184 | goto skip; |
185 | ||
13c0be19 | 186 | if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT) |
f8d44b01 JMM |
187 | goto skip; |
188 | ||
189 | if (d->stats < 2) | |
61375bd0 | 190 | { |
333ddd4f OZ |
191 | int label = (int) ea_get_int(e->attrs->eattrs, EA_MPLS_LABEL, (uint) -1); |
192 | ||
193 | if (first_show || (last_label != label)) | |
194 | { | |
195 | if (label < 0) | |
196 | net_format(n->n.addr, ia, sizeof(ia)); | |
197 | else | |
198 | bsnprintf(ia, sizeof(ia), "%N mpls %d", n->n.addr, label); | |
199 | } | |
61375bd0 OZ |
200 | else |
201 | ia[0] = 0; | |
202 | ||
498d8145 | 203 | rt_show_rte(c, ia, e, d, (e->net->routes == ee)); |
61375bd0 | 204 | first_show = 0; |
333ddd4f | 205 | last_label = label; |
61375bd0 | 206 | } |
f8d44b01 JMM |
207 | |
208 | d->show_counter++; | |
f8d44b01 JMM |
209 | |
210 | skip: | |
211 | if (e != ee) | |
212 | { | |
213 | rte_free(e); | |
214 | e = ee; | |
215 | } | |
216 | lp_flush(c->show_pool); | |
217 | ||
218 | if (d->primary_only) | |
219 | break; | |
220 | } | |
221 | } | |
222 | ||
223 | static void | |
224 | rt_show_cleanup(struct cli *c) | |
225 | { | |
226 | struct rt_show_data *d = c->rover; | |
227 | struct rt_show_data_rtable *tab; | |
228 | ||
229 | /* Unlink the iterator */ | |
9ac16df3 | 230 | if (d->table_open && !d->trie_walk) |
f8d44b01 JMM |
231 | fit_get(&d->tab->table->fib, &d->fit); |
232 | ||
5a89edc6 OZ |
233 | if (d->walk_lock) |
234 | rt_unlock_trie(d->tab->table, d->walk_lock); | |
235 | ||
f8d44b01 JMM |
236 | /* Unlock referenced tables */ |
237 | WALK_LIST(tab, d->tables) | |
238 | rt_unlock_table(tab->table); | |
239 | } | |
240 | ||
241 | static void | |
242 | rt_show_cont(struct cli *c) | |
243 | { | |
244 | struct rt_show_data *d = c->rover; | |
9ac16df3 | 245 | struct rtable *tab = d->tab->table; |
f8d44b01 JMM |
246 | #ifdef DEBUGGING |
247 | unsigned max = 4; | |
248 | #else | |
249 | unsigned max = 64; | |
250 | #endif | |
9ac16df3 | 251 | struct fib *fib = &tab->fib; |
f8d44b01 JMM |
252 | struct fib_iterator *it = &d->fit; |
253 | ||
254 | if (d->running_on_config && (d->running_on_config != config)) | |
255 | { | |
256 | cli_printf(c, 8004, "Stopped due to reconfiguration"); | |
257 | goto done; | |
258 | } | |
259 | ||
260 | if (!d->table_open) | |
261 | { | |
9ac16df3 OZ |
262 | /* We use either trie-based walk or fib-based walk */ |
263 | d->trie_walk = tab->trie && | |
264 | (d->addr_mode == RSD_ADDR_IN) && | |
265 | net_val_match(tab->addr_type, NB_IP); | |
266 | ||
267 | if (d->trie_walk && !d->walk_state) | |
268 | d->walk_state = lp_allocz(c->parser_pool, sizeof (struct f_trie_walk_state)); | |
269 | ||
270 | if (d->trie_walk) | |
5a89edc6 OZ |
271 | { |
272 | d->walk_lock = rt_lock_trie(tab); | |
9ac16df3 | 273 | trie_walk_init(d->walk_state, tab->trie, d->addr); |
5a89edc6 | 274 | } |
9ac16df3 OZ |
275 | else |
276 | FIB_ITERATE_INIT(&d->fit, &tab->fib); | |
277 | ||
f8d44b01 JMM |
278 | d->table_open = 1; |
279 | d->table_counter++; | |
cc75b3e1 | 280 | d->kernel = rt_show_get_kernel(d); |
f8d44b01 JMM |
281 | |
282 | d->show_counter_last = d->show_counter; | |
283 | d->rt_counter_last = d->rt_counter; | |
284 | d->net_counter_last = d->net_counter; | |
285 | ||
286 | if (d->tables_defined_by & RSD_TDB_SET) | |
287 | rt_show_table(c, d); | |
288 | } | |
289 | ||
9ac16df3 | 290 | if (d->trie_walk) |
f8d44b01 | 291 | { |
9ac16df3 OZ |
292 | /* Trie-based walk */ |
293 | net_addr addr; | |
294 | while (trie_walk_next(d->walk_state, &addr)) | |
f8d44b01 | 295 | { |
9ac16df3 OZ |
296 | net *n = net_find(tab, &addr); |
297 | if (!n) | |
298 | continue; | |
299 | ||
300 | rt_show_net(c, n, d); | |
301 | ||
302 | if (!--max) | |
303 | return; | |
f8d44b01 | 304 | } |
5a89edc6 OZ |
305 | |
306 | rt_unlock_trie(tab, d->walk_lock); | |
307 | d->walk_lock = NULL; | |
9ac16df3 OZ |
308 | } |
309 | else | |
310 | { | |
311 | /* fib-based walk */ | |
312 | FIB_ITERATE_START(fib, it, net, n) | |
313 | { | |
314 | if ((d->addr_mode == RSD_ADDR_IN) && (!net_in_netX(n->n.addr, d->addr))) | |
315 | goto next; | |
ea97b890 | 316 | |
9ac16df3 OZ |
317 | if (!max--) |
318 | { | |
319 | FIB_ITERATE_PUT(it); | |
320 | return; | |
321 | } | |
322 | rt_show_net(c, n, d); | |
323 | ||
324 | next:; | |
325 | } | |
326 | FIB_ITERATE_END; | |
f8d44b01 | 327 | } |
f8d44b01 JMM |
328 | |
329 | if (d->stats) | |
330 | { | |
331 | if (d->last_table != d->tab) | |
332 | rt_show_table(c, d); | |
333 | ||
334 | cli_printf(c, -1007, "%d of %d routes for %d networks in table %s", | |
335 | d->show_counter - d->show_counter_last, d->rt_counter - d->rt_counter_last, | |
9ac16df3 | 336 | d->net_counter - d->net_counter_last, tab->name); |
f8d44b01 JMM |
337 | } |
338 | ||
cc75b3e1 | 339 | d->kernel = NULL; |
f8d44b01 JMM |
340 | d->table_open = 0; |
341 | d->tab = NODE_NEXT(d->tab); | |
342 | ||
343 | if (NODE_VALID(d->tab)) | |
344 | return; | |
345 | ||
346 | if (d->stats && (d->table_counter > 1)) | |
347 | { | |
348 | if (d->last_table) cli_printf(c, -1007, ""); | |
349 | cli_printf(c, 14, "Total: %d of %d routes for %d networks in %d tables", | |
350 | d->show_counter, d->rt_counter, d->net_counter, d->table_counter); | |
351 | } | |
352 | else | |
353 | cli_printf(c, 0, ""); | |
354 | ||
355 | done: | |
356 | rt_show_cleanup(c); | |
357 | c->cont = c->cleanup = NULL; | |
358 | } | |
359 | ||
360 | struct rt_show_data_rtable * | |
361 | rt_show_add_table(struct rt_show_data *d, rtable *t) | |
362 | { | |
363 | struct rt_show_data_rtable *tab = cfg_allocz(sizeof(struct rt_show_data_rtable)); | |
364 | tab->table = t; | |
365 | add_tail(&(d->tables), &(tab->n)); | |
366 | return tab; | |
367 | } | |
368 | ||
369 | static inline void | |
370 | rt_show_get_default_tables(struct rt_show_data *d) | |
371 | { | |
372 | struct channel *c; | |
373 | struct rt_show_data_rtable *tab; | |
374 | ||
375 | if (d->export_channel) | |
376 | { | |
377 | c = d->export_channel; | |
378 | tab = rt_show_add_table(d, c->table); | |
379 | tab->export_channel = c; | |
380 | return; | |
381 | } | |
382 | ||
383 | if (d->export_protocol) | |
384 | { | |
385 | WALK_LIST(c, d->export_protocol->channels) | |
386 | { | |
387 | if (c->export_state == ES_DOWN) | |
388 | continue; | |
389 | ||
390 | tab = rt_show_add_table(d, c->table); | |
391 | tab->export_channel = c; | |
392 | } | |
393 | return; | |
394 | } | |
395 | ||
396 | if (d->show_protocol) | |
397 | { | |
398 | WALK_LIST(c, d->show_protocol->channels) | |
399 | rt_show_add_table(d, c->table); | |
400 | return; | |
401 | } | |
402 | ||
403 | for (int i=1; i<NET_MAX; i++) | |
8ac20511 | 404 | if (config->def_tables[i] && config->def_tables[i]->table) |
f8d44b01 JMM |
405 | rt_show_add_table(d, config->def_tables[i]->table); |
406 | } | |
407 | ||
408 | static inline void | |
409 | rt_show_prepare_tables(struct rt_show_data *d) | |
410 | { | |
411 | struct rt_show_data_rtable *tab, *tabx; | |
412 | ||
413 | /* Add implicit tables if no table is specified */ | |
414 | if (EMPTY_LIST(d->tables)) | |
415 | rt_show_get_default_tables(d); | |
416 | ||
417 | WALK_LIST_DELSAFE(tab, tabx, d->tables) | |
418 | { | |
419 | /* Ensure there is defined export_channel for each table */ | |
420 | if (d->export_mode) | |
421 | { | |
422 | if (!tab->export_channel && d->export_channel && | |
423 | (tab->table == d->export_channel->table)) | |
424 | tab->export_channel = d->export_channel; | |
425 | ||
426 | if (!tab->export_channel && d->export_protocol) | |
427 | tab->export_channel = proto_find_channel_by_table(d->export_protocol, tab->table); | |
428 | ||
429 | if (!tab->export_channel) | |
430 | { | |
431 | if (d->tables_defined_by & RSD_TDB_NMN) | |
432 | cf_error("No export channel for table %s", tab->table->name); | |
433 | ||
434 | rem_node(&(tab->n)); | |
435 | continue; | |
436 | } | |
437 | } | |
438 | ||
439 | /* Ensure specified network is compatible with each table */ | |
440 | if (d->addr && (tab->table->addr_type != d->addr->type)) | |
441 | { | |
442 | if (d->tables_defined_by & RSD_TDB_NMN) | |
443 | cf_error("Incompatible type of prefix/ip for table %s", tab->table->name); | |
444 | ||
445 | rem_node(&(tab->n)); | |
446 | continue; | |
447 | } | |
448 | } | |
449 | ||
450 | /* Ensure there is at least one table */ | |
451 | if (EMPTY_LIST(d->tables)) | |
452 | cf_error("No valid tables"); | |
453 | } | |
454 | ||
455 | void | |
456 | rt_show(struct rt_show_data *d) | |
457 | { | |
458 | struct rt_show_data_rtable *tab; | |
459 | net *n; | |
460 | ||
461 | /* Filtered routes are neither exported nor have sensible ordering */ | |
462 | if (d->filtered && (d->export_mode || d->primary_only)) | |
463 | cf_error("Incompatible show route options"); | |
464 | ||
465 | rt_show_prepare_tables(d); | |
466 | ||
ea97b890 | 467 | if (!d->addr || (d->addr_mode == RSD_ADDR_IN)) |
f8d44b01 JMM |
468 | { |
469 | WALK_LIST(tab, d->tables) | |
470 | rt_lock_table(tab->table); | |
471 | ||
472 | /* There is at least one table */ | |
473 | d->tab = HEAD(d->tables); | |
474 | this_cli->cont = rt_show_cont; | |
475 | this_cli->cleanup = rt_show_cleanup; | |
476 | this_cli->rover = d; | |
477 | } | |
478 | else | |
479 | { | |
480 | WALK_LIST(tab, d->tables) | |
481 | { | |
482 | d->tab = tab; | |
cc75b3e1 | 483 | d->kernel = rt_show_get_kernel(d); |
f8d44b01 | 484 | |
ea97b890 | 485 | if (d->addr_mode == RSD_ADDR_FOR) |
f8d44b01 JMM |
486 | n = net_route(tab->table, d->addr); |
487 | else | |
488 | n = net_find(tab->table, d->addr); | |
489 | ||
490 | if (n) | |
491 | rt_show_net(this_cli, n, d); | |
492 | } | |
493 | ||
494 | if (d->rt_counter) | |
495 | cli_msg(0, ""); | |
496 | else | |
497 | cli_msg(8001, "Network not found"); | |
498 | } | |
499 | } |