3 #include "rstp-common.h"
30 struct lan
*ports
[RSTP_MAX_PORTS
];
35 struct bpdu rxq
[RXQ_SIZE
];
36 int rxq_head
, rxq_tail
;
40 struct bridge
*bridge
;
48 struct lan_conn conns
[16];
53 struct bridge
*bridges
[16];
59 static const char *file_name
;
60 static int line_number
;
61 static char line
[128];
62 static char *pos
, *token
;
63 static int n_warnings
;
65 static struct test_case
*
68 struct test_case
*tc
= xmalloc(sizeof *tc
);
75 /* This callback is called with rstp_mutex held. */
77 send_bpdu(struct ofpbuf
*pkt
, void *port_
, void *b_
)
78 OVS_REQUIRES(rstp_mutex
)
80 struct bridge
*b
= b_
;
82 const struct rstp_port
*port
= port_
;
83 uint16_t port_no
= port
->port_number
;
85 assert(port_no
< b
->n_ports
);
86 lan
= b
->ports
[port_no
];
88 const void *data
= ofpbuf_l3(pkt
);
89 size_t size
= (char *) ofpbuf_tail(pkt
) - (char *) data
;
92 for (i
= 0; i
< lan
->n_conns
; i
++) {
93 struct lan_conn
*conn
= &lan
->conns
[i
];
95 if (conn
->bridge
!= b
|| conn
->port_no
!= port_no
) {
96 struct bridge
*dst
= conn
->bridge
;
97 struct bpdu
*bpdu
= &dst
->rxq
[dst
->rxq_head
++ % RXQ_SIZE
];
99 assert(dst
->rxq_head
- dst
->rxq_tail
<= RXQ_SIZE
);
100 bpdu
->data
= xmemdup(data
, size
);
102 bpdu
->port_no
= conn
->port_no
;
109 static struct bridge
*
110 new_bridge(struct test_case
*tc
, int id
)
112 struct bridge
*b
= xmalloc(sizeof *b
);
119 snprintf(name
, sizeof name
, "rstp%x", id
);
120 b
->rstp
= rstp_create(name
, id
, send_bpdu
, b
);
121 for (i
= 1; i
< MAX_PORTS
; i
++) {
122 p
= rstp_add_port(b
->rstp
);
123 rstp_port_set_aux(p
, p
);
124 rstp_port_set_state(p
, RSTP_DISABLED
);
125 rstp_port_set_mac_operational(p
, true);
128 assert(tc
->n_bridges
< ARRAY_SIZE(tc
->bridges
));
130 b
->n_active_ports
= 1;
131 b
->rxq_head
= b
->rxq_tail
= 0;
132 tc
->bridges
[tc
->n_bridges
++] = b
;
137 new_lan(struct test_case
*tc
, const char *name
)
139 struct lan
*lan
= xmalloc(sizeof *lan
);
141 lan
->name
= xstrdup(name
);
143 assert(tc
->n_lans
< ARRAY_SIZE(tc
->lans
));
144 tc
->lans
[tc
->n_lans
++] = lan
;
149 reconnect_port(struct bridge
*b
, int port_no
, struct lan
*new_lan
)
154 assert(port_no
< b
->n_ports
);
155 old_lan
= b
->ports
[port_no
];
156 if (old_lan
== new_lan
) {
160 /* Disconnect from old_lan. */
162 for (j
= 0; j
< old_lan
->n_conns
; j
++) {
163 struct lan_conn
*c
= &old_lan
->conns
[j
];
165 if (c
->bridge
== b
&& c
->port_no
== port_no
) {
166 memmove(c
, c
+ 1, sizeof *c
* (old_lan
->n_conns
- j
- 1));
173 /* Connect to new_lan. */
174 b
->ports
[port_no
] = new_lan
;
176 int conn_no
= new_lan
->n_conns
++;
178 assert(conn_no
< ARRAY_SIZE(new_lan
->conns
));
179 new_lan
->conns
[conn_no
].bridge
= b
;
180 new_lan
->conns
[conn_no
].port_no
= port_no
;
185 new_port(struct bridge
*b
, struct lan
*lan
, uint32_t path_cost
)
187 int port_no
= b
->n_ports
++;
188 struct rstp_port
*p
= rstp_get_port(b
->rstp
, port_no
);
190 assert(port_no
< ARRAY_SIZE(b
->ports
));
191 b
->ports
[port_no
] = NULL
;
193 reinitialize_port(p
);
194 rstp_port_set_path_cost(p
, path_cost
);
195 rstp_port_set_state(p
, RSTP_DISCARDING
);
196 rstp_port_set_mac_operational(p
, true);
197 reconnect_port(b
, port_no
, lan
);
201 dump(struct test_case
*tc
)
205 for (i
= 0; i
< tc
->n_bridges
; i
++) {
206 struct bridge
*b
= tc
->bridges
[i
];
207 struct rstp
*rstp
= b
->rstp
;
210 printf("%s:", rstp_get_name(rstp
));
211 if (rstp_is_root_bridge(rstp
)) {
215 for (j
= 0; j
< b
->n_ports
; j
++) {
216 struct rstp_port
*p
= rstp_get_port(rstp
, j
);
217 enum rstp_state state
= rstp_port_get_state(p
);
219 printf("\tport %d", j
);
221 printf(" (lan %s)", b
->ports
[j
]->name
);
223 printf(" (disconnected)");
225 printf(": %s", rstp_state_name(state
));
226 if (p
== rstp_get_root_port(rstp
)) {
227 printf(" (root port, root_path_cost=%u)",
228 rstp_get_root_path_cost(rstp
));
235 static void dump_lan_tree(struct test_case
*, struct lan
*, int level
);
238 dump_bridge_tree(struct test_case
*tc
, struct bridge
*b
, int level
)
246 for (i
= 0; i
< level
; i
++) {
249 printf("%s\n", rstp_get_name(b
->rstp
));
250 for (i
= 0; i
< b
->n_ports
; i
++) {
251 struct lan
*lan
= b
->ports
[i
];
252 struct rstp_port
*p
= rstp_get_port(b
->rstp
, i
);
254 if (rstp_port_get_state(p
) == RSTP_FORWARDING
&& lan
) {
255 dump_lan_tree(tc
, lan
, level
+ 1);
261 dump_lan_tree(struct test_case
*tc
, struct lan
*lan
, int level
)
269 for (i
= 0; i
< level
; i
++) {
272 printf("%s\n", lan
->name
);
273 for (i
= 0; i
< lan
->n_conns
; i
++) {
274 struct bridge
*b
= lan
->conns
[i
].bridge
;
276 dump_bridge_tree(tc
, b
, level
+ 1);
281 tree(struct test_case
*tc
)
285 for (i
= 0; i
< tc
->n_bridges
; i
++) {
286 struct bridge
*b
= tc
->bridges
[i
];
290 for (i
= 0; i
< tc
->n_lans
; i
++) {
291 struct lan
*lan
= tc
->lans
[i
];
293 lan
->reached
= false;
295 for (i
= 0; i
< tc
->n_bridges
; i
++) {
296 struct bridge
*b
= tc
->bridges
[i
];
297 struct rstp
*rstp
= b
->rstp
;
299 if (rstp_is_root_bridge(rstp
)) {
300 dump_bridge_tree(tc
, b
, 0);
306 simulate(struct test_case
*tc
, int granularity
)
308 int time
, i
, round_trips
;
310 for (time
= 0; time
< 1000 * 180; time
+= granularity
) {
312 for (i
= 0; i
< tc
->n_bridges
; i
++) {
313 rstp_tick_timers(tc
->bridges
[i
]->rstp
);
315 for (round_trips
= 0; round_trips
< granularity
; round_trips
++) {
318 for (i
= 0; i
< tc
->n_bridges
; i
++) {
319 struct bridge
*b
= tc
->bridges
[i
];
321 for (; b
->rxq_tail
!= b
->rxq_head
; b
->rxq_tail
++) {
322 struct bpdu
*bpdu
= &b
->rxq
[b
->rxq_tail
% RXQ_SIZE
];
324 rstp_port_received_bpdu(rstp_get_port(b
->rstp
,
326 bpdu
->data
, bpdu
->size
);
338 NO_RETURN
static void
339 err(const char *message
, ...)
343 err(const char *message
, ...)
347 fprintf(stderr
, "%s:%d:%"PRIdPTR
": ", file_name
, line_number
, pos
- line
);
348 va_start(args
, message
);
349 vfprintf(stderr
, message
, args
);
357 warn(const char *message
, ...)
361 warn(const char *message
, ...)
365 fprintf(stderr
, "%s:%d: ", file_name
, line_number
);
366 va_start(args
, message
);
367 vfprintf(stderr
, message
, args
);
379 while (isspace((unsigned char) *pos
)) {
389 if (isalpha((unsigned char) *pos
)) {
390 while (isalpha((unsigned char) *++pos
)) {
393 } else if (isdigit((unsigned char) *pos
)) {
394 if (*pos
== '0' && (pos
[1] == 'x' || pos
[1] == 'X')) {
396 while (isxdigit((unsigned char) *pos
)) {
400 while (isdigit((unsigned char) *++pos
)) {
409 token
= xmemdup0(start
, pos
- start
);
416 char *save_pos
= pos
;
418 if (token
&& isdigit((unsigned char) *token
)) {
419 *intp
= strtol(token
, NULL
, 0);
429 match(const char *want
)
431 if (token
&& !strcmp(want
, token
)) {
445 err("expected integer");
451 must_match(const char *want
)
454 err("expected \"%s\"", want
);
459 test_rstp_main(int argc
, char *argv
[])
461 struct test_case
*tc
;
465 vlog_set_pattern(VLF_CONSOLE
, "%c|%p|%m");
466 vlog_set_levels(NULL
, VLF_SYSLOG
, VLL_OFF
);
469 ovs_fatal(0, "usage: test-rstp INPUT.RSTP\n");
473 input_file
= fopen(file_name
, "r");
475 ovs_fatal(errno
, "error opening \"%s\"", file_name
);
478 tc
= new_test_case();
479 for (i
= 0; i
< 26; i
++) {
487 for (line_number
= 1; fgets(line
, sizeof line
, input_file
);
490 char *newline
, *hash
;
492 newline
= strchr(line
, '\n');
496 hash
= strchr(line
, '#');
505 if (match("bridge")) {
506 struct bridge
*bridge
;
507 int bridge_no
, port_no
;
509 bridge_no
= must_get_int();
510 if (bridge_no
< tc
->n_bridges
) {
511 bridge
= tc
->bridges
[bridge_no
];
512 } else if (bridge_no
== tc
->n_bridges
) {
513 bridge
= new_bridge(tc
, must_get_int());
515 err("bridges must be numbered consecutively from 0");
518 rstp_set_bridge_priority(bridge
->rstp
, must_get_int());
521 for (port_no
= 1; port_no
< MAX_PORTS
; port_no
++) {
522 struct rstp_port
*p
= rstp_get_port(bridge
->rstp
, port_no
);
524 if (!token
|| match("X")) {
526 reinitialize_port(p
);
527 rstp_port_set_state(p
, RSTP_DISABLED
);
528 rstp_port_set_mac_operational(p
, false);
529 } else if (match("_")) {
535 if (!strcmp(token
, "0")) {
537 } else if (strlen(token
) == 1
538 && islower((unsigned char)*token
)) {
539 lan
= tc
->lans
[*token
- 'a'];
541 err("%s is not a valid LAN name "
542 "(0 or a lowercase letter)", token
);
546 path_cost
= match(":") ? must_get_int() :
547 RSTP_DEFAULT_PORT_PATH_COST
;
548 if (port_no
< bridge
->n_ports
) {
550 reinitialize_port(p
);
551 rstp_port_set_path_cost(p
, path_cost
);
552 rstp_port_set_state(p
, RSTP_DISCARDING
);
553 rstp_port_set_mac_operational(p
, true);
554 reconnect_port(bridge
, port_no
, lan
);
555 } else if (port_no
== bridge
->n_ports
) {
556 new_port(bridge
, lan
, path_cost
);
557 bridge
->n_active_ports
++;
559 err("ports must be numbered consecutively");
562 rstp_port_set_priority(p
, must_get_int());
567 } else if (match("run")) {
568 simulate(tc
, must_get_int());
569 } else if (match("dump")) {
571 } else if (match("tree")) {
573 } else if (match("check")) {
576 int bridge_no
, port_no
;
579 bridge_no
= must_get_int();
580 if (bridge_no
>= tc
->n_bridges
) {
581 err("no bridge numbered %d", bridge_no
);
583 b
= tc
->bridges
[bridge_no
];
588 if (match("rootid")) {
592 rootid
= must_get_int();
594 rootid
|= (uint64_t) must_get_int() << 48;
596 rootid
|= UINT64_C(0x8000) << 48;
598 if (rstp_get_designated_root(rstp
) != rootid
) {
599 warn("%s: root "RSTP_ID_FMT
", not %"PRIx64
,
601 RSTP_ID_ARGS(rstp_get_designated_root(rstp
)),
605 cost_value
= rstp_get_root_path_cost(rstp
);
607 if (cost_value
!= 0) {
608 warn("%s: root path cost of root is %d instead of 0 \n",
609 rstp_get_name(rstp
), cost_value
);
611 if (!rstp_is_root_bridge(rstp
)) {
612 warn("%s: root is "RSTP_ID_FMT
", not "RSTP_ID_FMT
"",
614 RSTP_ID_ARGS(rstp_get_designated_root(rstp
)),
615 RSTP_ID_ARGS(rstp_get_bridge_id(rstp
)));
617 for (port_no
= 1; port_no
< b
->n_active_ports
; port_no
++) {
618 struct rstp_port
*p
= rstp_get_port(rstp
, port_no
);
619 enum rstp_state state
= rstp_port_get_state(p
);
621 if (state
!= RSTP_DISABLED
&& state
!= RSTP_FORWARDING
) {
622 warn("%s: root port %d in state %s",
623 rstp_get_name(b
->rstp
), port_no
,
624 rstp_state_name(state
));
628 for (port_no
= 1; port_no
< b
->n_active_ports
; port_no
++) {
629 struct rstp_port
*p
= rstp_get_port(rstp
, port_no
);
630 enum rstp_state state
;
632 if (token
== NULL
|| match("D")) {
633 state
= RSTP_DISABLED
;
634 } else if (match("Di")) {
635 state
= RSTP_DISCARDING
;
636 } else if (match("Le")) {
637 state
= RSTP_LEARNING
;
638 } else if (match("F")) {
639 state
= RSTP_FORWARDING
;
640 } else if (match("_")) {
643 err("unknown port state %s", token
);
645 if (rstp_port_get_state(p
) != state
) {
646 warn("%s port %d: state is %s but should be %s",
647 rstp_get_name(rstp
), port_no
,
648 rstp_state_name(rstp_port_get_state(p
)),
649 rstp_state_name(state
));
651 if (state
== RSTP_FORWARDING
) {
652 struct rstp_port
*root_port
= rstp_get_root_port(rstp
);
655 int root_path_cost
= must_get_int();
657 if (p
!= root_port
) {
658 warn("%s: port %d is not the root port",
659 rstp_get_name(rstp
), port_no
);
661 warn("%s: (there is no root port)",
662 rstp_get_name(rstp
));
664 warn("%s: (port %d is the root port)",
666 rstp_port_get_number(root_port
));
668 } else if (cost_value
!= root_path_cost
) {
669 warn("%s: root path cost is %d, should be %d",
674 } else if (p
== root_port
) {
675 warn("%s: port %d is the root port but "
676 "not expected to be",
677 rstp_get_name(rstp
), port_no
);
683 printf("failing because of %d warnings\n", n_warnings
);
688 printf("failing because of errors\n");
689 err("trailing garbage on line");
694 for (i
= 0; i
< tc
->n_lans
; i
++) {
695 struct lan
*lan
= tc
->lans
[i
];
697 free(CONST_CAST(char *, lan
->name
));
700 for (i
= 0; i
< tc
->n_bridges
; i
++) {
701 struct bridge
*bridge
= tc
->bridges
[i
];
704 for (j
= 1; j
< MAX_PORTS
; j
++) {
705 rstp_port_unref(rstp_get_port(bridge
->rstp
, j
));
707 rstp_unref(bridge
->rstp
);
713 OVSTEST_REGISTER("test-rstp", test_rstp_main
);