]>
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" | |
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 | |
498d8145 | 32 | rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary) |
f8d44b01 JMM |
33 | { |
34 | byte from[IPA_MAX_TEXT_LENGTH+8]; | |
35 | byte tm[TM_DATETIME_BUFFER_SIZE], info[256]; | |
36 | rta *a = e->attrs; | |
f8d44b01 | 37 | int sync_error = (e->net->n.flags & KRF_SYNC_ERROR); |
13c0be19 | 38 | void (*get_route_info)(struct rte *, byte *buf); |
f8d44b01 JMM |
39 | struct nexthop *nh; |
40 | ||
f047271c | 41 | tm_format_time(tm, &config->tf_route, e->lastmod); |
f8d44b01 JMM |
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 | ||
13c0be19 | 47 | /* Need to normalize the extended attributes */ |
875cc073 | 48 | if (d->verbose && !rta_is_cached(a) && a->eattrs) |
13c0be19 | 49 | ea_normalize(a->eattrs); |
875cc073 OZ |
50 | |
51 | get_route_info = a->src->proto->proto->get_route_info; | |
f8d44b01 | 52 | if (get_route_info) |
13c0be19 | 53 | get_route_info(e, info); |
f8d44b01 JMM |
54 | else |
55 | bsprintf(info, " (%d)", e->pref); | |
56 | ||
57 | if (d->last_table != d->tab) | |
58 | rt_show_table(c, d); | |
59 | ||
eb95b5ec | 60 | cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest), |
f8d44b01 JMM |
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; | |
a1f5e514 | 67 | char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : ""; |
eb95b5ec | 68 | char weight[16] = ""; |
f8d44b01 JMM |
69 | |
70 | if (nh->labels) | |
a82f692e | 71 | { |
f8d44b01 JMM |
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) | |
eb95b5ec OZ |
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); | |
f8d44b01 | 84 | else |
eb95b5ec OZ |
85 | cli_printf(c, -1007, "\tdev %s%s%s", |
86 | nh->iface->name, mpls, onlink, weight); | |
f8d44b01 JMM |
87 | } |
88 | ||
89 | if (d->verbose) | |
13c0be19 | 90 | rta_show(c, a); |
f8d44b01 JMM |
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]; | |
f8d44b01 JMM |
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; | |
875cc073 | 117 | rte_make_tmp_attrs(&e, c->show_pool, NULL); |
f8d44b01 JMM |
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)) | |
a82f692e | 125 | { |
f8d44b01 | 126 | rte *rt_free; |
13c0be19 | 127 | e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1); |
f8d44b01 JMM |
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; | |
14375237 | 136 | int ic = ep->preexport ? ep->preexport(ep, &e, c->show_pool) : 0; |
f8d44b01 JMM |
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) || | |
13c0be19 | 152 | (f_run(ec->out_filter, &e, c->show_pool, FF_SILENT) <= F_ACCEPT); |
f8d44b01 JMM |
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 | ||
13c0be19 | 165 | if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT) |
f8d44b01 JMM |
166 | goto skip; |
167 | ||
168 | if (d->stats < 2) | |
498d8145 | 169 | rt_show_rte(c, ia, e, d, (e->net->routes == ee)); |
f8d44b01 JMM |
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 | } |