]>
Commit | Line | Data |
---|---|---|
dba3c1d3 PG |
1 | /* BGP FlowSpec VTY |
2 | * Copyright (C) 2018 6WIND | |
3 | * | |
4 | * FRRouting is free software; you can redistribute it and/or modify it | |
5 | * under the terms of the GNU General Public License as published by the | |
6 | * Free Software Foundation; either version 2, or (at your option) any | |
7 | * later version. | |
8 | * | |
9 | * FRRouting is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along | |
15 | * with this program; see the file COPYING; if not, write to the Free Software | |
16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
17 | */ | |
18 | ||
19 | #include <zebra.h> | |
20 | #include "command.h" | |
21 | ||
22 | #include "bgpd/bgpd.h" | |
23 | #include "bgpd/bgp_table.h" | |
24 | #include "bgpd/bgp_attr.h" | |
25 | #include "bgpd/bgp_ecommunity.h" | |
26 | #include "bgpd/bgp_vty.h" | |
27 | #include "bgpd/bgp_route.h" | |
28 | #include "bgpd/bgp_aspath.h" | |
29 | #include "bgpd/bgp_flowspec.h" | |
30 | #include "bgpd/bgp_flowspec_util.h" | |
31 | #include "bgpd/bgp_flowspec_private.h" | |
268e1b9b | 32 | #include "bgpd/bgp_debug.h" |
b588b642 | 33 | #include "bgpd/bgp_pbr.h" |
dba3c1d3 PG |
34 | |
35 | /* Local Structures and variables declarations | |
36 | * This code block hosts the struct declared that host the flowspec rules | |
37 | * as well as some structure used to convert to stringx | |
38 | */ | |
39 | ||
40 | static const struct message bgp_flowspec_display_large[] = { | |
41 | {FLOWSPEC_DEST_PREFIX, "Destination Address"}, | |
42 | {FLOWSPEC_SRC_PREFIX, "Source Address"}, | |
43 | {FLOWSPEC_IP_PROTOCOL, "IP Protocol"}, | |
44 | {FLOWSPEC_PORT, "Port"}, | |
45 | {FLOWSPEC_DEST_PORT, "Destination Port"}, | |
46 | {FLOWSPEC_SRC_PORT, "Source Port"}, | |
47 | {FLOWSPEC_ICMP_TYPE, "ICMP Type"}, | |
48 | {FLOWSPEC_ICMP_CODE, "ICMP Code"}, | |
49 | {FLOWSPEC_TCP_FLAGS, "TCP Flags"}, | |
50 | {FLOWSPEC_PKT_LEN, "Packet Length"}, | |
51 | {FLOWSPEC_DSCP, "DSCP field"}, | |
52 | {FLOWSPEC_FRAGMENT, "Packet Fragment"}, | |
53 | {0} | |
54 | }; | |
55 | ||
56 | static const struct message bgp_flowspec_display_min[] = { | |
57 | {FLOWSPEC_DEST_PREFIX, "to"}, | |
58 | {FLOWSPEC_SRC_PREFIX, "from"}, | |
59 | {FLOWSPEC_IP_PROTOCOL, "proto"}, | |
60 | {FLOWSPEC_PORT, "port"}, | |
61 | {FLOWSPEC_DEST_PORT, "dstp"}, | |
62 | {FLOWSPEC_SRC_PORT, "srcp"}, | |
63 | {FLOWSPEC_ICMP_TYPE, "type"}, | |
64 | {FLOWSPEC_ICMP_CODE, "code"}, | |
65 | {FLOWSPEC_TCP_FLAGS, "flags"}, | |
66 | {FLOWSPEC_PKT_LEN, "pktlen"}, | |
67 | {FLOWSPEC_DSCP, "dscp"}, | |
68 | {FLOWSPEC_FRAGMENT, "pktfrag"}, | |
69 | {0} | |
70 | }; | |
71 | ||
362a06e3 PG |
72 | #define FS_STRING_UPDATE(count, ptr, format, remaining_len) do { \ |
73 | int _len_written; \ | |
74 | \ | |
75 | if (((format) == NLRI_STRING_FORMAT_DEBUG) && (count)) {\ | |
76 | _len_written = snprintf((ptr), (remaining_len), \ | |
77 | ", "); \ | |
78 | (remaining_len) -= _len_written; \ | |
79 | (ptr) += _len_written; \ | |
80 | } else if (((format) == NLRI_STRING_FORMAT_MIN) \ | |
81 | && (count)) { \ | |
82 | _len_written = snprintf((ptr), (remaining_len), \ | |
83 | " "); \ | |
84 | (remaining_len) -= _len_written; \ | |
85 | (ptr) += _len_written; \ | |
86 | } \ | |
87 | count++; \ | |
dba3c1d3 PG |
88 | } while (0) |
89 | ||
362a06e3 PG |
90 | /* Parse FLOWSPEC NLRI |
91 | * passed return_string string has assumed length | |
92 | * BGP_FLOWSPEC_STRING_DISPLAY_MAX | |
93 | */ | |
dba3c1d3 | 94 | void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, |
d33fc23b PG |
95 | char *return_string, int format, |
96 | json_object *json_path) | |
dba3c1d3 PG |
97 | { |
98 | uint32_t offset = 0; | |
99 | int type; | |
100 | int ret = 0, error = 0; | |
101 | char *ptr = return_string; | |
102 | char local_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; | |
103 | int count = 0; | |
104 | char extra[2] = ""; | |
105 | char pre_extra[2] = ""; | |
106 | const struct message *bgp_flowspec_display; | |
d33fc23b | 107 | enum bgp_flowspec_util_nlri_t type_util; |
362a06e3 PG |
108 | int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; |
109 | int len_written; | |
dba3c1d3 PG |
110 | |
111 | if (format == NLRI_STRING_FORMAT_LARGE) { | |
112 | snprintf(pre_extra, sizeof(pre_extra), "\t"); | |
113 | snprintf(extra, sizeof(extra), "\n"); | |
114 | bgp_flowspec_display = bgp_flowspec_display_large; | |
115 | } else | |
116 | bgp_flowspec_display = bgp_flowspec_display_min; | |
d33fc23b PG |
117 | /* if needed. type_util can be set to other values */ |
118 | type_util = BGP_FLOWSPEC_RETURN_STRING; | |
dba3c1d3 PG |
119 | error = 0; |
120 | while (offset < len-1 && error >= 0) { | |
121 | type = nlri_content[offset]; | |
122 | offset++; | |
123 | switch (type) { | |
124 | case FLOWSPEC_DEST_PREFIX: | |
125 | case FLOWSPEC_SRC_PREFIX: | |
126 | ret = bgp_flowspec_ip_address( | |
d33fc23b | 127 | type_util, |
dba3c1d3 PG |
128 | nlri_content+offset, |
129 | len - offset, | |
130 | local_string, &error); | |
131 | if (ret <= 0) | |
132 | break; | |
d33fc23b PG |
133 | if (json_path) { |
134 | json_object_string_add(json_path, | |
135 | lookup_msg(bgp_flowspec_display, type, ""), | |
136 | local_string); | |
137 | break; | |
138 | } | |
362a06e3 PG |
139 | FS_STRING_UPDATE(count, ptr, format, len_string); |
140 | len_written = snprintf(ptr, len_string, "%s%s %s%s", | |
141 | pre_extra, | |
142 | lookup_msg(bgp_flowspec_display, | |
143 | type, ""), | |
144 | local_string, extra); | |
145 | len_string -= len_written; | |
146 | ptr += len_written; | |
dba3c1d3 PG |
147 | break; |
148 | case FLOWSPEC_IP_PROTOCOL: | |
149 | case FLOWSPEC_PORT: | |
150 | case FLOWSPEC_DEST_PORT: | |
151 | case FLOWSPEC_SRC_PORT: | |
152 | case FLOWSPEC_ICMP_TYPE: | |
153 | case FLOWSPEC_ICMP_CODE: | |
d33fc23b | 154 | ret = bgp_flowspec_op_decode(type_util, |
dba3c1d3 PG |
155 | nlri_content+offset, |
156 | len - offset, | |
157 | local_string, &error); | |
158 | if (ret <= 0) | |
159 | break; | |
d33fc23b PG |
160 | if (json_path) { |
161 | json_object_string_add(json_path, | |
162 | lookup_msg(bgp_flowspec_display, type, ""), | |
163 | local_string); | |
164 | break; | |
165 | } | |
362a06e3 PG |
166 | FS_STRING_UPDATE(count, ptr, format, len_string); |
167 | len_written = snprintf(ptr, len_string, "%s%s %s%s", | |
168 | pre_extra, | |
169 | lookup_msg(bgp_flowspec_display, | |
170 | type, ""), | |
dba3c1d3 | 171 | local_string, extra); |
362a06e3 PG |
172 | len_string -= len_written; |
173 | ptr += len_written; | |
dba3c1d3 PG |
174 | break; |
175 | case FLOWSPEC_TCP_FLAGS: | |
176 | ret = bgp_flowspec_tcpflags_decode( | |
d33fc23b | 177 | type_util, |
dba3c1d3 PG |
178 | nlri_content+offset, |
179 | len - offset, | |
180 | local_string, &error); | |
181 | if (ret <= 0) | |
182 | break; | |
d33fc23b PG |
183 | if (json_path) { |
184 | json_object_string_add(json_path, | |
362a06e3 PG |
185 | lookup_msg(bgp_flowspec_display, |
186 | type, ""), | |
d33fc23b PG |
187 | local_string); |
188 | break; | |
189 | } | |
362a06e3 PG |
190 | FS_STRING_UPDATE(count, ptr, format, len_string); |
191 | len_written = snprintf(ptr, len_string, "%s%s %s%s", | |
192 | pre_extra, | |
193 | lookup_msg(bgp_flowspec_display, | |
194 | type, ""), | |
195 | local_string, extra); | |
196 | len_string -= len_written; | |
197 | ptr += len_written; | |
dba3c1d3 PG |
198 | break; |
199 | case FLOWSPEC_PKT_LEN: | |
200 | case FLOWSPEC_DSCP: | |
201 | ret = bgp_flowspec_op_decode( | |
d33fc23b | 202 | type_util, |
dba3c1d3 PG |
203 | nlri_content + offset, |
204 | len - offset, local_string, | |
205 | &error); | |
206 | if (ret <= 0) | |
207 | break; | |
d33fc23b PG |
208 | if (json_path) { |
209 | json_object_string_add(json_path, | |
210 | lookup_msg(bgp_flowspec_display, type, ""), | |
211 | local_string); | |
212 | break; | |
213 | } | |
362a06e3 PG |
214 | FS_STRING_UPDATE(count, ptr, format, len_string); |
215 | len_written = snprintf(ptr, len_string, "%s%s %s%s", | |
216 | pre_extra, | |
217 | lookup_msg(bgp_flowspec_display, | |
218 | type, ""), | |
dba3c1d3 | 219 | local_string, extra); |
362a06e3 PG |
220 | len_string -= len_written; |
221 | ptr += len_written; | |
dba3c1d3 PG |
222 | break; |
223 | case FLOWSPEC_FRAGMENT: | |
224 | ret = bgp_flowspec_fragment_type_decode( | |
d33fc23b | 225 | type_util, |
dba3c1d3 PG |
226 | nlri_content + offset, |
227 | len - offset, local_string, | |
228 | &error); | |
229 | if (ret <= 0) | |
230 | break; | |
d33fc23b PG |
231 | if (json_path) { |
232 | json_object_string_add(json_path, | |
233 | lookup_msg(bgp_flowspec_display, | |
234 | type, ""), | |
235 | local_string); | |
236 | break; | |
237 | } | |
362a06e3 PG |
238 | FS_STRING_UPDATE(count, ptr, format, len_string); |
239 | len_written = snprintf(ptr, len_string, "%s%s %s%s", | |
240 | pre_extra, | |
241 | lookup_msg(bgp_flowspec_display, | |
242 | type, ""), | |
243 | local_string, extra); | |
244 | len_string -= len_written; | |
245 | ptr += len_written; | |
dba3c1d3 PG |
246 | break; |
247 | default: | |
248 | error = -1; | |
249 | break; | |
250 | } | |
251 | offset += ret; | |
252 | } | |
253 | } | |
254 | ||
255 | void route_vty_out_flowspec(struct vty *vty, struct prefix *p, | |
256 | struct bgp_info *binfo, | |
257 | int display, json_object *json_paths) | |
258 | { | |
259 | struct attr *attr; | |
260 | char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; | |
261 | char *s; | |
d33fc23b PG |
262 | json_object *json_nlri_path = NULL; |
263 | json_object *json_ecom_path = NULL; | |
264 | json_object *json_time_path = NULL; | |
265 | char timebuf[BGP_UPTIME_LEN]; | |
dba3c1d3 PG |
266 | |
267 | /* Print prefix */ | |
268 | if (p != NULL) { | |
269 | if (p->family != AF_FLOWSPEC) | |
270 | return; | |
d33fc23b PG |
271 | if (json_paths) { |
272 | if (display == NLRI_STRING_FORMAT_JSON) | |
273 | json_nlri_path = json_object_new_object(); | |
274 | else | |
275 | json_nlri_path = json_paths; | |
276 | } | |
dba3c1d3 PG |
277 | if (display == NLRI_STRING_FORMAT_LARGE) |
278 | vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", | |
279 | binfo->flags); | |
280 | bgp_fs_nlri_get_string((unsigned char *) | |
281 | p->u.prefix_flowspec.ptr, | |
282 | p->u.prefix_flowspec.prefixlen, | |
283 | return_string, | |
d33fc23b PG |
284 | display, |
285 | json_nlri_path); | |
dba3c1d3 PG |
286 | if (display == NLRI_STRING_FORMAT_LARGE) |
287 | vty_out(vty, "%s", return_string); | |
288 | else if (display == NLRI_STRING_FORMAT_DEBUG) | |
289 | vty_out(vty, "%s", return_string); | |
d33fc23b | 290 | else if (display == NLRI_STRING_FORMAT_MIN) |
dba3c1d3 | 291 | vty_out(vty, " %-30s", return_string); |
d33fc23b PG |
292 | else if (json_paths && display == NLRI_STRING_FORMAT_JSON) |
293 | json_object_array_add(json_paths, json_nlri_path); | |
dba3c1d3 PG |
294 | } |
295 | if (!binfo) | |
296 | return; | |
297 | if (binfo->attr && binfo->attr->ecommunity) { | |
298 | /* Print attribute */ | |
299 | attr = binfo->attr; | |
300 | s = ecommunity_ecom2str(attr->ecommunity, | |
301 | ECOMMUNITY_FORMAT_ROUTE_MAP, 0); | |
302 | if (!s) | |
303 | return; | |
304 | if (display == NLRI_STRING_FORMAT_LARGE) | |
305 | vty_out(vty, "\t%s\n", s); | |
d33fc23b | 306 | else if (display == NLRI_STRING_FORMAT_MIN) |
dba3c1d3 | 307 | vty_out(vty, "%s", s); |
d33fc23b PG |
308 | else if (json_paths) { |
309 | json_ecom_path = json_object_new_object(); | |
310 | json_object_string_add(json_ecom_path, | |
311 | "ecomlist", s); | |
312 | if (display == NLRI_STRING_FORMAT_JSON) | |
313 | json_object_array_add(json_paths, | |
314 | json_ecom_path); | |
315 | } | |
026b914a PG |
316 | if (attr->nexthop.s_addr != 0 && |
317 | display == NLRI_STRING_FORMAT_LARGE) | |
318 | vty_out(vty, "\tNH %-16s\n", inet_ntoa(attr->nexthop)); | |
dba3c1d3 PG |
319 | XFREE(MTYPE_ECOMMUNITY_STR, s); |
320 | } | |
d33fc23b | 321 | peer_uptime(binfo->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL); |
b588b642 PG |
322 | if (display == NLRI_STRING_FORMAT_LARGE) { |
323 | vty_out(vty, "\treceived for %8s\n", timebuf); | |
324 | } else if (json_paths) { | |
d33fc23b PG |
325 | json_time_path = json_object_new_object(); |
326 | json_object_string_add(json_time_path, | |
327 | "time", timebuf); | |
328 | if (display == NLRI_STRING_FORMAT_JSON) | |
329 | json_object_array_add(json_paths, json_time_path); | |
dba3c1d3 | 330 | } |
b588b642 PG |
331 | if (display == NLRI_STRING_FORMAT_LARGE) { |
332 | struct bgp_info_extra *extra = bgp_info_extra_get(binfo); | |
dba3c1d3 | 333 | |
b588b642 PG |
334 | if (extra->bgp_fs_pbr) { |
335 | struct bgp_pbr_match_entry *bpme; | |
336 | struct bgp_pbr_match *bpm; | |
337 | ||
338 | bpme = (struct bgp_pbr_match_entry *)extra->bgp_fs_pbr; | |
339 | bpm = bpme->backpointer; | |
340 | vty_out(vty, "\tinstalled in PBR"); | |
341 | if (bpm) | |
342 | vty_out(vty, " (%s)\n", bpm->ipset_name); | |
343 | else | |
344 | vty_out(vty, "\n"); | |
345 | } else | |
346 | vty_out(vty, "\tnot installed in PBR\n"); | |
347 | } | |
dba3c1d3 PG |
348 | } |
349 | ||
350 | int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, | |
351 | struct bgp_table *table, enum bgp_show_type type, | |
352 | void *output_arg, uint8_t use_json, | |
353 | int is_last, unsigned long *output_cum, | |
354 | unsigned long *total_cum) | |
355 | { | |
356 | struct bgp_info *ri; | |
357 | struct bgp_node *rn; | |
358 | unsigned long total_count = 0; | |
359 | json_object *json_paths = NULL; | |
d33fc23b | 360 | int display = NLRI_STRING_FORMAT_LARGE; |
dba3c1d3 PG |
361 | |
362 | if (type != bgp_show_type_detail) | |
363 | return CMD_SUCCESS; | |
364 | ||
dba3c1d3 PG |
365 | for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { |
366 | if (rn->info == NULL) | |
367 | continue; | |
d33fc23b PG |
368 | if (use_json) { |
369 | json_paths = json_object_new_array(); | |
370 | display = NLRI_STRING_FORMAT_JSON; | |
371 | } | |
dba3c1d3 PG |
372 | for (ri = rn->info; ri; ri = ri->next) { |
373 | total_count++; | |
374 | route_vty_out_flowspec(vty, &rn->p, | |
375 | ri, display, | |
376 | json_paths); | |
377 | ||
378 | } | |
d33fc23b PG |
379 | if (use_json) { |
380 | vty_out(vty, "%s\n", | |
381 | json_object_to_json_string_ext( | |
382 | json_paths, | |
383 | JSON_C_TO_STRING_PRETTY)); | |
384 | json_object_free(json_paths); | |
385 | json_paths = NULL; | |
386 | } | |
dba3c1d3 | 387 | } |
d33fc23b | 388 | if (total_count && !use_json) |
dba3c1d3 PG |
389 | vty_out(vty, |
390 | "\nDisplayed %ld flowspec entries\n", | |
391 | total_count); | |
392 | return CMD_SUCCESS; | |
393 | } | |
394 | ||
268e1b9b PG |
395 | DEFUN (debug_bgp_flowspec, |
396 | debug_bgp_flowspec_cmd, | |
397 | "debug bgp flowspec", | |
398 | DEBUG_STR | |
399 | BGP_STR | |
400 | "BGP allow flowspec debugging entries\n") | |
401 | { | |
402 | if (vty->node == CONFIG_NODE) | |
403 | DEBUG_ON(flowspec, FLOWSPEC); | |
404 | else { | |
405 | TERM_DEBUG_ON(flowspec, FLOWSPEC); | |
406 | vty_out(vty, "BGP flowspec debugging is on\n"); | |
407 | } | |
408 | return CMD_SUCCESS; | |
409 | } | |
410 | ||
411 | DEFUN (no_debug_bgp_flowspec, | |
412 | no_debug_bgp_flowspec_cmd, | |
413 | "no debug bgp flowspec", | |
414 | NO_STR | |
415 | DEBUG_STR | |
416 | BGP_STR | |
417 | "BGP allow flowspec debugging entries\n") | |
418 | { | |
419 | if (vty->node == CONFIG_NODE) | |
420 | DEBUG_OFF(flowspec, FLOWSPEC); | |
421 | else { | |
422 | TERM_DEBUG_OFF(flowspec, FLOWSPEC); | |
423 | vty_out(vty, "BGP flowspec debugging is off\n"); | |
424 | } | |
425 | return CMD_SUCCESS; | |
426 | } | |
427 | ||
4762c213 PG |
428 | int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, |
429 | afi_t afi, safi_t safi) | |
430 | { | |
431 | struct bgp_pbr_interface *pbr_if; | |
432 | bool declare_node = false; | |
433 | struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; | |
434 | struct bgp_pbr_interface_head *head; | |
435 | bool bgp_pbr_interface_any; | |
436 | ||
437 | if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC || afi != AFI_IP) | |
438 | return 0; | |
439 | head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); | |
440 | bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; | |
441 | if (!RB_EMPTY(bgp_pbr_interface_head, head) || | |
442 | !bgp_pbr_interface_any) | |
443 | declare_node = true; | |
444 | RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { | |
445 | vty_out(vty, " local-install %s\n", pbr_if->name); | |
446 | } | |
447 | if (!bgp_pbr_interface_any) | |
448 | vty_out(vty, " no local-install any\n"); | |
449 | return declare_node ? 1 : 0; | |
450 | } | |
451 | ||
452 | static int bgp_fs_local_install_interface(struct bgp *bgp, | |
453 | const char *no, const char *ifname) | |
454 | { | |
455 | struct bgp_pbr_interface *pbr_if; | |
456 | struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; | |
457 | struct bgp_pbr_interface_head *head; | |
458 | bool *bgp_pbr_interface_any; | |
459 | ||
460 | if (!bgp_pbr_cfg) | |
461 | return CMD_SUCCESS; | |
462 | head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); | |
463 | bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); | |
464 | if (no) { | |
465 | if (!ifname) { | |
466 | if (*bgp_pbr_interface_any) { | |
467 | *bgp_pbr_interface_any = false; | |
468 | /* remove all other interface list */ | |
469 | bgp_pbr_reset(bgp, AFI_IP); | |
470 | } | |
471 | return CMD_SUCCESS; | |
472 | } | |
473 | pbr_if = bgp_pbr_interface_lookup(ifname, head); | |
474 | if (!pbr_if) | |
475 | return CMD_SUCCESS; | |
476 | RB_REMOVE(bgp_pbr_interface_head, head, pbr_if); | |
477 | return CMD_SUCCESS; | |
478 | } | |
479 | if (ifname) { | |
480 | pbr_if = bgp_pbr_interface_lookup(ifname, head); | |
481 | if (pbr_if) | |
482 | return CMD_SUCCESS; | |
483 | pbr_if = XCALLOC(MTYPE_TMP, | |
484 | sizeof(struct bgp_pbr_interface)); | |
485 | strlcpy(pbr_if->name, ifname, INTERFACE_NAMSIZ); | |
486 | RB_INSERT(bgp_pbr_interface_head, head, pbr_if); | |
487 | *bgp_pbr_interface_any = false; | |
488 | } else { | |
489 | /* set to default */ | |
490 | if (!*bgp_pbr_interface_any) { | |
491 | /* remove all other interface list | |
492 | */ | |
493 | bgp_pbr_reset(bgp, AFI_IP); | |
494 | *bgp_pbr_interface_any = true; | |
495 | } | |
496 | } | |
497 | return CMD_SUCCESS; | |
498 | } | |
499 | ||
500 | DEFUN (bgp_fs_local_install_ifname, | |
501 | bgp_fs_local_install_ifname_cmd, | |
502 | "[no] local-install INTERFACE", | |
503 | NO_STR | |
504 | "Apply local policy routing\n" | |
505 | "Interface name\n") | |
506 | { | |
507 | struct bgp *bgp = VTY_GET_CONTEXT(bgp); | |
508 | int idx = 0; | |
509 | const char *no = strmatch(argv[0]->text, (char *)"no") ? "no" : NULL; | |
510 | char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ? | |
511 | argv[idx]->arg : NULL; | |
512 | ||
513 | return bgp_fs_local_install_interface(bgp, no, ifname); | |
514 | } | |
515 | ||
516 | DEFUN (bgp_fs_local_install_any, | |
517 | bgp_fs_local_install_any_cmd, | |
518 | "[no] local-install any", | |
519 | NO_STR | |
520 | "Apply local policy routing\n" | |
521 | "Any Interface\n") | |
522 | { | |
523 | struct bgp *bgp = VTY_GET_CONTEXT(bgp); | |
524 | const char *no = strmatch(argv[0]->text, (char *)"no") ? "no" : NULL; | |
525 | ||
526 | return bgp_fs_local_install_interface(bgp, no, NULL); | |
527 | } | |
528 | ||
dba3c1d3 PG |
529 | void bgp_flowspec_vty_init(void) |
530 | { | |
268e1b9b PG |
531 | install_element(ENABLE_NODE, &debug_bgp_flowspec_cmd); |
532 | install_element(CONFIG_NODE, &debug_bgp_flowspec_cmd); | |
533 | install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd); | |
534 | install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd); | |
4762c213 PG |
535 | install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_any_cmd); |
536 | install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd); | |
dba3c1d3 | 537 | } |