3 #include "rstp-common.h"
10 #include "openvswitch/ofpbuf.h"
12 #include "dp-packet.h"
14 #include "openvswitch/vlog.h"
31 struct lan
*ports
[RSTP_MAX_PORTS
];
36 struct bpdu rxq
[RXQ_SIZE
];
37 int rxq_head
, rxq_tail
;
41 struct bridge
*bridge
;
49 struct lan_conn conns
[16];
54 struct bridge
*bridges
[16];
60 static const char *file_name
;
61 static int line_number
;
62 static char line
[128];
63 static char *pos
, *token
;
64 static int n_warnings
;
66 static struct test_case
*
69 struct test_case
*tc
= xmalloc(sizeof *tc
);
76 /* This callback is called with rstp_mutex held. */
78 send_bpdu(struct dp_packet
*pkt
, void *port_
, void *b_
)
79 OVS_REQUIRES(rstp_mutex
)
81 struct bridge
*b
= b_
;
83 const struct rstp_port
*port
= port_
;
84 uint16_t port_no
= port
->port_number
;
86 assert(port_no
< b
->n_ports
);
87 lan
= b
->ports
[port_no
];
89 const void *data
= dp_packet_l3(pkt
);
90 size_t size
= (char *) dp_packet_tail(pkt
) - (char *) data
;
93 for (i
= 0; i
< lan
->n_conns
; i
++) {
94 struct lan_conn
*conn
= &lan
->conns
[i
];
96 if (conn
->bridge
!= b
|| conn
->port_no
!= port_no
) {
97 struct bridge
*dst
= conn
->bridge
;
98 struct bpdu
*bpdu
= &dst
->rxq
[dst
->rxq_head
++ % RXQ_SIZE
];
100 assert(dst
->rxq_head
- dst
->rxq_tail
<= RXQ_SIZE
);
101 bpdu
->data
= xmemdup(data
, size
);
103 bpdu
->port_no
= conn
->port_no
;
107 dp_packet_delete(pkt
);
110 static struct bridge
*
111 new_bridge(struct test_case
*tc
, int id
)
113 struct bridge
*b
= xmalloc(sizeof *b
);
120 snprintf(name
, sizeof name
, "rstp%x", id
);
121 b
->rstp
= rstp_create(name
, id
, send_bpdu
, b
);
122 for (i
= 1; i
< MAX_PORTS
; i
++) {
123 p
= rstp_add_port(b
->rstp
);
124 rstp_port_set_aux(p
, p
);
125 rstp_port_set_state(p
, RSTP_DISABLED
);
126 rstp_port_set_mac_operational(p
, true);
129 assert(tc
->n_bridges
< ARRAY_SIZE(tc
->bridges
));
131 b
->n_active_ports
= 1;
132 b
->rxq_head
= b
->rxq_tail
= 0;
133 tc
->bridges
[tc
->n_bridges
++] = b
;
138 new_lan(struct test_case
*tc
, const char *name
)
140 struct lan
*lan
= xmalloc(sizeof *lan
);
142 lan
->name
= xstrdup(name
);
144 assert(tc
->n_lans
< ARRAY_SIZE(tc
->lans
));
145 tc
->lans
[tc
->n_lans
++] = lan
;
150 reconnect_port(struct bridge
*b
, int port_no
, struct lan
*new_lan
)
155 assert(port_no
< b
->n_ports
);
156 old_lan
= b
->ports
[port_no
];
157 if (old_lan
== new_lan
) {
161 /* Disconnect from old_lan. */
163 for (j
= 0; j
< old_lan
->n_conns
; j
++) {
164 struct lan_conn
*c
= &old_lan
->conns
[j
];
166 if (c
->bridge
== b
&& c
->port_no
== port_no
) {
167 memmove(c
, c
+ 1, sizeof *c
* (old_lan
->n_conns
- j
- 1));
174 /* Connect to new_lan. */
175 b
->ports
[port_no
] = new_lan
;
177 int conn_no
= new_lan
->n_conns
++;
179 assert(conn_no
< ARRAY_SIZE(new_lan
->conns
));
180 new_lan
->conns
[conn_no
].bridge
= b
;
181 new_lan
->conns
[conn_no
].port_no
= port_no
;
186 new_port(struct bridge
*b
, struct lan
*lan
, uint32_t path_cost
)
188 int port_no
= b
->n_ports
++;
189 struct rstp_port
*p
= rstp_get_port(b
->rstp
, port_no
);
191 assert(port_no
< ARRAY_SIZE(b
->ports
));
192 b
->ports
[port_no
] = NULL
;
194 reinitialize_port(p
);
195 rstp_port_set_path_cost(p
, path_cost
);
196 rstp_port_set_state(p
, RSTP_DISCARDING
);
197 rstp_port_set_mac_operational(p
, true);
198 reconnect_port(b
, port_no
, lan
);
202 dump(struct test_case
*tc
)
206 for (i
= 0; i
< tc
->n_bridges
; i
++) {
207 struct bridge
*b
= tc
->bridges
[i
];
208 struct rstp
*rstp
= b
->rstp
;
211 printf("%s:", rstp_get_name(rstp
));
212 if (rstp_is_root_bridge(rstp
)) {
216 for (j
= 0; j
< b
->n_ports
; j
++) {
217 struct rstp_port
*p
= rstp_get_port(rstp
, j
);
218 enum rstp_state state
= rstp_port_get_state(p
);
220 printf("\tport %d", j
);
222 printf(" (lan %s)", b
->ports
[j
]->name
);
224 printf(" (disconnected)");
226 printf(": %s", rstp_state_name(state
));
227 if (p
== rstp_get_root_port(rstp
)) {
228 printf(" (root port, root_path_cost=%u)",
229 rstp_get_root_path_cost(rstp
));
236 static void dump_lan_tree(struct test_case
*, struct lan
*, int level
);
239 dump_bridge_tree(struct test_case
*tc
, struct bridge
*b
, int level
)
247 for (i
= 0; i
< level
; i
++) {
250 printf("%s\n", rstp_get_name(b
->rstp
));
251 for (i
= 0; i
< b
->n_ports
; i
++) {
252 struct lan
*lan
= b
->ports
[i
];
253 struct rstp_port
*p
= rstp_get_port(b
->rstp
, i
);
255 if (rstp_port_get_state(p
) == RSTP_FORWARDING
&& lan
) {
256 dump_lan_tree(tc
, lan
, level
+ 1);
262 dump_lan_tree(struct test_case
*tc
, struct lan
*lan
, int level
)
270 for (i
= 0; i
< level
; i
++) {
273 printf("%s\n", lan
->name
);
274 for (i
= 0; i
< lan
->n_conns
; i
++) {
275 struct bridge
*b
= lan
->conns
[i
].bridge
;
277 dump_bridge_tree(tc
, b
, level
+ 1);
282 tree(struct test_case
*tc
)
286 for (i
= 0; i
< tc
->n_bridges
; i
++) {
287 struct bridge
*b
= tc
->bridges
[i
];
291 for (i
= 0; i
< tc
->n_lans
; i
++) {
292 struct lan
*lan
= tc
->lans
[i
];
294 lan
->reached
= false;
296 for (i
= 0; i
< tc
->n_bridges
; i
++) {
297 struct bridge
*b
= tc
->bridges
[i
];
298 struct rstp
*rstp
= b
->rstp
;
300 if (rstp_is_root_bridge(rstp
)) {
301 dump_bridge_tree(tc
, b
, 0);
307 simulate(struct test_case
*tc
, int granularity
)
309 int time
, i
, round_trips
;
311 for (time
= 0; time
< 1000 * 180; time
+= granularity
) {
313 for (i
= 0; i
< tc
->n_bridges
; i
++) {
314 rstp_tick_timers(tc
->bridges
[i
]->rstp
);
316 for (round_trips
= 0; round_trips
< granularity
; round_trips
++) {
319 for (i
= 0; i
< tc
->n_bridges
; i
++) {
320 struct bridge
*b
= tc
->bridges
[i
];
322 for (; b
->rxq_tail
!= b
->rxq_head
; b
->rxq_tail
++) {
323 struct bpdu
*bpdu
= &b
->rxq
[b
->rxq_tail
% RXQ_SIZE
];
325 rstp_port_received_bpdu(rstp_get_port(b
->rstp
,
327 bpdu
->data
, bpdu
->size
);
339 OVS_NO_RETURN
static void
340 err(const char *message
, ...)
341 OVS_PRINTF_FORMAT(1, 2);
344 err(const char *message
, ...)
348 fprintf(stderr
, "%s:%d:%"PRIdPTR
": ", file_name
, line_number
, pos
- line
);
349 va_start(args
, message
);
350 vfprintf(stderr
, message
, args
);
358 warn(const char *message
, ...)
359 OVS_PRINTF_FORMAT(1, 2);
362 warn(const char *message
, ...)
366 fprintf(stderr
, "%s:%d: ", file_name
, line_number
);
367 va_start(args
, message
);
368 vfprintf(stderr
, message
, args
);
380 while (isspace((unsigned char) *pos
)) {
390 if (isalpha((unsigned char) *pos
)) {
391 while (isalpha((unsigned char) *++pos
)) {
394 } else if (isdigit((unsigned char) *pos
)) {
395 if (*pos
== '0' && (pos
[1] == 'x' || pos
[1] == 'X')) {
397 while (isxdigit((unsigned char) *pos
)) {
401 while (isdigit((unsigned char) *++pos
)) {
410 token
= xmemdup0(start
, pos
- start
);
417 char *save_pos
= pos
;
419 if (token
&& isdigit((unsigned char) *token
)) {
420 *intp
= strtol(token
, NULL
, 0);
430 match(const char *want
)
432 if (token
&& !strcmp(want
, token
)) {
446 err("expected integer");
452 must_match(const char *want
)
455 err("expected \"%s\"", want
);
460 test_rstp_main(int argc
, char *argv
[])
462 struct test_case
*tc
;
466 vlog_set_pattern(VLF_CONSOLE
, "%c|%p|%m");
467 vlog_set_levels(NULL
, VLF_SYSLOG
, VLL_OFF
);
470 ovs_fatal(0, "usage: test-rstp INPUT.RSTP");
474 input_file
= fopen(file_name
, "r");
476 ovs_fatal(errno
, "error opening \"%s\"", file_name
);
479 tc
= new_test_case();
480 for (i
= 0; i
< 26; i
++) {
488 for (line_number
= 1; fgets(line
, sizeof line
, input_file
);
491 char *newline
, *hash
;
493 newline
= strchr(line
, '\n');
497 hash
= strchr(line
, '#');
506 if (match("bridge")) {
507 struct bridge
*bridge
;
508 int bridge_no
, port_no
;
510 bridge_no
= must_get_int();
511 if (bridge_no
< tc
->n_bridges
) {
512 bridge
= tc
->bridges
[bridge_no
];
513 } else if (bridge_no
== tc
->n_bridges
) {
514 bridge
= new_bridge(tc
, must_get_int());
516 err("bridges must be numbered consecutively from 0");
519 rstp_set_bridge_priority(bridge
->rstp
, must_get_int());
522 for (port_no
= 1; port_no
< MAX_PORTS
; port_no
++) {
523 struct rstp_port
*p
= rstp_get_port(bridge
->rstp
, port_no
);
525 if (!token
|| match("X")) {
527 reinitialize_port(p
);
528 rstp_port_set_state(p
, RSTP_DISABLED
);
529 rstp_port_set_mac_operational(p
, false);
530 } else if (match("_")) {
536 if (!strcmp(token
, "0")) {
538 } else if (strlen(token
) == 1
539 && islower((unsigned char)*token
)) {
540 lan
= tc
->lans
[*token
- 'a'];
542 err("%s is not a valid LAN name "
543 "(0 or a lowercase letter)", token
);
547 path_cost
= match(":") ? must_get_int() :
548 RSTP_DEFAULT_PORT_PATH_COST
;
549 if (port_no
< bridge
->n_ports
) {
551 reinitialize_port(p
);
552 rstp_port_set_path_cost(p
, path_cost
);
553 rstp_port_set_state(p
, RSTP_DISCARDING
);
554 rstp_port_set_mac_operational(p
, true);
555 reconnect_port(bridge
, port_no
, lan
);
556 } else if (port_no
== bridge
->n_ports
) {
557 new_port(bridge
, lan
, path_cost
);
558 bridge
->n_active_ports
++;
560 err("ports must be numbered consecutively");
563 rstp_port_set_priority(p
, must_get_int());
568 } else if (match("run")) {
569 simulate(tc
, must_get_int());
570 } else if (match("dump")) {
572 } else if (match("tree")) {
574 } else if (match("check")) {
577 int bridge_no
, port_no
;
580 bridge_no
= must_get_int();
581 if (bridge_no
>= tc
->n_bridges
) {
582 err("no bridge numbered %d", bridge_no
);
584 b
= tc
->bridges
[bridge_no
];
589 if (match("rootid")) {
593 rootid
= must_get_int();
595 rootid
|= (uint64_t) must_get_int() << 48;
597 rootid
|= UINT64_C(0x8000) << 48;
599 if (rstp_get_designated_root(rstp
) != rootid
) {
600 warn("%s: root "RSTP_ID_FMT
", not %"PRIx64
,
602 RSTP_ID_ARGS(rstp_get_designated_root(rstp
)),
606 cost_value
= rstp_get_root_path_cost(rstp
);
608 if (cost_value
!= 0) {
609 warn("%s: root path cost of root is %d instead of 0 \n",
610 rstp_get_name(rstp
), cost_value
);
612 if (!rstp_is_root_bridge(rstp
)) {
613 warn("%s: root is "RSTP_ID_FMT
", not "RSTP_ID_FMT
"",
615 RSTP_ID_ARGS(rstp_get_designated_root(rstp
)),
616 RSTP_ID_ARGS(rstp_get_bridge_id(rstp
)));
618 for (port_no
= 1; port_no
< b
->n_active_ports
; port_no
++) {
619 struct rstp_port
*p
= rstp_get_port(rstp
, port_no
);
620 enum rstp_state state
= rstp_port_get_state(p
);
622 if (state
!= RSTP_DISABLED
&& state
!= RSTP_FORWARDING
) {
623 warn("%s: root port %d in state %s",
624 rstp_get_name(b
->rstp
), port_no
,
625 rstp_state_name(state
));
629 for (port_no
= 1; port_no
< b
->n_active_ports
; port_no
++) {
630 struct rstp_port
*p
= rstp_get_port(rstp
, port_no
);
631 enum rstp_state state
;
633 if (token
== NULL
|| match("D")) {
634 state
= RSTP_DISABLED
;
635 } else if (match("Di")) {
636 state
= RSTP_DISCARDING
;
637 } else if (match("Le")) {
638 state
= RSTP_LEARNING
;
639 } else if (match("F")) {
640 state
= RSTP_FORWARDING
;
641 } else if (match("_")) {
644 err("unknown port state %s", token
);
646 if (rstp_port_get_state(p
) != state
) {
647 warn("%s port %d: state is %s but should be %s",
648 rstp_get_name(rstp
), port_no
,
649 rstp_state_name(rstp_port_get_state(p
)),
650 rstp_state_name(state
));
652 if (state
== RSTP_FORWARDING
) {
653 struct rstp_port
*root_port
= rstp_get_root_port(rstp
);
656 int root_path_cost
= must_get_int();
658 if (p
!= root_port
) {
659 warn("%s: port %d is not the root port",
660 rstp_get_name(rstp
), port_no
);
662 warn("%s: (there is no root port)",
663 rstp_get_name(rstp
));
665 warn("%s: (port %d is the root port)",
667 rstp_port_get_number(root_port
));
669 } else if (cost_value
!= root_path_cost
) {
670 warn("%s: root path cost is %d, should be %d",
675 } else if (p
== root_port
) {
676 warn("%s: port %d is the root port but "
677 "not expected to be",
678 rstp_get_name(rstp
), port_no
);
684 printf("failing because of %d warnings\n", n_warnings
);
689 printf("failing because of errors\n");
690 err("trailing garbage on line");
696 for (i
= 0; i
< tc
->n_lans
; i
++) {
697 struct lan
*lan
= tc
->lans
[i
];
699 free(CONST_CAST(char *, lan
->name
));
702 for (i
= 0; i
< tc
->n_bridges
; i
++) {
703 struct bridge
*bridge
= tc
->bridges
[i
];
706 for (j
= 1; j
< MAX_PORTS
; j
++) {
707 rstp_port_unref(rstp_get_port(bridge
->rstp
, j
));
709 rstp_unref(bridge
->rstp
);
715 OVSTEST_REGISTER("test-rstp", test_rstp_main
);