]> git.proxmox.com Git - mirror_frr.git/blob - tests/bgpd/test_peer_attr.c
tests: Add tests for overriding BGP 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 = NULL;
40
41 enum test_state {
42 TEST_SUCCESS,
43 TEST_COMMAND_ERROR,
44 TEST_CONFIG_ERROR,
45 TEST_ASSERT_ERROR,
46 TEST_INTERNAL_ERROR,
47 };
48
49 struct test {
50 enum test_state state;
51 char *desc;
52 char *error;
53 struct list *log;
54
55 struct vty *vty;
56 struct bgp *bgp;
57 struct peer *peer;
58 struct peer_group *group;
59 };
60
61 struct test_config {
62 int local_asn;
63 int peer_asn;
64 const char *peer_address;
65 const char *peer_group;
66 };
67
68 struct test_peer_family {
69 afi_t afi;
70 safi_t safi;
71 };
72
73 struct test_peer_attr {
74 const char *cmd;
75 const char *peer_cmd;
76 const char *group_cmd;
77
78 enum { PEER_AT_AF_FLAG = 0,
79 PEER_AT_AF_FILTER = 1,
80 } type;
81 union {
82 uint32_t flag;
83 struct {
84 uint32_t flag;
85 size_t direct;
86 } filter;
87 } u;
88 struct {
89 bool invert;
90 bool use_ibgp;
91 } o;
92
93 afi_t afi;
94 safi_t safi;
95 struct test_peer_family families[AFI_MAX * SAFI_MAX];
96 };
97
98 #define OUT_SYMBOL_INFO "\u25ba"
99 #define OUT_SYMBOL_OK "\u2714"
100 #define OUT_SYMBOL_NOK "\u2716"
101
102 /* clang-format off */
103 #define TEST_ASSERT(T, C) \
104 do { \
105 if ((T)->state != TEST_SUCCESS || (C)) \
106 break; \
107 \
108 (T)->state = TEST_ASSERT_ERROR; \
109 (T)->error = str_printf("assertion failed: %s", (#C)); \
110 } while (0)
111
112 #define TEST_AF_FLAGS(T, P, A, V, O) \
113 do { \
114 if ((T)->state != TEST_SUCCESS) \
115 break; \
116 \
117 TEST_ASSERT((T), !!CHECK_FLAG((P)->af_flags[(A)->afi][(A)->safi], (A)->u.flag) == ((V) ^ (A)->o.invert)); \
118 TEST_ASSERT((T), !!CHECK_FLAG((P)->af_flags_override[(A)->afi][(A)->safi], (A)->u.flag) == (O)); \
119 TEST_ASSERT((T), !!CHECK_FLAG((P)->af_flags_invert[(A)->afi][(A)->safi], (A)->u.flag) == (A)->o.invert); \
120 } while (0)
121
122 #define TEST_AF_FILTER(T, P, A, S, O) \
123 do { \
124 if ((T)->state != TEST_SUCCESS) \
125 break; \
126 \
127 TEST_ASSERT((T), !!CHECK_FLAG((P)->filter_override[(A)->afi][(A)->safi][(A)->u.filter.direct], (A)->u.filter.flag) == (O)); \
128 switch ((A)->u.filter.flag) { \
129 case PEER_FT_DISTRIBUTE_LIST: \
130 TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].dlist[(A)->u.filter.direct].name) == (S)); \
131 break; \
132 case PEER_FT_FILTER_LIST: \
133 TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].aslist[(A)->u.filter.direct].name) == (S)); \
134 break; \
135 case PEER_FT_PREFIX_LIST: \
136 TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].plist[(A)->u.filter.direct].name) == (S)); \
137 break; \
138 case PEER_FT_ROUTE_MAP: \
139 TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].map[(A)->u.filter.direct].name) == (S)); \
140 break; \
141 case PEER_FT_UNSUPPRESS_MAP: \
142 TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].usmap.name) == (S)); \
143 break; \
144 } \
145 } while (0)
146 /* clang-format on */
147
148 static struct test_config cfg = {
149 .local_asn = 100,
150 .peer_asn = 200,
151 .peer_address = "1.1.1.1",
152 .peer_group = "PG-TEST",
153 };
154
155 /* clang-format off */
156 static struct test_peer_attr test_peer_attrs[] = {
157 {
158 .cmd = "addpath-tx-all-paths",
159 .u.flag = PEER_FLAG_ADDPATH_TX_ALL_PATHS,
160 .families = {
161 { .afi = AFI_IP, .safi = SAFI_UNICAST },
162 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
163 }
164 },
165 {
166 .cmd = "addpath-tx-bestpath-per-AS",
167 .u.flag = PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS,
168 .families = {
169 { .afi = AFI_IP, .safi = SAFI_UNICAST },
170 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
171 }
172 },
173 {
174 .cmd = "allowas-in",
175 .peer_cmd = "allowas-in 1",
176 .group_cmd = "allowas-in 2",
177 .u.flag = PEER_FLAG_ALLOWAS_IN,
178 .families = {
179 { .afi = AFI_IP, .safi = SAFI_UNICAST },
180 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
181 }
182 },
183 {
184 .cmd = "allowas-in origin",
185 .u.flag = PEER_FLAG_ALLOWAS_IN_ORIGIN,
186 .families = {
187 { .afi = AFI_IP, .safi = SAFI_UNICAST },
188 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
189 }
190 },
191 {
192 .cmd = "as-override",
193 .u.flag = PEER_FLAG_AS_OVERRIDE,
194 .families = {
195 { .afi = AFI_IP, .safi = SAFI_UNICAST },
196 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
197 }
198 },
199 {
200 .cmd = "attribute-unchanged as-path",
201 .u.flag = PEER_FLAG_AS_PATH_UNCHANGED,
202 .families = {
203 { .afi = AFI_IP, .safi = SAFI_UNICAST },
204 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
205 }
206 },
207 {
208 .cmd = "attribute-unchanged next-hop",
209 .u.flag = PEER_FLAG_NEXTHOP_UNCHANGED,
210 .families = {
211 { .afi = AFI_IP, .safi = SAFI_UNICAST },
212 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
213 }
214 },
215 {
216 .cmd = "attribute-unchanged med",
217 .u.flag = PEER_FLAG_MED_UNCHANGED,
218 .families = {
219 { .afi = AFI_IP, .safi = SAFI_UNICAST },
220 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
221 }
222 },
223 {
224 .cmd = "attribute-unchanged as-path next-hop",
225 .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
226 | PEER_FLAG_NEXTHOP_UNCHANGED,
227 .families = {
228 { .afi = AFI_IP, .safi = SAFI_UNICAST },
229 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
230 }
231 },
232 {
233 .cmd = "attribute-unchanged as-path med",
234 .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
235 | PEER_FLAG_MED_UNCHANGED,
236 .families = {
237 { .afi = AFI_IP, .safi = SAFI_UNICAST },
238 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
239 }
240 },
241 {
242 .cmd = "attribute-unchanged as-path next-hop med",
243 .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
244 | PEER_FLAG_NEXTHOP_UNCHANGED
245 | PEER_FLAG_MED_UNCHANGED,
246 .families = {
247 { .afi = AFI_IP, .safi = SAFI_UNICAST },
248 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
249 }
250 },
251 {
252 .cmd = "capability orf prefix-list send",
253 .u.flag = PEER_FLAG_ORF_PREFIX_SM,
254 .families = {
255 { .afi = AFI_IP, .safi = SAFI_UNICAST },
256 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
257 }
258 },
259 {
260 .cmd = "capability orf prefix-list receive",
261 .u.flag = PEER_FLAG_ORF_PREFIX_RM,
262 .families = {
263 { .afi = AFI_IP, .safi = SAFI_UNICAST },
264 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
265 }
266 },
267 {
268 .cmd = "capability orf prefix-list both",
269 .u.flag = PEER_FLAG_ORF_PREFIX_SM | PEER_FLAG_ORF_PREFIX_RM,
270 .families = {
271 { .afi = AFI_IP, .safi = SAFI_UNICAST },
272 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
273 }
274 },
275 {
276 .cmd = "default-originate",
277 .u.flag = PEER_FLAG_DEFAULT_ORIGINATE,
278 .families = {
279 { .afi = AFI_IP, .safi = SAFI_UNICAST },
280 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
281 }
282 },
283 {
284 .cmd = "default-originate route-map",
285 .peer_cmd = "default-originate route-map RM-PEER",
286 .group_cmd = "default-originate route-map RM-GROUP",
287 .u.flag = PEER_FLAG_DEFAULT_ORIGINATE,
288 .families = {
289 { .afi = AFI_IP, .safi = SAFI_UNICAST },
290 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
291 }
292 },
293 {
294 .cmd = "distribute-list",
295 .peer_cmd = "distribute-list DL-PEER in",
296 .group_cmd = "distribute-list DL-GROUP in",
297 .type = PEER_AT_AF_FILTER,
298 .u.filter.flag = PEER_FT_DISTRIBUTE_LIST,
299 .u.filter.direct = FILTER_IN,
300 .families = {
301 { .afi = AFI_IP, .safi = SAFI_UNICAST },
302 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
303 }
304 },
305 {
306 .cmd = "distribute-list",
307 .peer_cmd = "distribute-list DL-PEER out",
308 .group_cmd = "distribute-list DL-GROUP out",
309 .type = PEER_AT_AF_FILTER,
310 .u.filter.flag = PEER_FT_DISTRIBUTE_LIST,
311 .u.filter.direct = FILTER_OUT,
312 .families = {
313 { .afi = AFI_IP, .safi = SAFI_UNICAST },
314 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
315 }
316 },
317 {
318 .cmd = "filter-list",
319 .peer_cmd = "filter-list FL-PEER in",
320 .group_cmd = "filter-list FL-GROUP in",
321 .type = PEER_AT_AF_FILTER,
322 .u.filter.flag = PEER_FT_FILTER_LIST,
323 .u.filter.direct = FILTER_IN,
324 .families = {
325 { .afi = AFI_IP, .safi = SAFI_UNICAST },
326 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
327 }
328 },
329 {
330 .cmd = "filter-list",
331 .peer_cmd = "filter-list FL-PEER out",
332 .group_cmd = "filter-list FL-GROUP out",
333 .type = PEER_AT_AF_FILTER,
334 .u.filter.flag = PEER_FT_FILTER_LIST,
335 .u.filter.direct = FILTER_OUT,
336 .families = {
337 { .afi = AFI_IP, .safi = SAFI_UNICAST },
338 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
339 }
340 },
341 {
342 .cmd = "maximum-prefix",
343 .peer_cmd = "maximum-prefix 10",
344 .group_cmd = "maximum-prefix 20",
345 .u.flag = PEER_FLAG_MAX_PREFIX,
346 .families = {
347 { .afi = AFI_IP, .safi = SAFI_UNICAST },
348 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
349 }
350 },
351 {
352 .cmd = "maximum-prefix",
353 .peer_cmd = "maximum-prefix 10 restart 100",
354 .group_cmd = "maximum-prefix 20 restart 200",
355 .u.flag = PEER_FLAG_MAX_PREFIX,
356 .families = {
357 { .afi = AFI_IP, .safi = SAFI_UNICAST },
358 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
359 }
360 },
361 {
362 .cmd = "maximum-prefix",
363 .peer_cmd = "maximum-prefix 10 1 restart 100",
364 .group_cmd = "maximum-prefix 20 2 restart 200",
365 .u.flag = PEER_FLAG_MAX_PREFIX,
366 .families = {
367 { .afi = AFI_IP, .safi = SAFI_UNICAST },
368 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
369 }
370 },
371 {
372 .cmd = "maximum-prefix",
373 .peer_cmd = "maximum-prefix 10 warning-only",
374 .group_cmd = "maximum-prefix 20 warning-only",
375 .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING,
376 .families = {
377 { .afi = AFI_IP, .safi = SAFI_UNICAST },
378 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
379 }
380 },
381 {
382 .cmd = "maximum-prefix",
383 .peer_cmd = "maximum-prefix 10 1 warning-only",
384 .group_cmd = "maximum-prefix 20 2 warning-only",
385 .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING,
386 .families = {
387 { .afi = AFI_IP, .safi = SAFI_UNICAST },
388 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
389 }
390 },
391 {
392 .cmd = "next-hop-self",
393 .u.flag = PEER_FLAG_NEXTHOP_SELF,
394 .families = {
395 { .afi = AFI_IP, .safi = SAFI_UNICAST },
396 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
397 }
398 },
399 {
400 .cmd = "next-hop-self force",
401 .u.flag = PEER_FLAG_FORCE_NEXTHOP_SELF,
402 .families = {
403 { .afi = AFI_IP, .safi = SAFI_UNICAST },
404 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
405 }
406 },
407 {
408 .cmd = "prefix-list",
409 .peer_cmd = "prefix-list PL-PEER in",
410 .group_cmd = "prefix-list PL-GROUP in",
411 .type = PEER_AT_AF_FILTER,
412 .u.filter.flag = PEER_FT_PREFIX_LIST,
413 .u.filter.direct = FILTER_IN,
414 .families = {
415 { .afi = AFI_IP, .safi = SAFI_UNICAST },
416 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
417 }
418 },
419 {
420 .cmd = "prefix-list",
421 .peer_cmd = "prefix-list PL-PEER out",
422 .group_cmd = "prefix-list PL-GROUP out",
423 .type = PEER_AT_AF_FILTER,
424 .u.filter.flag = PEER_FT_PREFIX_LIST,
425 .u.filter.direct = FILTER_OUT,
426 .families = {
427 { .afi = AFI_IP, .safi = SAFI_UNICAST },
428 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
429 }
430 },
431 {
432 .cmd = "remove-private-AS",
433 .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS,
434 .families = {
435 { .afi = AFI_IP, .safi = SAFI_UNICAST },
436 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
437 }
438 },
439 {
440 .cmd = "remove-private-AS all",
441 .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS
442 | PEER_FLAG_REMOVE_PRIVATE_AS_ALL,
443 .families = {
444 { .afi = AFI_IP, .safi = SAFI_UNICAST },
445 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
446 }
447 },
448 {
449 .cmd = "remove-private-AS replace-AS",
450 .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS
451 | PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE,
452 .families = {
453 { .afi = AFI_IP, .safi = SAFI_UNICAST },
454 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
455 }
456 },
457 {
458 .cmd = "remove-private-AS all replace-AS",
459 .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE,
460 .families = {
461 { .afi = AFI_IP, .safi = SAFI_UNICAST },
462 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
463 }
464 },
465 {
466 .cmd = "route-map",
467 .peer_cmd = "route-map RM-PEER in",
468 .group_cmd = "route-map RM-GROUP in",
469 .type = PEER_AT_AF_FILTER,
470 .u.filter.flag = PEER_FT_ROUTE_MAP,
471 .u.filter.direct = FILTER_IN,
472 .families = {
473 { .afi = AFI_IP, .safi = SAFI_UNICAST },
474 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
475 }
476 },
477 {
478 .cmd = "route-map",
479 .peer_cmd = "route-map RM-PEER out",
480 .group_cmd = "route-map RM-GROUP out",
481 .type = PEER_AT_AF_FILTER,
482 .u.filter.flag = PEER_FT_ROUTE_MAP,
483 .u.filter.direct = FILTER_OUT,
484 .families = {
485 { .afi = AFI_IP, .safi = SAFI_UNICAST },
486 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
487 }
488 },
489 {
490 .cmd = "route-reflector-client",
491 .u.flag = PEER_FLAG_REFLECTOR_CLIENT,
492 .o.use_ibgp = true,
493 .families = {
494 { .afi = AFI_IP, .safi = SAFI_UNICAST },
495 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
496 }
497 },
498 {
499 .cmd = "route-server-client",
500 .u.flag = PEER_FLAG_RSERVER_CLIENT,
501 .families = {
502 { .afi = AFI_IP, .safi = SAFI_UNICAST },
503 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
504 }
505 },
506 {
507 .cmd = "send-community",
508 .u.flag = PEER_FLAG_SEND_COMMUNITY,
509 .o.invert = true,
510 .families = {
511 { .afi = AFI_IP, .safi = SAFI_UNICAST },
512 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
513 }
514 },
515 {
516 .cmd = "send-community extended",
517 .u.flag = PEER_FLAG_SEND_EXT_COMMUNITY,
518 .o.invert = true,
519 .families = {
520 { .afi = AFI_IP, .safi = SAFI_UNICAST },
521 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
522 }
523 },
524 {
525 .cmd = "send-community large",
526 .u.flag = PEER_FLAG_SEND_LARGE_COMMUNITY,
527 .o.invert = true,
528 .families = {
529 { .afi = AFI_IP, .safi = SAFI_UNICAST },
530 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
531 }
532 },
533 {
534 .cmd = "soft-reconfiguration inbound",
535 .u.flag = PEER_FLAG_SOFT_RECONFIG,
536 .families = {
537 { .afi = AFI_IP, .safi = SAFI_UNICAST },
538 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
539 }
540 },
541 {
542 .cmd = "unsuppress-map",
543 .peer_cmd = "unsuppress-map UM-PEER",
544 .group_cmd = "unsuppress-map UM-GROUP",
545 .type = PEER_AT_AF_FILTER,
546 .u.filter.flag = PEER_FT_UNSUPPRESS_MAP,
547 .u.filter.direct = 0,
548 .families = {
549 { .afi = AFI_IP, .safi = SAFI_UNICAST },
550 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
551 }
552 },
553 {
554 .cmd = "weight",
555 .peer_cmd = "weight 100",
556 .group_cmd = "weight 200",
557 .u.flag = PEER_FLAG_WEIGHT,
558 .families = {
559 { .afi = AFI_IP, .safi = SAFI_UNICAST },
560 { .afi = AFI_IP6, .safi = SAFI_UNICAST },
561 }
562 },
563 {NULL}
564 };
565 /* clang-format on */
566
567 static char *str_vprintf(const char *fmt, va_list ap)
568 {
569 int ret;
570 int buf_size = 0;
571 char *buf = NULL;
572 va_list apc;
573
574 while (1) {
575 va_copy(apc, ap);
576 ret = vsnprintf(buf, buf_size, fmt, apc);
577 va_end(apc);
578
579 if (ret >= 0 && ret < buf_size)
580 break;
581
582 if (ret >= 0)
583 buf_size = ret + 1;
584 else
585 buf_size *= 2;
586
587 buf = XREALLOC(MTYPE_TMP, buf, buf_size);
588 }
589
590 return buf;
591 }
592
593 static char *str_printf(const char *fmt, ...)
594 {
595 char *buf;
596 va_list ap;
597
598 va_start(ap, fmt);
599 buf = str_vprintf(fmt, ap);
600 va_end(ap);
601
602 return buf;
603 }
604
605 static const char *str_from_afi(afi_t afi)
606 {
607 switch (afi) {
608 case AFI_IP:
609 return "ipv4";
610 case AFI_IP6:
611 return "ipv6";
612 default:
613 return "<unknown AFI>";
614 }
615 }
616
617 static const char *str_from_safi(safi_t safi)
618 {
619 switch (safi) {
620 case SAFI_UNICAST:
621 return "unicast";
622 case SAFI_MULTICAST:
623 return "multicast";
624 case SAFI_MPLS_VPN:
625 return "labeled-unicast";
626 case SAFI_FLOWSPEC:
627 return "flowspec";
628 default:
629 return "<unknown SAFI>";
630 }
631 }
632
633 static void test_execute(struct test *test, const char *fmt, ...)
634 {
635 int ret;
636 char *cmd;
637 va_list ap;
638 vector vline;
639
640 /* Skip execution if test instance has previously failed. */
641 if (test->state != TEST_SUCCESS)
642 return;
643
644 /* Format command string with variadic arguments. */
645 va_start(ap, fmt);
646 cmd = str_vprintf(fmt, ap);
647 va_end(ap);
648 if (!cmd) {
649 test->state = TEST_INTERNAL_ERROR;
650 test->error =
651 str_printf("could not format command string [%s]", fmt);
652 return;
653 }
654
655 /* Tokenize formatted command string. */
656 vline = cmd_make_strvec(cmd);
657 if (vline == NULL) {
658 test->state = TEST_INTERNAL_ERROR;
659 test->error = str_printf(
660 "tokenizing command string [%s] returned empty result",
661 cmd);
662 XFREE(MTYPE_TMP, cmd);
663
664 return;
665 }
666
667 /* Execute command (non-strict). */
668 ret = cmd_execute_command(vline, test->vty, NULL, 0);
669 if (ret != CMD_SUCCESS) {
670 test->state = TEST_COMMAND_ERROR;
671 test->error = str_printf(
672 "execution of command [%s] has failed with code [%d]",
673 cmd, ret);
674 }
675
676 /* Free memory and return. */
677 cmd_free_strvec(vline);
678 XFREE(MTYPE_TMP, cmd);
679 return;
680 }
681
682 static void test_config(struct test *test, const char *fmt, bool invert,
683 va_list ap)
684 {
685 char *matcher;
686 char *config;
687 bool matched;
688 va_list apc;
689
690 /* Skip execution if test instance has previously failed. */
691 if (test->state != TEST_SUCCESS)
692 return;
693
694 /* Format matcher string with variadic arguments. */
695 va_copy(apc, ap);
696 matcher = str_vprintf(fmt, apc);
697 va_end(apc);
698 if (!matcher) {
699 test->state = TEST_INTERNAL_ERROR;
700 test->error =
701 str_printf("could not format matcher string [%s]", fmt);
702 return;
703 }
704
705 /* Fetch BGP configuration into buffer. */
706 bgp_config_write(test->vty);
707 config = buffer_getstr(test->vty->obuf);
708 buffer_reset(test->vty->obuf);
709
710 /* Match config against matcher. */
711 matched = !!strstr(config, matcher);
712 if (!matched && !invert) {
713 test->state = TEST_CONFIG_ERROR;
714 test->error = str_printf("expected config [%s] to be present",
715 matcher);
716 } else if (matched && invert) {
717 test->state = TEST_CONFIG_ERROR;
718 test->error = str_printf("expected config [%s] to be absent",
719 matcher);
720 }
721
722 /* Free memory and return. */
723 XFREE(MTYPE_TMP, matcher);
724 XFREE(MTYPE_TMP, config);
725 return;
726 }
727
728 static void test_config_present(struct test *test, const char *fmt, ...)
729 {
730 va_list ap;
731
732 va_start(ap, fmt);
733 test_config(test, fmt, false, ap);
734 va_end(ap);
735 }
736
737 static void test_config_absent(struct test *test, const char *fmt, ...)
738 {
739 va_list ap;
740
741 va_start(ap, fmt);
742 test_config(test, fmt, true, ap);
743 va_end(ap);
744 }
745
746 static struct test *test_new(const char *desc, bool use_ibgp)
747 {
748 struct test *test;
749 union sockunion su;
750
751 test = XCALLOC(MTYPE_TMP, sizeof(struct test));
752 test->state = TEST_SUCCESS;
753 test->desc = XSTRDUP(MTYPE_TMP, desc);
754 test->log = list_new();
755
756 test->vty = vty_new();
757 test->vty->type = VTY_TERM;
758 test->vty->node = CONFIG_NODE;
759
760 /* Attempt gracefully to purge previous BGP configuration. */
761 test_execute(test, "no router bgp");
762 test->state = TEST_SUCCESS;
763
764 /* Initialize BGP test environment. */
765 test_execute(test, "router bgp %d", cfg.local_asn);
766 test_execute(test, "no bgp default ipv4-unicast");
767 test_execute(test, "neighbor %s peer-group", cfg.peer_group);
768 test_execute(test, "neighbor %s remote-as %d", cfg.peer_address,
769 use_ibgp ? cfg.local_asn : cfg.peer_asn);
770 if (test->state != TEST_SUCCESS)
771 return test;
772
773 /* Fetch default BGP instance. */
774 test->bgp = bgp_get_default();
775 if (!test->bgp) {
776 test->state = TEST_INTERNAL_ERROR;
777 test->error =
778 str_printf("could not retrieve default bgp instance");
779 return test;
780 }
781
782 /* Fetch peer instance. */
783 str2sockunion(cfg.peer_address, &su);
784 test->peer = peer_lookup(test->bgp, &su);
785 if (!test->peer) {
786 test->state = TEST_INTERNAL_ERROR;
787 test->error = str_printf(
788 "could not retrieve instance of bgp peer [%s]",
789 cfg.peer_address);
790 return test;
791 }
792
793 /* Fetch peer-group instance. */
794 test->group = peer_group_lookup(test->bgp, cfg.peer_group);
795 if (!test->group) {
796 test->state = TEST_INTERNAL_ERROR;
797 test->error = str_printf(
798 "could not retrieve instance of bgp peer-group [%s]",
799 cfg.peer_group);
800 return test;
801 }
802
803 return test;
804 };
805
806 static void test_log(struct test *test, const char *fmt, ...)
807 {
808 va_list ap;
809
810 /* Skip logging if test instance has previously failed. */
811 if (test->state != TEST_SUCCESS)
812 return;
813
814 /* Store formatted log message. */
815 va_start(ap, fmt);
816 listnode_add(test->log, str_vprintf(fmt, ap));
817 va_end(ap);
818 }
819
820 static void test_finish(struct test *test)
821 {
822 char *msg;
823 struct listnode *node, *nnode;
824
825 /* Print test output header. */
826 printf("%s [test] %s\n",
827 (test->state == TEST_SUCCESS) ? OUT_SYMBOL_OK : OUT_SYMBOL_NOK,
828 test->desc);
829
830 /* Print test log messages. */
831 for (ALL_LIST_ELEMENTS(test->log, node, nnode, msg)) {
832 printf("%s %s\n", OUT_SYMBOL_INFO, msg);
833 XFREE(MTYPE_TMP, msg);
834 }
835
836 /* Print test error message if available. */
837 if (test->state != TEST_SUCCESS && test->error)
838 printf("%s error: %s\n", OUT_SYMBOL_INFO, test->error);
839
840 /* Print machine-readable result of test. */
841 printf("%s\n", test->state == TEST_SUCCESS ? "OK" : "failed");
842
843 /* Cleanup allocated memory. */
844 if (test->vty) {
845 vty_close(test->vty);
846 test->vty = NULL;
847 }
848 if (test->log)
849 list_delete_and_null(&test->log);
850 if (test->desc)
851 XFREE(MTYPE_TMP, test->desc);
852 if (test->error)
853 XFREE(MTYPE_TMP, test->error);
854 XFREE(MTYPE_TMP, test);
855 }
856
857 static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
858 {
859 int tc = 1;
860 const char *ec = pa->o.invert ? "no " : "";
861 const char *dc = pa->o.invert ? "" : "no ";
862 const char *peer_cmd = pa->peer_cmd ?: pa->cmd;
863 const char *group_cmd = pa->group_cmd ?: pa->cmd;
864 struct peer *p = test->peer;
865 struct peer_group *g = test->group;
866
867 /* Test Case: Switch active address-family. */
868 if (pa->type == PEER_AT_AF_FLAG || pa->type == PEER_AT_AF_FILTER) {
869 test_log(test, "prepare: switch address-family to [%s]",
870 afi_safi_print(pa->afi, pa->safi));
871 test_execute(test, "address-family %s %s",
872 str_from_afi(pa->afi), str_from_safi(pa->safi));
873 }
874
875 /* Test Case: Set flag on BGP peer. */
876 test_log(test, "case %02d: set af-flag [%s] on [%s]", tc++, peer_cmd,
877 p->host);
878 test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
879 test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
880 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
881 if (pa->type == PEER_AT_AF_FLAG) {
882 TEST_AF_FLAGS(test, p, pa, true, true);
883 TEST_AF_FLAGS(test, g->conf, pa, false, false);
884 } else if (pa->type == PEER_AT_AF_FILTER) {
885 TEST_AF_FILTER(test, p, pa, true, true);
886 TEST_AF_FILTER(test, g->conf, pa, false, false);
887 }
888
889 /* Test Case: Add BGP peer to peer-group. */
890 test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
891 g->name);
892 test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
893 test_config_present(test, "neighbor %s peer-group %s", p->host,
894 g->name);
895 test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
896 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
897 if (pa->type == PEER_AT_AF_FLAG) {
898 TEST_AF_FLAGS(test, p, pa, true, true);
899 TEST_AF_FLAGS(test, g->conf, pa, false, false);
900 } else if (pa->type == PEER_AT_AF_FILTER) {
901 TEST_AF_FILTER(test, p, pa, true, true);
902 TEST_AF_FILTER(test, g->conf, pa, false, false);
903 }
904
905 /* Test Case: Re-add BGP peer to peer-group. */
906 test_log(test, "case %02d: re-add peer [%s] to group [%s]", tc++,
907 p->host, g->name);
908 test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
909 test_config_present(test, "neighbor %s peer-group %s", p->host,
910 g->name);
911 test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
912 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
913 if (pa->type == PEER_AT_AF_FLAG) {
914 TEST_AF_FLAGS(test, p, pa, true, true);
915 TEST_AF_FLAGS(test, g->conf, pa, false, false);
916 } else if (pa->type == PEER_AT_AF_FILTER) {
917 TEST_AF_FILTER(test, p, pa, true, true);
918 TEST_AF_FILTER(test, g->conf, pa, false, false);
919 }
920
921 /* Test Case: Set flag on BGP peer-group. */
922 test_log(test, "case %02d: set af-flag [%s] on [%s]", tc++, group_cmd,
923 g->name);
924 test_execute(test, "%sneighbor %s %s", ec, g->name, group_cmd);
925 test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
926 test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd);
927 if (pa->type == PEER_AT_AF_FLAG) {
928 TEST_AF_FLAGS(test, p, pa, true, true);
929 TEST_AF_FLAGS(test, g->conf, pa, true, false);
930 } else if (pa->type == PEER_AT_AF_FILTER) {
931 TEST_AF_FILTER(test, p, pa, true, true);
932 TEST_AF_FILTER(test, g->conf, pa, true, false);
933 }
934
935 /* Test Case: Unset flag on BGP peer-group. */
936 test_log(test, "case %02d: unset af-flag [%s] on [%s]", tc++, group_cmd,
937 g->name);
938 test_execute(test, "%sneighbor %s %s", dc, g->name, group_cmd);
939 test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
940 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
941 if (pa->type == PEER_AT_AF_FLAG) {
942 TEST_AF_FLAGS(test, p, pa, true, true);
943 TEST_AF_FLAGS(test, g->conf, pa, false, false);
944 } else if (pa->type == PEER_AT_AF_FILTER) {
945 TEST_AF_FILTER(test, p, pa, true, true);
946 TEST_AF_FILTER(test, g->conf, pa, false, false);
947 }
948
949 /* Test Case: Set flag on BGP peer-group. */
950 test_log(test, "case %02d: set af-flag [%s] on [%s]", tc++, group_cmd,
951 g->name);
952 test_execute(test, "%sneighbor %s %s", ec, g->name, group_cmd);
953 test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
954 test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd);
955 if (pa->type == PEER_AT_AF_FLAG) {
956 TEST_AF_FLAGS(test, p, pa, true, true);
957 TEST_AF_FLAGS(test, g->conf, pa, true, false);
958 } else if (pa->type == PEER_AT_AF_FILTER) {
959 TEST_AF_FILTER(test, p, pa, true, true);
960 TEST_AF_FILTER(test, g->conf, pa, true, false);
961 }
962
963 /* Test Case: Re-set flag on BGP peer. */
964 test_log(test, "case %02d: re-set af-flag [%s] on [%s]", tc++, peer_cmd,
965 p->host);
966 test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
967 test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
968 test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd);
969 if (pa->type == PEER_AT_AF_FLAG) {
970 TEST_AF_FLAGS(test, p, pa, true, true);
971 TEST_AF_FLAGS(test, g->conf, pa, true, false);
972 } else if (pa->type == PEER_AT_AF_FILTER) {
973 TEST_AF_FILTER(test, p, pa, true, true);
974 TEST_AF_FILTER(test, g->conf, pa, true, false);
975 }
976
977 /* Test Case: Unset flag on BGP peer. */
978 test_log(test, "case %02d: unset af-flag [%s] on [%s]", tc++, peer_cmd,
979 p->host);
980 test_execute(test, "%sneighbor %s %s", dc, p->host, peer_cmd);
981 test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
982 test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd);
983 if (pa->type == PEER_AT_AF_FLAG) {
984 TEST_AF_FLAGS(test, p, pa, true, false);
985 TEST_AF_FLAGS(test, g->conf, pa, true, false);
986 } else if (pa->type == PEER_AT_AF_FILTER) {
987 TEST_AF_FILTER(test, p, pa, true, false);
988 TEST_AF_FILTER(test, g->conf, pa, true, false);
989 }
990
991 /* Test Case: Unset flag on BGP peer-group. */
992 test_log(test, "case %02d: unset af-flag [%s] on [%s]", tc++, group_cmd,
993 g->name);
994 test_execute(test, "%sneighbor %s %s", dc, g->name, group_cmd);
995 test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
996 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
997 if (pa->type == PEER_AT_AF_FLAG) {
998 TEST_AF_FLAGS(test, p, pa, false, false);
999 TEST_AF_FLAGS(test, g->conf, pa, false, false);
1000 } else if (pa->type == PEER_AT_AF_FILTER) {
1001 TEST_AF_FILTER(test, p, pa, false, false);
1002 TEST_AF_FILTER(test, g->conf, pa, false, false);
1003 }
1004
1005 /* Test Case: Set flag on BGP peer. */
1006 test_log(test, "case %02d: set af-flag [%s] on [%s]", tc++, peer_cmd,
1007 p->host);
1008 test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
1009 test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
1010 test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
1011 if (pa->type == PEER_AT_AF_FLAG) {
1012 TEST_AF_FLAGS(test, p, pa, true, true);
1013 TEST_AF_FLAGS(test, g->conf, pa, false, false);
1014 } else if (pa->type == PEER_AT_AF_FILTER) {
1015 TEST_AF_FILTER(test, p, pa, true, true);
1016 TEST_AF_FILTER(test, g->conf, pa, false, false);
1017 }
1018 }
1019
1020 static void bgp_startup()
1021 {
1022 cmd_init(1);
1023 openzlog("testbgpd", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
1024 LOG_DAEMON);
1025 zprivs_preinit(&bgpd_privs);
1026 zprivs_init(&bgpd_privs);
1027
1028 master = thread_master_create(NULL);
1029 bgp_master_init(master);
1030 bgp_option_set(BGP_OPT_NO_LISTEN);
1031 vrf_init(NULL, NULL, NULL, NULL);
1032 bgp_init();
1033 bgp_pthreads_run();
1034 }
1035
1036 static void bgp_shutdown()
1037 {
1038 struct bgp *bgp;
1039 struct listnode *node, *nnode;
1040
1041 bgp_terminate();
1042 bgp_close();
1043 for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
1044 bgp_delete(bgp);
1045 bgp_dump_finish();
1046 bgp_route_finish();
1047 bgp_route_map_terminate();
1048 bgp_attr_finish();
1049 bgp_pthreads_finish();
1050 access_list_add_hook(NULL);
1051 access_list_delete_hook(NULL);
1052 access_list_reset();
1053 as_list_add_hook(NULL);
1054 as_list_delete_hook(NULL);
1055 bgp_filter_reset();
1056 prefix_list_add_hook(NULL);
1057 prefix_list_delete_hook(NULL);
1058 prefix_list_reset();
1059 community_list_terminate(bgp_clist);
1060 vrf_terminate();
1061 #ifdef ENABLE_BGP_VNC
1062 vnc_zebra_destroy();
1063 #endif
1064 bgp_zebra_destroy();
1065
1066 bf_free(bm->rd_idspace);
1067 list_delete_and_null(&bm->bgp);
1068 memset(bm, 0, sizeof(*bm));
1069
1070 vty_terminate();
1071 cmd_terminate();
1072 zprivs_terminate(&bgpd_privs);
1073 thread_master_free(master);
1074 master = NULL;
1075 closezlog();
1076 }
1077
1078 int main(void)
1079 {
1080 int i, ii;
1081 struct list *pa_list;
1082 struct test_peer_attr *pa, *pac;
1083 struct listnode *node, *nnode;
1084
1085 bgp_startup();
1086
1087 pa_list = list_new();
1088 i = 0;
1089 while (test_peer_attrs[i].cmd) {
1090 pa = &test_peer_attrs[i++];
1091
1092 if (pa->families[0].afi && pa->families[0].safi) {
1093 ii = 0;
1094
1095 while (pa->families[ii].afi && pa->families[ii].safi) {
1096 pac = XMALLOC(MTYPE_TMP,
1097 sizeof(struct test_peer_attr));
1098 memcpy(pac, pa, sizeof(struct test_peer_attr));
1099
1100 pac->afi = pa->families[ii].afi;
1101 pac->safi = pa->families[ii].safi;
1102 listnode_add(pa_list, pac);
1103
1104 ii++;
1105 }
1106 } else {
1107 pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr));
1108 memcpy(pac, pa, sizeof(struct test_peer_attr));
1109 listnode_add(pa_list, pac);
1110 }
1111 }
1112
1113 for (ALL_LIST_ELEMENTS(pa_list, node, nnode, pa)) {
1114 char *desc;
1115 struct test *test;
1116
1117 /* Build test description string. */
1118 if (pa->afi && pa->safi)
1119 desc = str_printf("peer\\%s-%s\\%s",
1120 str_from_afi(pa->afi),
1121 str_from_safi(pa->safi), pa->cmd);
1122 else
1123 desc = str_printf("peer\\%s", pa->cmd);
1124
1125 /* Initialize new test instance. */
1126 test = test_new(desc, pa->o.use_ibgp);
1127 XFREE(MTYPE_TMP, desc);
1128
1129 /* Execute tests and finish test instance. */
1130 test_peer_attr(test, pa);
1131 test_finish(test);
1132
1133 /* Print empty line as spacer. */
1134 printf("\n");
1135
1136 /* Free memory used for peer-attr declaration. */
1137 XFREE(MTYPE_TMP, pa);
1138 }
1139
1140 list_delete_and_null(&pa_list);
1141 bgp_shutdown();
1142
1143 return 0;
1144 }