]> git.proxmox.com Git - mirror_frr.git/blob - tests/bgpd/test_peer_attr.c
bgpd: Implement group-overrides for peer attrs
[mirror_frr.git] / tests / bgpd / test_peer_attr.c
1 /*
2 * BGP Peer Attribute Unit Tests
3 * Copyright (C) 2018 Pascal Mathis
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 #include <zebra.h>
20
21 #include "memory.h"
22 #include "plist.h"
23 #include "bgpd/bgpd.h"
24 #include "bgpd/bgp_attr.h"
25 #include "bgpd/bgp_regex.h"
26 #include "bgpd/bgp_clist.h"
27 #include "bgpd/bgp_dump.h"
28 #include "bgpd/bgp_filter.h"
29 #include "bgpd/bgp_route.h"
30 #include "bgpd/bgp_vty.h"
31 #include "bgpd/bgp_zebra.h"
32
33 #ifdef ENABLE_BGP_VNC
34 #include "bgpd/rfapi/rfapi_backend.h"
35 #endif
36
37 /* Required variables to link in libbgp */
38 struct zebra_privs_t bgpd_privs = {0};
39 struct thread_master *master;
40
41 enum test_state {
42 TEST_SUCCESS,
43 TEST_SKIPPING,
44 TEST_COMMAND_ERROR,
45 TEST_CONFIG_ERROR,
46 TEST_ASSERT_ERROR,
47 TEST_CUSTOM_ERROR,
48 TEST_INTERNAL_ERROR,
49 };
50
51 enum test_peer_attr_type {
52 PEER_AT_AF_FLAG = 0,
53 PEER_AT_AF_FILTER = 1,
54 PEER_AT_AF_CUSTOM = 2,
55 PEER_AT_GLOBAL_FLAG = 3,
56 PEER_AT_GLOBAL_CUSTOM = 4
57 };
58
59 struct test {
60 enum test_state state;
61 char *desc;
62 char *error;
63 struct list *log;
64
65 struct vty *vty;
66 struct bgp *bgp;
67 struct peer *peer;
68 struct peer_group *group;
69
70 struct {
71 bool use_ibgp;
72 bool use_iface_peer;
73 } o;
74 };
75
76 struct test_config {
77 int local_asn;
78 int peer_asn;
79 const char *peer_address;
80 const char *peer_interface;
81 const char *peer_group;
82 };
83
84 struct test_peer_family {
85 afi_t afi;
86 safi_t safi;
87 };
88
89 struct test_peer_attr {
90 const char *cmd;
91 const char *peer_cmd;
92 const char *group_cmd;
93
94 enum test_peer_attr_type type;
95 union {
96 uint32_t flag;
97 struct {
98 uint32_t flag;
99 size_t direct;
100 } filter;
101 } u;
102 struct {
103 bool invert_peer;
104 bool invert_group;
105 bool use_ibgp;
106 bool use_iface_peer;
107 bool skip_xfer_cases;
108 } o;
109
110 afi_t afi;
111 safi_t safi;
112 struct test_peer_family families[AFI_MAX * SAFI_MAX];
113
114 void (*custom_handler)(struct test *test, struct peer *peer,
115 struct peer *group, bool peer_set,
116 bool group_set);
117 };
118
119 #define OUT_SYMBOL_INFO "\u25ba"
120 #define OUT_SYMBOL_OK "\u2714"
121 #define OUT_SYMBOL_NOK "\u2716"
122
123 #define TEST_ASSERT_EQ(T, A, B) \
124 do { \
125 if ((T)->state != TEST_SUCCESS || ((A) == (B))) \
126 break; \
127 (T)->state = TEST_ASSERT_ERROR; \
128 (T)->error = str_printf( \
129 "assertion failed: %s[%d] == [%d]%s (%s:%d)", (#A), \
130 (A), (B), (#B), __FILE__, __LINE__); \
131 } while (0)
132
133 static struct test_config cfg = {
134 .local_asn = 100,
135 .peer_asn = 200,
136 .peer_address = "1.1.1.1",
137 .peer_interface = "IP-TEST",
138 .peer_group = "PG-TEST",
139 };
140
141 static struct test_peer_family test_default_families[] = {
142 {.afi = AFI_IP, .safi = SAFI_UNICAST},
143 {.afi = AFI_IP, .safi = SAFI_MULTICAST},
144 {.afi = AFI_IP6, .safi = SAFI_UNICAST},
145 {.afi = AFI_IP6, .safi = SAFI_MULTICAST},
146 };
147
148 static char *str_vprintf(const char *fmt, va_list ap)
149 {
150 int ret;
151 int buf_size = 0;
152 char *buf = NULL;
153 va_list apc;
154
155 while (1) {
156 va_copy(apc, ap);
157 ret = vsnprintf(buf, buf_size, fmt, apc);
158 va_end(apc);
159
160 if (ret >= 0 && ret < buf_size)
161 break;
162
163 if (ret >= 0)
164 buf_size = ret + 1;
165 else
166 buf_size *= 2;
167
168 buf = XREALLOC(MTYPE_TMP, buf, buf_size);
169 }
170
171 return buf;
172 }
173
174 static char *str_printf(const char *fmt, ...)
175 {
176 char *buf;
177 va_list ap;
178
179 va_start(ap, fmt);
180 buf = str_vprintf(fmt, ap);
181 va_end(ap);
182
183 return buf;
184 }
185
186 /* clang-format off */
187 static struct test_peer_attr test_peer_attrs[] = {
188 /* Peer Attributes */
189 {
190 .cmd = "advertisement-interval",
191 .peer_cmd = "advertisement-interval 10",
192 .group_cmd = "advertisement-interval 20",
193 .u.flag = PEER_FLAG_ROUTEADV,
194 .type = PEER_AT_GLOBAL_FLAG,
195 },
196 {
197 .cmd = "capability dynamic",
198 .u.flag = PEER_FLAG_DYNAMIC_CAPABILITY,
199 .type = PEER_AT_GLOBAL_FLAG,
200 },
201 {
202 .cmd = "capability extended-nexthop",
203 .u.flag = PEER_FLAG_CAPABILITY_ENHE,
204 .type = PEER_AT_GLOBAL_FLAG,
205 },
206 {
207 .cmd = "capability extended-nexthop",
208 .u.flag = PEER_FLAG_CAPABILITY_ENHE,
209 .type = PEER_AT_GLOBAL_FLAG,
210 .o.invert_peer = true,
211 .o.use_iface_peer = true,
212 },
213 {
214 .cmd = "description",
215 .peer_cmd = "description FRR Peer",
216 .group_cmd = "description FRR Group",
217 .type = PEER_AT_GLOBAL_CUSTOM,
218 },
219 {
220 .cmd = "disable-connected-check",
221 .u.flag = PEER_FLAG_DISABLE_CONNECTED_CHECK,
222 .type = PEER_AT_GLOBAL_FLAG,
223 },
224 {
225 .cmd = "dont-capability-negotiate",
226 .u.flag = PEER_FLAG_DONT_CAPABILITY,
227 .type = PEER_AT_GLOBAL_FLAG,
228 },
229 {
230 .cmd = "enforce-first-as",
231 .u.flag = PEER_FLAG_ENFORCE_FIRST_AS,
232 .type = PEER_AT_GLOBAL_FLAG,
233 },
234 {
235 .cmd = "local-as",
236 .peer_cmd = "local-as 1",
237 .group_cmd = "local-as 2",
238 .type = PEER_AT_GLOBAL_CUSTOM,
239 },
240 {
241 .cmd = "local-as 1 no-prepend",
242 .u.flag = PEER_FLAG_LOCAL_AS_NO_PREPEND,
243 .type = PEER_AT_GLOBAL_FLAG,
244 },
245 {
246 .cmd = "local-as 1 no-prepend replace-as",
247 .u.flag = PEER_FLAG_LOCAL_AS_REPLACE_AS,
248 .type = PEER_AT_GLOBAL_FLAG,
249 },
250 {
251 .cmd = "override-capability",
252 .u.flag = PEER_FLAG_OVERRIDE_CAPABILITY,
253 .type = PEER_AT_GLOBAL_FLAG,
254 },
255 {
256 .cmd = "passive",
257 .u.flag = PEER_FLAG_PASSIVE,
258 .type = PEER_AT_GLOBAL_FLAG,
259 },
260 {
261 .cmd = "password",
262 .peer_cmd = "password FRR-Peer",
263 .group_cmd = "password FRR-Group",
264 .type = PEER_AT_GLOBAL_CUSTOM,
265 },
266 {
267 .cmd = "shutdown",
268 .u.flag = PEER_FLAG_SHUTDOWN,
269 .type = PEER_AT_GLOBAL_FLAG,
270 },
271 {
272 .cmd = "strict-capability-match",
273 .u.flag = PEER_FLAG_STRICT_CAP_MATCH,
274 .type = PEER_AT_GLOBAL_FLAG,
275 },
276 {
277 .cmd = "timers",
278 .peer_cmd = "timers 10 30",
279 .group_cmd = "timers 20 60",
280 .u.flag = PEER_FLAG_TIMER,
281 .type = PEER_AT_GLOBAL_FLAG,
282 },
283 {
284 .cmd = "timers connect",
285 .peer_cmd = "timers connect 10",
286 .group_cmd = "timers connect 20",
287 .u.flag = PEER_FLAG_TIMER_CONNECT,
288 .type = PEER_AT_GLOBAL_FLAG,
289 },
290 {
291 .cmd = "update-source",
292 .peer_cmd = "update-source 255.255.255.1",
293 .group_cmd = "update-source 255.255.255.2",
294 .type = PEER_AT_GLOBAL_CUSTOM,
295 },
296 {
297 .cmd = "update-source",
298 .peer_cmd = "update-source IFACE-PEER",
299 .group_cmd = "update-source IFACE-GROUP",
300 .type = PEER_AT_GLOBAL_CUSTOM,
301 },
302
303 /* Address Family Attributes */
304 {
305 .cmd = "addpath-tx-all-paths",
306 .u.flag = PEER_FLAG_ADDPATH_TX_ALL_PATHS,
307 },
308 {
309 .cmd = "addpath-tx-bestpath-per-AS",
310 .u.flag = PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS,
311 },
312 {
313 .cmd = "allowas-in",
314 .peer_cmd = "allowas-in 1",
315 .group_cmd = "allowas-in 2",
316 .u.flag = PEER_FLAG_ALLOWAS_IN,
317 },
318 {
319 .cmd = "allowas-in origin",
320 .u.flag = PEER_FLAG_ALLOWAS_IN_ORIGIN,
321 },
322 {
323 .cmd = "as-override",
324 .u.flag = PEER_FLAG_AS_OVERRIDE,
325 },
326 {
327 .cmd = "attribute-unchanged as-path",
328 .u.flag = PEER_FLAG_AS_PATH_UNCHANGED,
329 },
330 {
331 .cmd = "attribute-unchanged next-hop",
332 .u.flag = PEER_FLAG_NEXTHOP_UNCHANGED,
333 },
334 {
335 .cmd = "attribute-unchanged med",
336 .u.flag = PEER_FLAG_MED_UNCHANGED,
337 },
338 {
339 .cmd = "attribute-unchanged as-path next-hop",
340 .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
341 | PEER_FLAG_NEXTHOP_UNCHANGED,
342 },
343 {
344 .cmd = "attribute-unchanged as-path med",
345 .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
346 | PEER_FLAG_MED_UNCHANGED,
347 },
348 {
349 .cmd = "attribute-unchanged as-path next-hop med",
350 .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
351 | PEER_FLAG_NEXTHOP_UNCHANGED
352 | PEER_FLAG_MED_UNCHANGED,
353 },
354 {
355 .cmd = "capability orf prefix-list send",
356 .u.flag = PEER_FLAG_ORF_PREFIX_SM,
357 },
358 {
359 .cmd = "capability orf prefix-list receive",
360 .u.flag = PEER_FLAG_ORF_PREFIX_RM,
361 },
362 {
363 .cmd = "capability orf prefix-list both",
364 .u.flag = PEER_FLAG_ORF_PREFIX_SM | PEER_FLAG_ORF_PREFIX_RM,
365 },
366 {
367 .cmd = "default-originate",
368 .u.flag = PEER_FLAG_DEFAULT_ORIGINATE,
369 },
370 {
371 .cmd = "default-originate route-map",
372 .peer_cmd = "default-originate route-map RM-PEER",
373 .group_cmd = "default-originate route-map RM-GROUP",
374 .u.flag = PEER_FLAG_DEFAULT_ORIGINATE,
375 },
376 {
377 .cmd = "distribute-list",
378 .peer_cmd = "distribute-list FL-PEER in",
379 .group_cmd = "distribute-list FL-GROUP in",
380 .type = PEER_AT_AF_FILTER,
381 .u.filter.flag = PEER_FT_DISTRIBUTE_LIST,
382 .u.filter.direct = FILTER_IN,
383 },
384 {
385 .cmd = "distribute-list",
386 .peer_cmd = "distribute-list FL-PEER out",
387 .group_cmd = "distribute-list FL-GROUP out",
388 .type = PEER_AT_AF_FILTER,
389 .u.filter.flag = PEER_FT_DISTRIBUTE_LIST,
390 .u.filter.direct = FILTER_OUT,
391 },
392 {
393 .cmd = "filter-list",
394 .peer_cmd = "filter-list FL-PEER in",
395 .group_cmd = "filter-list FL-GROUP in",
396 .type = PEER_AT_AF_FILTER,
397 .u.filter.flag = PEER_FT_FILTER_LIST,
398 .u.filter.direct = FILTER_IN,
399 },
400 {
401 .cmd = "filter-list",
402 .peer_cmd = "filter-list FL-PEER out",
403 .group_cmd = "filter-list FL-GROUP out",
404 .type = PEER_AT_AF_FILTER,
405 .u.filter.flag = PEER_FT_FILTER_LIST,
406 .u.filter.direct = FILTER_OUT,
407 },
408 {
409 .cmd = "maximum-prefix",
410 .peer_cmd = "maximum-prefix 10",
411 .group_cmd = "maximum-prefix 20",
412 .u.flag = PEER_FLAG_MAX_PREFIX,
413 },
414 {
415 .cmd = "maximum-prefix",
416 .peer_cmd = "maximum-prefix 10 restart 100",
417 .group_cmd = "maximum-prefix 20 restart 200",
418 .u.flag = PEER_FLAG_MAX_PREFIX,
419 },
420 {
421 .cmd = "maximum-prefix",
422 .peer_cmd = "maximum-prefix 10 1 restart 100",
423 .group_cmd = "maximum-prefix 20 2 restart 200",
424 .u.flag = PEER_FLAG_MAX_PREFIX,
425 },
426 {
427 .cmd = "maximum-prefix",
428 .peer_cmd = "maximum-prefix 10 warning-only",
429 .group_cmd = "maximum-prefix 20 warning-only",
430 .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING,
431 },
432 {
433 .cmd = "maximum-prefix",
434 .peer_cmd = "maximum-prefix 10 1 warning-only",
435 .group_cmd = "maximum-prefix 20 2 warning-only",
436 .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING,
437 },
438 {
439 .cmd = "next-hop-self",
440 .u.flag = PEER_FLAG_NEXTHOP_SELF,
441 },
442 {
443 .cmd = "next-hop-self force",
444 .u.flag = PEER_FLAG_FORCE_NEXTHOP_SELF,
445 },
446 {
447 .cmd = "prefix-list",
448 .peer_cmd = "prefix-list PL-PEER in",
449 .group_cmd = "prefix-list PL-GROUP in",
450 .type = PEER_AT_AF_FILTER,
451 .u.filter.flag = PEER_FT_PREFIX_LIST,
452 .u.filter.direct = FILTER_IN,
453 },
454 {
455 .cmd = "prefix-list",
456 .peer_cmd = "prefix-list PL-PEER out",
457 .group_cmd = "prefix-list PL-GROUP out",
458 .type = PEER_AT_AF_FILTER,
459 .u.filter.flag = PEER_FT_PREFIX_LIST,
460 .u.filter.direct = FILTER_OUT,
461 },
462 {
463 .cmd = "remove-private-AS",
464 .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS,
465 },
466 {
467 .cmd = "remove-private-AS all",
468 .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS
469 | PEER_FLAG_REMOVE_PRIVATE_AS_ALL,
470 },
471 {
472 .cmd = "remove-private-AS replace-AS",
473 .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS
474 | PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE,
475 },
476 {
477 .cmd = "remove-private-AS all replace-AS",
478 .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE,
479 },
480 {
481 .cmd = "route-map",
482 .peer_cmd = "route-map RM-PEER in",
483 .group_cmd = "route-map RM-GROUP in",
484 .type = PEER_AT_AF_FILTER,
485 .u.filter.flag = PEER_FT_ROUTE_MAP,
486 .u.filter.direct = FILTER_IN,
487 },
488 {
489 .cmd = "route-map",
490 .peer_cmd = "route-map RM-PEER out",
491 .group_cmd = "route-map RM-GROUP out",
492 .type = PEER_AT_AF_FILTER,
493 .u.filter.flag = PEER_FT_ROUTE_MAP,
494 .u.filter.direct = FILTER_OUT,
495 },
496 {
497 .cmd = "route-reflector-client",
498 .u.flag = PEER_FLAG_REFLECTOR_CLIENT,
499 .o.use_ibgp = true,
500 .o.skip_xfer_cases = true,
501 },
502 {
503 .cmd = "route-server-client",
504 .u.flag = PEER_FLAG_RSERVER_CLIENT,
505 },
506 {
507 .cmd = "send-community",
508 .u.flag = PEER_FLAG_SEND_COMMUNITY,
509 .o.invert_peer = true,
510 .o.invert_group = true,
511 },
512 {
513 .cmd = "send-community extended",
514 .u.flag = PEER_FLAG_SEND_EXT_COMMUNITY,
515 .o.invert_peer = true,
516 .o.invert_group = true,
517 },
518 {
519 .cmd = "send-community large",
520 .u.flag = PEER_FLAG_SEND_LARGE_COMMUNITY,
521 .o.invert_peer = true,
522 .o.invert_group = true,
523 },
524 {
525 .cmd = "soft-reconfiguration inbound",
526 .u.flag = PEER_FLAG_SOFT_RECONFIG,
527 },
528 {
529 .cmd = "unsuppress-map",
530 .peer_cmd = "unsuppress-map UM-PEER",
531 .group_cmd = "unsuppress-map UM-GROUP",
532 .type = PEER_AT_AF_FILTER,
533 .u.filter.flag = PEER_FT_UNSUPPRESS_MAP,
534 .u.filter.direct = 0,
535 },
536 {
537 .cmd = "weight",
538 .peer_cmd = "weight 100",
539 .group_cmd = "weight 200",
540 .u.flag = PEER_FLAG_WEIGHT,
541 },
542 {NULL}
543 };
544 /* clang-format on */
545
546 static const char *str_from_afi(afi_t afi)
547 {
548 switch (afi) {
549 case AFI_IP:
550 return "ipv4";
551 case AFI_IP6:
552 return "ipv6";
553 default:
554 return "<unknown AFI>";
555 }
556 }
557
558 static const char *str_from_safi(safi_t safi)
559 {
560 switch (safi) {
561 case SAFI_UNICAST:
562 return "unicast";
563 case SAFI_MULTICAST:
564 return "multicast";
565 default:
566 return "<unknown SAFI>";
567 }
568 }
569
570 static const char *str_from_attr_type(enum test_peer_attr_type at)
571 {
572 switch (at) {
573 case PEER_AT_GLOBAL_FLAG:
574 return "peer-flag";
575 case PEER_AT_AF_FLAG:
576 return "af-flag";
577 case PEER_AT_AF_FILTER:
578 return "af-filter";
579 case PEER_AT_GLOBAL_CUSTOM:
580 case PEER_AT_AF_CUSTOM:
581 return "custom";
582 default:
583 return NULL;
584 }
585 }
586
587 static bool is_attr_type_global(enum test_peer_attr_type at)
588 {
589 return at == PEER_AT_GLOBAL_FLAG || at == PEER_AT_GLOBAL_CUSTOM;
590 }
591
592 static void test_log(struct test *test, const char *fmt, ...)
593 {
594 va_list ap;
595
596 /* Skip logging if test instance has previously failed. */
597 if (test->state != TEST_SUCCESS)
598 return;
599
600 /* Store formatted log message. */
601 va_start(ap, fmt);
602 listnode_add(test->log, str_vprintf(fmt, ap));
603 va_end(ap);
604 }
605
606 static void test_execute(struct test *test, const char *fmt, ...)
607 {
608 int ret;
609 char *cmd;
610 va_list ap;
611 vector vline;
612
613 /* Skip execution if test instance has previously failed. */
614 if (test->state != TEST_SUCCESS)
615 return;
616
617 /* Format command string with variadic arguments. */
618 va_start(ap, fmt);
619 cmd = str_vprintf(fmt, ap);
620 va_end(ap);
621 if (!cmd) {
622 test->state = TEST_INTERNAL_ERROR;
623 test->error =
624 str_printf("could not format command string [%s]", fmt);
625 return;
626 }
627
628 /* Tokenize formatted command string. */
629 vline = cmd_make_strvec(cmd);
630 if (vline == NULL) {
631 test->state = TEST_INTERNAL_ERROR;
632 test->error = str_printf(
633 "tokenizing command string [%s] returned empty result",
634 cmd);
635 XFREE(MTYPE_TMP, cmd);
636
637 return;
638 }
639
640 /* Execute command (non-strict). */
641 ret = cmd_execute_command(vline, test->vty, NULL, 0);
642 if (ret != CMD_SUCCESS) {
643 test->state = TEST_COMMAND_ERROR;
644 test->error = str_printf(
645 "execution of command [%s] has failed with code [%d]",
646 cmd, ret);
647 }
648
649 /* Free memory. */
650 cmd_free_strvec(vline);
651 XFREE(MTYPE_TMP, cmd);
652 }
653
654 static void test_config(struct test *test, const char *fmt, bool invert,
655 va_list ap)
656 {
657 char *matcher;
658 char *config;
659 bool matched;
660 va_list apc;
661
662 /* Skip execution if test instance has previously failed. */
663 if (test->state != TEST_SUCCESS)
664 return;
665
666 /* Format matcher string with variadic arguments. */
667 va_copy(apc, ap);
668 matcher = str_vprintf(fmt, apc);
669 va_end(apc);
670 if (!matcher) {
671 test->state = TEST_INTERNAL_ERROR;
672 test->error =
673 str_printf("could not format matcher string [%s]", fmt);
674 return;
675 }
676
677 /* Fetch BGP configuration into buffer. */
678 bgp_config_write(test->vty);
679 config = buffer_getstr(test->vty->obuf);
680 buffer_reset(test->vty->obuf);
681
682 /* Match config against matcher. */
683 matched = !!strstr(config, matcher);
684 if (!matched && !invert) {
685 test->state = TEST_CONFIG_ERROR;
686 test->error = str_printf("expected config [%s] to be present",
687 matcher);
688 } else if (matched && invert) {
689 test->state = TEST_CONFIG_ERROR;
690 test->error = str_printf("expected config [%s] to be absent",
691 matcher);
692 }
693
694 /* Free memory and return. */
695 XFREE(MTYPE_TMP, matcher);
696 XFREE(MTYPE_TMP, config);
697 }
698
699 static void test_config_present(struct test *test, const char *fmt, ...)
700 {
701 va_list ap;
702
703 va_start(ap, fmt);
704 test_config(test, fmt, false, ap);
705 va_end(ap);
706 }
707
708 static void test_config_absent(struct test *test, const char *fmt, ...)
709 {
710 va_list ap;
711
712 va_start(ap, fmt);
713 test_config(test, fmt, true, ap);
714 va_end(ap);
715 }
716
717 static void test_initialize(struct test *test)
718 {
719 union sockunion su;
720
721 /* Skip execution if test instance has previously failed. */
722 if (test->state != TEST_SUCCESS)
723 return;
724
725 /* Log message about (re)-initialization */
726 test_log(test, "prepare: %sinitialize bgp test environment",
727 test->bgp ? "re-" : "");
728
729 /* Attempt gracefully to purge previous BGP configuration. */
730 test_execute(test, "no router bgp");
731 test->state = TEST_SUCCESS;
732
733 /* Initialize BGP test environment. */
734 test_execute(test, "router bgp %d", cfg.local_asn);
735 test_execute(test, "no bgp default ipv4-unicast");
736 test_execute(test, "neighbor %s peer-group", cfg.peer_group);
737 if (test->o.use_iface_peer) {
738 test_execute(test, "neighbor %s interface", cfg.peer_interface);
739 test_execute(test, "neighbor %s remote-as %d",
740 cfg.peer_interface,
741 test->o.use_ibgp ? cfg.local_asn : cfg.peer_asn);
742 } else {
743 test_execute(test, "neighbor %s remote-as %d", cfg.peer_address,
744 test->o.use_ibgp ? cfg.local_asn : cfg.peer_asn);
745 }
746
747 if (test->state != TEST_SUCCESS)
748 return;
749
750 /* Fetch default BGP instance. */
751 test->bgp = bgp_get_default();
752 if (!test->bgp) {
753 test->state = TEST_INTERNAL_ERROR;
754 test->error =
755 str_printf("could not retrieve default bgp instance");
756 return;
757 }
758
759 /* Fetch peer instance. */
760 if (test->o.use_iface_peer) {
761 test->peer =
762 peer_lookup_by_conf_if(test->bgp, cfg.peer_interface);
763 } else {
764 str2sockunion(cfg.peer_address, &su);
765 test->peer = peer_lookup(test->bgp, &su);
766 }
767 if (!test->peer) {
768 test->state = TEST_INTERNAL_ERROR;
769 test->error = str_printf(
770 "could not retrieve instance of bgp peer [%s]",
771 cfg.peer_address);
772 return;
773 }
774
775 /* Fetch peer-group instance. */
776 test->group = peer_group_lookup(test->bgp, cfg.peer_group);
777 if (!test->group) {
778 test->state = TEST_INTERNAL_ERROR;
779 test->error = str_printf(
780 "could not retrieve instance of bgp peer-group [%s]",
781 cfg.peer_group);
782 return;
783 }
784 }
785
786 static struct test *test_new(const char *desc, bool use_ibgp,
787 bool use_iface_peer)
788 {
789 struct test *test;
790
791 test = XCALLOC(MTYPE_TMP, sizeof(struct test));
792 test->state = TEST_SUCCESS;
793 test->desc = XSTRDUP(MTYPE_TMP, desc);
794 test->log = list_new();
795 test->o.use_ibgp = use_ibgp;
796 test->o.use_iface_peer = use_iface_peer;
797
798 test->vty = vty_new();
799 test->vty->type = VTY_TERM;
800 test->vty->node = CONFIG_NODE;
801
802 test_initialize(test);
803
804 return test;
805 };
806
807 static void test_finish(struct test *test)
808 {
809 char *msg;
810 struct listnode *node, *nnode;
811
812 /* Print test output header. */
813 printf("%s [test] %s\n",
814 (test->state == TEST_SUCCESS) ? OUT_SYMBOL_OK : OUT_SYMBOL_NOK,
815 test->desc);
816
817 /* Print test log messages. */
818 for (ALL_LIST_ELEMENTS(test->log, node, nnode, msg)) {
819 printf("%s %s\n", OUT_SYMBOL_INFO, msg);
820 XFREE(MTYPE_TMP, msg);
821 }
822
823 /* Print test error message if available. */
824 if (test->state != TEST_SUCCESS && test->error)
825 printf("%s error: %s\n", OUT_SYMBOL_INFO, test->error);
826
827 /* Print machine-readable result of test. */
828 printf("%s\n", test->state == TEST_SUCCESS ? "OK" : "failed");
829
830 /* Cleanup allocated memory. */
831 if (test->vty) {
832 vty_close(test->vty);
833 test->vty = NULL;
834 }
835 if (test->log)
836 list_delete_and_null(&test->log);
837 if (test->desc)
838 XFREE(MTYPE_TMP, test->desc);
839 if (test->error)
840 XFREE(MTYPE_TMP, test->error);
841 XFREE(MTYPE_TMP, test);
842 }
843
844 static void test_peer_flags(struct test *test, struct test_peer_attr *pa,
845 struct peer *peer, bool exp_val, bool exp_ovrd)
846 {
847 bool exp_inv, cur_val, cur_ovrd, cur_inv;
848
849 /* Skip execution if test instance has previously failed. */
850 if (test->state != TEST_SUCCESS)
851 return;
852
853 /* Detect if flag is meant to be inverted. */
854 if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
855 exp_inv = pa->o.invert_group;
856 else
857 exp_inv = pa->o.invert_peer;
858
859 /* Flip expected value if flag is inverted. */
860 exp_val ^= exp_inv;
861
862 /* Fetch current state of value, override and invert flags. */
863 if (pa->type == PEER_AT_GLOBAL_FLAG) {
864 cur_val = !!CHECK_FLAG(peer->flags, pa->u.flag);
865 cur_ovrd = !!CHECK_FLAG(peer->flags_override, pa->u.flag);
866 cur_inv = !!CHECK_FLAG(peer->flags_invert, pa->u.flag);
867 } else /* if (pa->type == PEER_AT_AF_FLAG) */ {
868 cur_val = !!CHECK_FLAG(peer->af_flags[pa->afi][pa->safi],
869 pa->u.flag);
870 cur_ovrd = !!CHECK_FLAG(
871 peer->af_flags_override[pa->afi][pa->safi], pa->u.flag);
872 cur_inv = !!CHECK_FLAG(peer->af_flags_invert[pa->afi][pa->safi],
873 pa->u.flag);
874 }
875
876 /* Assert expected flag states. */
877 TEST_ASSERT_EQ(test, cur_val, exp_val);
878 TEST_ASSERT_EQ(test, cur_ovrd, exp_ovrd);
879 TEST_ASSERT_EQ(test, cur_inv, exp_inv);
880 }
881
882 static void test_af_filter(struct test *test, struct test_peer_attr *pa,
883 struct peer *peer, bool exp_state, bool exp_ovrd)
884 {
885 bool cur_ovrd;
886 struct bgp_filter *filter;
887
888 /* Skip execution if test instance has previously failed. */
889 if (test->state != TEST_SUCCESS)
890 return;
891
892 /* Fetch and assert current state of override flag. */
893 cur_ovrd = !!CHECK_FLAG(
894 peer->filter_override[pa->afi][pa->safi][pa->u.filter.direct],
895 pa->u.filter.flag);
896
897 TEST_ASSERT_EQ(test, cur_ovrd, exp_ovrd);
898
899 /* Assert that map/list matches expected state (set/unset). */
900 filter = &peer->filter[pa->afi][pa->safi];
901
902 switch (pa->u.filter.flag) {
903 case PEER_FT_DISTRIBUTE_LIST:
904 TEST_ASSERT_EQ(test,
905 !!(filter->dlist[pa->u.filter.direct].name),
906 exp_state);
907 break;
908 case PEER_FT_FILTER_LIST:
909 TEST_ASSERT_EQ(test,
910 !!(filter->aslist[pa->u.filter.direct].name),
911 exp_state);
912 break;
913 case PEER_FT_PREFIX_LIST:
914 TEST_ASSERT_EQ(test,
915 !!(filter->plist[pa->u.filter.direct].name),
916 exp_state);
917 break;
918 case PEER_FT_ROUTE_MAP:
919 TEST_ASSERT_EQ(test, !!(filter->map[pa->u.filter.direct].name),
920 exp_state);
921 break;
922 case PEER_FT_UNSUPPRESS_MAP:
923 TEST_ASSERT_EQ(test, !!(filter->usmap.name), exp_state);
924 break;
925 }
926 }
927
928 static void test_custom(struct test *test, struct test_peer_attr *pa,
929 struct peer *peer, struct peer *group, bool peer_set,
930 bool group_set)
931 {
932 char *handler_error;
933
934 /* Skip execution if test instance has previously failed. */
935 if (test->state != TEST_SUCCESS)
936 return;
937
938 /* Skip execution if no custom handler is defined. */
939 if (!pa->custom_handler)
940 return;
941
942 /* Execute custom handler. */
943 pa->custom_handler(test, peer, group, peer_set, group_set);
944 if (test->state != TEST_SUCCESS) {
945 test->state = TEST_CUSTOM_ERROR;
946 handler_error = test->error;
947 test->error =
948 str_printf("custom handler failed: %s", handler_error);
949 XFREE(MTYPE_TMP, handler_error);
950 }
951 }
952
953
954 static void test_process(struct test *test, struct test_peer_attr *pa,
955 struct peer *peer, struct peer *group, bool peer_set,
956 bool group_set)
957 {
958 switch (pa->type) {
959 case PEER_AT_GLOBAL_FLAG:
960 case PEER_AT_AF_FLAG:
961 test_peer_flags(test, pa, peer, peer_set || group_set,
962 peer_set);
963 test_peer_flags(test, pa, group, group_set, false);
964 break;
965
966 case PEER_AT_AF_FILTER:
967 test_af_filter(test, pa, peer, peer_set || group_set, peer_set);
968 test_af_filter(test, pa, group, group_set, false);
969 break;
970
971 case PEER_AT_GLOBAL_CUSTOM:
972 case PEER_AT_AF_CUSTOM:
973 /*
974 * Do nothing here - a custom handler can be executed, but this
975 * is not required. This will allow defining peer attributes
976 * which shall not be checked for flag/filter/other internal
977 * states.
978 */
979 break;
980
981 default:
982 test->state = TEST_INTERNAL_ERROR;
983 test->error =
984 str_printf("invalid attribute type: %d", pa->type);
985 break;
986 }
987
988 /* Attempt to call a custom handler if set for further processing. */
989 test_custom(test, pa, peer, group, peer_set, group_set);
990 }
991
992 static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
993 {
994 int tc = 1;
995 const char *type;
996 const char *ecp = pa->o.invert_peer ? "no " : "";
997 const char *dcp = pa->o.invert_peer ? "" : "no ";
998 const char *ecg = pa->o.invert_group ? "no " : "";
999 const char *dcg = pa->o.invert_group ? "" : "no ";
1000 const char *peer_cmd = pa->peer_cmd ?: pa->cmd;
1001 const char *group_cmd = pa->group_cmd ?: pa->cmd;
1002 struct peer *p = test->peer;
1003 struct peer_group *g = test->group;
1004
1005 /* Determine type and if test is address-family relevant */
1006 type = str_from_attr_type(pa->type);
1007 if (!type) {
1008 test->state = TEST_INTERNAL_ERROR;
1009 test->error =
1010 str_printf("invalid attribute type: %d", pa->type);
1011 return;
1012 }
1013
1014 /* Test Preparation: Switch and activate address-family. */
1015 if (!is_attr_type_global(pa->type)) {
1016 test_log(test, "prepare: switch address-family to [%s]",
1017 afi_safi_print(pa->afi, pa->safi));
1018 test_execute(test, "address-family %s %s",
1019 str_from_afi(pa->afi), str_from_safi(pa->safi));
1020 test_execute(test, "neighbor %s activate", g->name);
1021 test_execute(test, "neighbor %s activate", p->host);
1022 }
1023
1024 /* Skip peer-group to peer transfer test cases if requested. */
1025 if (pa->o.skip_xfer_cases && test->state == TEST_SUCCESS)
1026 test->state = TEST_SKIPPING;
1027
1028 /* Test Case: Set flag on BGP peer. */
1029 test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
1030 p->host);
1031 test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1032 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1033 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1034 test_process(test, pa, p, g->conf, true, false);
1035
1036 /* Test Case: Set flag on BGP peer-group. */
1037 test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
1038 g->name);
1039 test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1040 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1041 test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1042 test_process(test, pa, p, g->conf, true, true);
1043
1044 /* Test Case: Add BGP peer to peer-group. */
1045 test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
1046 g->name);
1047 test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
1048 test_config_present(test, "neighbor %s %speer-group %s", p->host,
1049 p->conf_if ? "interface " : "", g->name);
1050 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1051 test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1052 test_process(test, pa, p, g->conf, true, true);
1053
1054 /* Test Case: Unset flag on BGP peer-group. */
1055 test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
1056 group_cmd, g->name);
1057 test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
1058 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1059 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1060 test_process(test, pa, p, g->conf, true, false);
1061
1062 /* Stop skipping test cases if previously enabled. */
1063 if (pa->o.skip_xfer_cases && test->state == TEST_SKIPPING)
1064 test->state = TEST_SUCCESS;
1065
1066 /* Test Preparation: Re-initialize test environment. */
1067 test_initialize(test);
1068 p = test->peer;
1069 g = test->group;
1070
1071 /* Test Preparation: Switch and activate address-family. */
1072 if (!is_attr_type_global(pa->type)) {
1073 test_log(test, "prepare: switch address-family to [%s]",
1074 afi_safi_print(pa->afi, pa->safi));
1075 test_execute(test, "address-family %s %s",
1076 str_from_afi(pa->afi), str_from_safi(pa->safi));
1077 test_execute(test, "neighbor %s activate", g->name);
1078 test_execute(test, "neighbor %s activate", p->host);
1079 }
1080
1081 /* Test Case: Set flag on BGP peer. */
1082 test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
1083 p->host);
1084 test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1085 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1086 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1087 test_process(test, pa, p, g->conf, true, false);
1088
1089 /* Test Case: Add BGP peer to peer-group. */
1090 test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
1091 g->name);
1092 test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
1093 test_config_present(test, "neighbor %s %speer-group %s", p->host,
1094 p->conf_if ? "interface " : "", g->name);
1095 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1096 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1097 test_process(test, pa, p, g->conf, true, false);
1098
1099 /* Test Case: Re-add BGP peer to peer-group. */
1100 test_log(test, "case %02d: re-add peer [%s] to group [%s]", tc++,
1101 p->host, g->name);
1102 test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
1103 test_config_present(test, "neighbor %s %speer-group %s", p->host,
1104 p->conf_if ? "interface " : "", g->name);
1105 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1106 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1107 test_process(test, pa, p, g->conf, true, false);
1108
1109 /* Test Case: Set flag on BGP peer-group. */
1110 test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
1111 g->name);
1112 test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1113 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1114 test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1115 test_process(test, pa, p, g->conf, true, true);
1116
1117 /* Test Case: Unset flag on BGP peer-group. */
1118 test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
1119 group_cmd, g->name);
1120 test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
1121 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1122 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1123 test_process(test, pa, p, g->conf, true, false);
1124
1125 /* Test Case: Set flag on BGP peer-group. */
1126 test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
1127 g->name);
1128 test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1129 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1130 test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1131 test_process(test, pa, p, g->conf, true, true);
1132
1133 /* Test Case: Re-set flag on BGP peer. */
1134 test_log(test, "case %02d: re-set %s [%s] on [%s]", tc++, type,
1135 peer_cmd, p->host);
1136 test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1137 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1138 test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1139 test_process(test, pa, p, g->conf, true, true);
1140
1141 /* Test Case: Unset flag on BGP peer. */
1142 test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type, peer_cmd,
1143 p->host);
1144 test_execute(test, "%sneighbor %s %s", dcp, p->host, peer_cmd);
1145 test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
1146 test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
1147 test_process(test, pa, p, g->conf, false, true);
1148
1149 /* Test Case: Unset flag on BGP peer-group. */
1150 test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
1151 group_cmd, g->name);
1152 test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
1153 test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
1154 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1155 test_process(test, pa, p, g->conf, false, false);
1156
1157 /* Test Case: Set flag on BGP peer. */
1158 test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
1159 p->host);
1160 test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1161 test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
1162 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1163 test_process(test, pa, p, g->conf, true, false);
1164 }
1165
1166 static void bgp_startup(void)
1167 {
1168 cmd_init(1);
1169 openzlog("testbgpd", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
1170 LOG_DAEMON);
1171 zprivs_preinit(&bgpd_privs);
1172 zprivs_init(&bgpd_privs);
1173
1174 master = thread_master_create(NULL);
1175 bgp_master_init(master);
1176 bgp_option_set(BGP_OPT_NO_LISTEN);
1177 vrf_init(NULL, NULL, NULL, NULL);
1178 bgp_init();
1179 bgp_pthreads_run();
1180 }
1181
1182 static void bgp_shutdown(void)
1183 {
1184 struct bgp *bgp;
1185 struct listnode *node, *nnode;
1186
1187 bgp_terminate();
1188 bgp_close();
1189 for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
1190 bgp_delete(bgp);
1191 bgp_dump_finish();
1192 bgp_route_finish();
1193 bgp_route_map_terminate();
1194 bgp_attr_finish();
1195 bgp_pthreads_finish();
1196 access_list_add_hook(NULL);
1197 access_list_delete_hook(NULL);
1198 access_list_reset();
1199 as_list_add_hook(NULL);
1200 as_list_delete_hook(NULL);
1201 bgp_filter_reset();
1202 prefix_list_add_hook(NULL);
1203 prefix_list_delete_hook(NULL);
1204 prefix_list_reset();
1205 community_list_terminate(bgp_clist);
1206 vrf_terminate();
1207 #ifdef ENABLE_BGP_VNC
1208 vnc_zebra_destroy();
1209 #endif
1210 bgp_zebra_destroy();
1211
1212 bf_free(bm->rd_idspace);
1213 list_delete_and_null(&bm->bgp);
1214 memset(bm, 0, sizeof(*bm));
1215
1216 vty_terminate();
1217 cmd_terminate();
1218 zprivs_terminate(&bgpd_privs);
1219 thread_master_free(master);
1220 master = NULL;
1221 closezlog();
1222 }
1223
1224 int main(void)
1225 {
1226 int i, ii;
1227 struct list *pa_list;
1228 struct test_peer_attr *pa, *pac;
1229 struct listnode *node, *nnode;
1230
1231 bgp_startup();
1232
1233 pa_list = list_new();
1234 i = 0;
1235 while (test_peer_attrs[i].cmd) {
1236 pa = &test_peer_attrs[i++];
1237
1238 /* Just copy the peer attribute structure for global flags. */
1239 if (is_attr_type_global(pa->type)) {
1240 pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr));
1241 memcpy(pac, pa, sizeof(struct test_peer_attr));
1242 listnode_add(pa_list, pac);
1243 continue;
1244 }
1245
1246 /* Fallback to default families if not specified. */
1247 if (!pa->families[0].afi && !pa->families[0].safi)
1248 memcpy(&pa->families, test_default_families,
1249 sizeof(test_default_families));
1250
1251 /* Add peer attribute definition for each address family. */
1252 ii = 0;
1253 while (pa->families[ii].afi && pa->families[ii].safi) {
1254 pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr));
1255 memcpy(pac, pa, sizeof(struct test_peer_attr));
1256
1257 pac->afi = pa->families[ii].afi;
1258 pac->safi = pa->families[ii].safi;
1259 listnode_add(pa_list, pac);
1260
1261 ii++;
1262 }
1263 }
1264
1265 for (ALL_LIST_ELEMENTS(pa_list, node, nnode, pa)) {
1266 char *desc;
1267 struct test *test;
1268
1269 /* Build test description string. */
1270 if (pa->afi && pa->safi)
1271 desc = str_printf("peer\\%s-%s\\%s",
1272 str_from_afi(pa->afi),
1273 str_from_safi(pa->safi), pa->cmd);
1274 else
1275 desc = str_printf("peer\\%s", pa->cmd);
1276
1277 /* Initialize new test instance. */
1278 test = test_new(desc, pa->o.use_ibgp, pa->o.use_iface_peer);
1279 XFREE(MTYPE_TMP, desc);
1280
1281 /* Execute tests and finish test instance. */
1282 test_peer_attr(test, pa);
1283 test_finish(test);
1284
1285 /* Print empty line as spacer. */
1286 printf("\n");
1287
1288 /* Free memory used for peer-attr declaration. */
1289 XFREE(MTYPE_TMP, pa);
1290 }
1291
1292 list_delete_and_null(&pa_list);
1293 bgp_shutdown();
1294
1295 return 0;
1296 }