]> git.ipfire.org Git - thirdparty/bird.git/blob - nest/rt-show.c
Formalized our contribution policy which we're currently applying
[thirdparty/bird.git] / nest / rt-show.c
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"
18 #include "filter/data.h"
19 #include "sysdep/unix/krt.h"
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
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
40 static void
41 rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary)
42 {
43 byte from[IPA_MAX_TEXT_LENGTH+8];
44 byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
45 rta *a = e->attrs;
46 int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0;
47 void (*get_route_info)(struct rte *, byte *buf);
48 struct nexthop *nh;
49
50 tm_format_time(tm, &config->tf_route, e->lastmod);
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
56 /* Need to normalize the extended attributes */
57 if (d->verbose && !rta_is_cached(a) && a->eattrs)
58 ea_normalize(a->eattrs);
59
60 get_route_info = e->src->proto->proto->get_route_info;
61 if (get_route_info)
62 get_route_info(e, info);
63 else
64 bsprintf(info, " (%d)", a->pref);
65
66 if (d->last_table != d->tab)
67 rt_show_table(c, d);
68
69 cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
70 e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
71
72 if (a->dest == RTD_UNICAST)
73 for (nh = &(a->nh); nh; nh = nh->next)
74 {
75 char mpls[MPLS_MAX_LABEL_STRING], *lsp = mpls;
76 char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : "";
77 char weight[16] = "";
78
79 if (nh->labels)
80 {
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)
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);
93 else
94 cli_printf(c, -1007, "\tdev %s%s%s",
95 nh->iface->name, mpls, onlink, weight);
96 }
97
98 if (d->verbose)
99 rta_show(c, a);
100 }
101
102 static void
103 rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
104 {
105 rte *e, *ee;
106 byte ia[NET_MAX_TEXT_LENGTH+16+1];
107 struct channel *ec = d->tab->export_channel;
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() */
111 ASSUME(!d->export_mode || ec);
112
113 int first = 1;
114 int first_show = 1;
115 int last_label = 0;
116 int pass = 0;
117
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;
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
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))
145 {
146 /* Special case for merged export */
147 rte *rt_free;
148 e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1);
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;
157 int ic = ep->preexport ? ep->preexport(ec, e) : 0;
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) ||
173 (f_run(ec->out_filter, &e, c->show_pool, FF_SILENT) <= F_ACCEPT);
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
183 if (d->show_protocol && (d->show_protocol != e->src->proto))
184 goto skip;
185
186 if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
187 goto skip;
188
189 if (d->stats < 2)
190 {
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 }
200 else
201 ia[0] = 0;
202
203 rt_show_rte(c, ia, e, d, (e->net->routes == ee));
204 first_show = 0;
205 last_label = label;
206 }
207
208 d->show_counter++;
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 */
230 if (d->table_open && !d->trie_walk)
231 fit_get(&d->tab->table->fib, &d->fit);
232
233 if (d->walk_lock)
234 rt_unlock_trie(d->tab->table, d->walk_lock);
235
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;
245 struct rtable *tab = d->tab->table;
246 #ifdef DEBUGGING
247 unsigned max = 4;
248 #else
249 unsigned max = 64;
250 #endif
251 struct fib *fib = &tab->fib;
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 {
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)
271 {
272 d->walk_lock = rt_lock_trie(tab);
273 trie_walk_init(d->walk_state, tab->trie, d->addr);
274 }
275 else
276 FIB_ITERATE_INIT(&d->fit, &tab->fib);
277
278 d->table_open = 1;
279 d->table_counter++;
280 d->kernel = rt_show_get_kernel(d);
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
290 if (d->trie_walk)
291 {
292 /* Trie-based walk */
293 net_addr addr;
294 while (trie_walk_next(d->walk_state, &addr))
295 {
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;
304 }
305
306 rt_unlock_trie(tab, d->walk_lock);
307 d->walk_lock = NULL;
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;
316
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;
327 }
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,
336 d->net_counter - d->net_counter_last, tab->name);
337 }
338
339 d->kernel = NULL;
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++)
404 if (config->def_tables[i] && config->def_tables[i]->table)
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
467 if (!d->addr || (d->addr_mode == RSD_ADDR_IN))
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;
483 d->kernel = rt_show_get_kernel(d);
484
485 if (d->addr_mode == RSD_ADDR_FOR)
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 }