]> git.ipfire.org Git - thirdparty/bird.git/blob - nest/rt-show.c
Nest: Fix primary flag in show route
[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
19 static void
20 rt_show_table(struct cli *c, struct rt_show_data *d)
21 {
22 /* No table blocks in 'show route count' */
23 if (d->stats == 2)
24 return;
25
26 if (d->last_table) cli_printf(c, -1007, "");
27 cli_printf(c, -1007, "Table %s:", d->tab->table->name);
28 d->last_table = d->tab;
29 }
30
31 static void
32 rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary)
33 {
34 byte from[IPA_MAX_TEXT_LENGTH+8];
35 byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
36 rta *a = e->attrs;
37 int sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
38 void (*get_route_info)(struct rte *, byte *buf);
39 struct nexthop *nh;
40
41 tm_format_time(tm, &config->tf_route, e->lastmod);
42 if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
43 bsprintf(from, " from %I", a->from);
44 else
45 from[0] = 0;
46
47 /* Need to normalize the extended attributes */
48 if (d->verbose && !rta_is_cached(a) && a->eattrs)
49 ea_normalize(a->eattrs);
50
51 get_route_info = a->src->proto->proto->get_route_info;
52 if (get_route_info)
53 get_route_info(e, info);
54 else
55 bsprintf(info, " (%d)", e->pref);
56
57 if (d->last_table != d->tab)
58 rt_show_table(c, d);
59
60 cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
61 a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
62
63 if (a->dest == RTD_UNICAST)
64 for (nh = &(a->nh); nh; nh = nh->next)
65 {
66 char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls;
67 char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : "";
68 char weight[16] = "";
69
70 if (nh->labels)
71 {
72 lsp += bsprintf(lsp, " mpls %d", nh->label[0]);
73 for (int i=1;i<nh->labels; i++)
74 lsp += bsprintf(lsp, "/%d", nh->label[i]);
75 }
76 *lsp = '\0';
77
78 if (a->nh.next)
79 bsprintf(weight, " weight %d", nh->weight + 1);
80
81 if (ipa_nonzero(nh->gw))
82 cli_printf(c, -1007, "\tvia %I on %s%s%s%s",
83 nh->gw, nh->iface->name, mpls, onlink, weight);
84 else
85 cli_printf(c, -1007, "\tdev %s%s%s",
86 nh->iface->name, mpls, onlink, weight);
87 }
88
89 if (d->verbose)
90 rta_show(c, a);
91 }
92
93 static void
94 rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
95 {
96 rte *e, *ee;
97 byte ia[NET_MAX_TEXT_LENGTH+1];
98 struct channel *ec = d->tab->export_channel;
99 int first = 1;
100 int pass = 0;
101
102 bsnprintf(ia, sizeof(ia), "%N", n->n.addr);
103
104 for (e = n->routes; e; e = e->next)
105 {
106 if (rte_is_filtered(e) != d->filtered)
107 continue;
108
109 d->rt_counter++;
110 d->net_counter += first;
111 first = 0;
112
113 if (pass)
114 continue;
115
116 ee = e;
117 rte_make_tmp_attrs(&e, c->show_pool, NULL);
118
119 /* Export channel is down, do not try to export routes to it */
120 if (ec && (ec->export_state == ES_DOWN))
121 goto skip;
122
123 /* Special case for merged export */
124 if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
125 {
126 rte *rt_free;
127 e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1);
128 pass = 1;
129
130 if (!e)
131 { e = ee; goto skip; }
132 }
133 else if (d->export_mode)
134 {
135 struct proto *ep = ec->proto;
136 int ic = ep->preexport ? ep->preexport(ep, &e, c->show_pool) : 0;
137
138 if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
139 pass = 1;
140
141 if (ic < 0)
142 goto skip;
143
144 if (d->export_mode > RSEM_PREEXPORT)
145 {
146 /*
147 * FIXME - This shows what should be exported according to current
148 * filters, but not what was really exported. 'configure soft'
149 * command may change the export filter and do not update routes.
150 */
151 int do_export = (ic > 0) ||
152 (f_run(ec->out_filter, &e, c->show_pool, FF_SILENT) <= F_ACCEPT);
153
154 if (do_export != (d->export_mode == RSEM_EXPORT))
155 goto skip;
156
157 if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_ACCEPTED))
158 pass = 1;
159 }
160 }
161
162 if (d->show_protocol && (d->show_protocol != e->attrs->src->proto))
163 goto skip;
164
165 if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
166 goto skip;
167
168 if (d->stats < 2)
169 rt_show_rte(c, ia, e, d, (e->net->routes == ee));
170
171 d->show_counter++;
172 ia[0] = 0;
173
174 skip:
175 if (e != ee)
176 {
177 rte_free(e);
178 e = ee;
179 }
180 lp_flush(c->show_pool);
181
182 if (d->primary_only)
183 break;
184 }
185 }
186
187 static void
188 rt_show_cleanup(struct cli *c)
189 {
190 struct rt_show_data *d = c->rover;
191 struct rt_show_data_rtable *tab;
192
193 /* Unlink the iterator */
194 if (d->table_open)
195 fit_get(&d->tab->table->fib, &d->fit);
196
197 /* Unlock referenced tables */
198 WALK_LIST(tab, d->tables)
199 rt_unlock_table(tab->table);
200 }
201
202 static void
203 rt_show_cont(struct cli *c)
204 {
205 struct rt_show_data *d = c->rover;
206 #ifdef DEBUGGING
207 unsigned max = 4;
208 #else
209 unsigned max = 64;
210 #endif
211 struct fib *fib = &d->tab->table->fib;
212 struct fib_iterator *it = &d->fit;
213
214 if (d->running_on_config && (d->running_on_config != config))
215 {
216 cli_printf(c, 8004, "Stopped due to reconfiguration");
217 goto done;
218 }
219
220 if (!d->table_open)
221 {
222 FIB_ITERATE_INIT(&d->fit, &d->tab->table->fib);
223 d->table_open = 1;
224 d->table_counter++;
225
226 d->show_counter_last = d->show_counter;
227 d->rt_counter_last = d->rt_counter;
228 d->net_counter_last = d->net_counter;
229
230 if (d->tables_defined_by & RSD_TDB_SET)
231 rt_show_table(c, d);
232 }
233
234 FIB_ITERATE_START(fib, it, net, n)
235 {
236 if (!max--)
237 {
238 FIB_ITERATE_PUT(it);
239 return;
240 }
241 rt_show_net(c, n, d);
242 }
243 FIB_ITERATE_END;
244
245 if (d->stats)
246 {
247 if (d->last_table != d->tab)
248 rt_show_table(c, d);
249
250 cli_printf(c, -1007, "%d of %d routes for %d networks in table %s",
251 d->show_counter - d->show_counter_last, d->rt_counter - d->rt_counter_last,
252 d->net_counter - d->net_counter_last, d->tab->table->name);
253 }
254
255 d->table_open = 0;
256 d->tab = NODE_NEXT(d->tab);
257
258 if (NODE_VALID(d->tab))
259 return;
260
261 if (d->stats && (d->table_counter > 1))
262 {
263 if (d->last_table) cli_printf(c, -1007, "");
264 cli_printf(c, 14, "Total: %d of %d routes for %d networks in %d tables",
265 d->show_counter, d->rt_counter, d->net_counter, d->table_counter);
266 }
267 else
268 cli_printf(c, 0, "");
269
270 done:
271 rt_show_cleanup(c);
272 c->cont = c->cleanup = NULL;
273 }
274
275 struct rt_show_data_rtable *
276 rt_show_add_table(struct rt_show_data *d, rtable *t)
277 {
278 struct rt_show_data_rtable *tab = cfg_allocz(sizeof(struct rt_show_data_rtable));
279 tab->table = t;
280 add_tail(&(d->tables), &(tab->n));
281 return tab;
282 }
283
284 static inline void
285 rt_show_get_default_tables(struct rt_show_data *d)
286 {
287 struct channel *c;
288 struct rt_show_data_rtable *tab;
289
290 if (d->export_channel)
291 {
292 c = d->export_channel;
293 tab = rt_show_add_table(d, c->table);
294 tab->export_channel = c;
295 return;
296 }
297
298 if (d->export_protocol)
299 {
300 WALK_LIST(c, d->export_protocol->channels)
301 {
302 if (c->export_state == ES_DOWN)
303 continue;
304
305 tab = rt_show_add_table(d, c->table);
306 tab->export_channel = c;
307 }
308 return;
309 }
310
311 if (d->show_protocol)
312 {
313 WALK_LIST(c, d->show_protocol->channels)
314 rt_show_add_table(d, c->table);
315 return;
316 }
317
318 for (int i=1; i<NET_MAX; i++)
319 if (config->def_tables[i])
320 rt_show_add_table(d, config->def_tables[i]->table);
321 }
322
323 static inline void
324 rt_show_prepare_tables(struct rt_show_data *d)
325 {
326 struct rt_show_data_rtable *tab, *tabx;
327
328 /* Add implicit tables if no table is specified */
329 if (EMPTY_LIST(d->tables))
330 rt_show_get_default_tables(d);
331
332 WALK_LIST_DELSAFE(tab, tabx, d->tables)
333 {
334 /* Ensure there is defined export_channel for each table */
335 if (d->export_mode)
336 {
337 if (!tab->export_channel && d->export_channel &&
338 (tab->table == d->export_channel->table))
339 tab->export_channel = d->export_channel;
340
341 if (!tab->export_channel && d->export_protocol)
342 tab->export_channel = proto_find_channel_by_table(d->export_protocol, tab->table);
343
344 if (!tab->export_channel)
345 {
346 if (d->tables_defined_by & RSD_TDB_NMN)
347 cf_error("No export channel for table %s", tab->table->name);
348
349 rem_node(&(tab->n));
350 continue;
351 }
352 }
353
354 /* Ensure specified network is compatible with each table */
355 if (d->addr && (tab->table->addr_type != d->addr->type))
356 {
357 if (d->tables_defined_by & RSD_TDB_NMN)
358 cf_error("Incompatible type of prefix/ip for table %s", tab->table->name);
359
360 rem_node(&(tab->n));
361 continue;
362 }
363 }
364
365 /* Ensure there is at least one table */
366 if (EMPTY_LIST(d->tables))
367 cf_error("No valid tables");
368 }
369
370 void
371 rt_show(struct rt_show_data *d)
372 {
373 struct rt_show_data_rtable *tab;
374 net *n;
375
376 /* Filtered routes are neither exported nor have sensible ordering */
377 if (d->filtered && (d->export_mode || d->primary_only))
378 cf_error("Incompatible show route options");
379
380 rt_show_prepare_tables(d);
381
382 if (!d->addr)
383 {
384 WALK_LIST(tab, d->tables)
385 rt_lock_table(tab->table);
386
387 /* There is at least one table */
388 d->tab = HEAD(d->tables);
389 this_cli->cont = rt_show_cont;
390 this_cli->cleanup = rt_show_cleanup;
391 this_cli->rover = d;
392 }
393 else
394 {
395 WALK_LIST(tab, d->tables)
396 {
397 d->tab = tab;
398
399 if (d->show_for)
400 n = net_route(tab->table, d->addr);
401 else
402 n = net_find(tab->table, d->addr);
403
404 if (n)
405 rt_show_net(this_cli, n, d);
406 }
407
408 if (d->rt_counter)
409 cli_msg(0, "");
410 else
411 cli_msg(8001, "Network not found");
412 }
413 }