]> git.ipfire.org Git - thirdparty/bird.git/blame - nest/rt-show.c
Lib: Use alloc_size() function attribute
[thirdparty/bird.git] / nest / rt-show.c
CommitLineData
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
21static void
22rt_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
33static inline struct krt_proto *
34rt_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 40static void
498d8145 41rt_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
102static void
103rt_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
223static void
224rt_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
241static void
242rt_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
355done:
356 rt_show_cleanup(c);
357 c->cont = c->cleanup = NULL;
358}
359
360struct rt_show_data_rtable *
361rt_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
369static inline void
370rt_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
408static inline void
409rt_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
455void
456rt_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}