]>
git.proxmox.com Git - mirror_ovs.git/blob - tests/test-stp.c
2 * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2014 Nicira, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
29 #include "openvswitch/vlog.h"
44 struct lan
*ports
[STP_MAX_PORTS
];
48 struct bpdu rxq
[RXQ_SIZE
];
49 int rxq_head
, rxq_tail
;
53 struct bridge
*bridge
;
61 struct lan_conn conns
[16];
66 struct bridge
*bridges
[16];
72 static const char *file_name
;
73 static int line_number
;
74 static char line
[128];
75 static char *pos
, *token
;
76 static int n_warnings
;
78 static struct test_case
*
81 struct test_case
*tc
= xmalloc(sizeof *tc
);
88 send_bpdu(struct ofpbuf
*pkt
, int port_no
, void *b_
)
90 struct bridge
*b
= b_
;
93 assert(port_no
< b
->n_ports
);
94 lan
= b
->ports
[port_no
];
96 const void *data
= ofpbuf_l3(pkt
);
97 size_t size
= (char *) ofpbuf_tail(pkt
) - (char *) data
;
100 for (i
= 0; i
< lan
->n_conns
; i
++) {
101 struct lan_conn
*conn
= &lan
->conns
[i
];
102 if (conn
->bridge
!= b
|| conn
->port_no
!= port_no
) {
103 struct bridge
*dst
= conn
->bridge
;
104 struct bpdu
*bpdu
= &dst
->rxq
[dst
->rxq_head
++ % RXQ_SIZE
];
105 assert(dst
->rxq_head
- dst
->rxq_tail
<= RXQ_SIZE
);
106 bpdu
->data
= xmemdup(data
, size
);
108 bpdu
->port_no
= conn
->port_no
;
115 static struct bridge
*
116 new_bridge(struct test_case
*tc
, int id
)
118 struct bridge
*b
= xmalloc(sizeof *b
);
122 snprintf(name
, sizeof name
, "stp%x", id
);
123 b
->stp
= stp_create(name
, id
, send_bpdu
, b
);
124 assert(tc
->n_bridges
< ARRAY_SIZE(tc
->bridges
));
126 b
->rxq_head
= b
->rxq_tail
= 0;
127 tc
->bridges
[tc
->n_bridges
++] = b
;
132 new_lan(struct test_case
*tc
, const char *name
)
134 struct lan
*lan
= xmalloc(sizeof *lan
);
136 lan
->name
= xstrdup(name
);
138 assert(tc
->n_lans
< ARRAY_SIZE(tc
->lans
));
139 tc
->lans
[tc
->n_lans
++] = lan
;
144 reconnect_port(struct bridge
*b
, int port_no
, struct lan
*new_lan
)
149 assert(port_no
< b
->n_ports
);
150 old_lan
= b
->ports
[port_no
];
151 if (old_lan
== new_lan
) {
155 /* Disconnect from old_lan. */
157 for (j
= 0; j
< old_lan
->n_conns
; j
++) {
158 struct lan_conn
*c
= &old_lan
->conns
[j
];
159 if (c
->bridge
== b
&& c
->port_no
== port_no
) {
160 memmove(c
, c
+ 1, sizeof *c
* (old_lan
->n_conns
- j
- 1));
167 /* Connect to new_lan. */
168 b
->ports
[port_no
] = new_lan
;
170 int conn_no
= new_lan
->n_conns
++;
171 assert(conn_no
< ARRAY_SIZE(new_lan
->conns
));
172 new_lan
->conns
[conn_no
].bridge
= b
;
173 new_lan
->conns
[conn_no
].port_no
= port_no
;
178 new_port(struct bridge
*b
, struct lan
*lan
, int path_cost
)
180 int port_no
= b
->n_ports
++;
181 struct stp_port
*p
= stp_get_port(b
->stp
, port_no
);
182 assert(port_no
< ARRAY_SIZE(b
->ports
));
183 b
->ports
[port_no
] = NULL
;
184 stp_port_set_path_cost(p
, path_cost
);
186 reconnect_port(b
, port_no
, lan
);
190 dump(struct test_case
*tc
)
194 for (i
= 0; i
< tc
->n_bridges
; i
++) {
195 struct bridge
*b
= tc
->bridges
[i
];
196 struct stp
*stp
= b
->stp
;
199 printf("%s:", stp_get_name(stp
));
200 if (stp_is_root_bridge(stp
)) {
204 for (j
= 0; j
< b
->n_ports
; j
++) {
205 struct stp_port
*p
= stp_get_port(stp
, j
);
206 enum stp_state state
= stp_port_get_state(p
);
208 printf("\tport %d", j
);
210 printf(" (lan %s)", b
->ports
[j
]->name
);
212 printf(" (disconnected)");
214 printf(": %s", stp_state_name(state
));
215 if (p
== stp_get_root_port(stp
)) {
216 printf(" (root port, root_path_cost=%u)", stp_get_root_path_cost(stp
));
223 static void dump_lan_tree(struct test_case
*, struct lan
*, int level
);
226 dump_bridge_tree(struct test_case
*tc
, struct bridge
*b
, int level
)
234 for (i
= 0; i
< level
; i
++) {
237 printf("%s\n", stp_get_name(b
->stp
));
238 for (i
= 0; i
< b
->n_ports
; i
++) {
239 struct lan
*lan
= b
->ports
[i
];
240 struct stp_port
*p
= stp_get_port(b
->stp
, i
);
241 if (stp_port_get_state(p
) == STP_FORWARDING
&& lan
) {
242 dump_lan_tree(tc
, lan
, level
+ 1);
248 dump_lan_tree(struct test_case
*tc
, struct lan
*lan
, int level
)
256 for (i
= 0; i
< level
; i
++) {
259 printf("%s\n", lan
->name
);
260 for (i
= 0; i
< lan
->n_conns
; i
++) {
261 struct bridge
*b
= lan
->conns
[i
].bridge
;
262 dump_bridge_tree(tc
, b
, level
+ 1);
267 tree(struct test_case
*tc
)
271 for (i
= 0; i
< tc
->n_bridges
; i
++) {
272 struct bridge
*b
= tc
->bridges
[i
];
275 for (i
= 0; i
< tc
->n_lans
; i
++) {
276 struct lan
*lan
= tc
->lans
[i
];
277 lan
->reached
= false;
279 for (i
= 0; i
< tc
->n_bridges
; i
++) {
280 struct bridge
*b
= tc
->bridges
[i
];
281 struct stp
*stp
= b
->stp
;
282 if (stp_is_root_bridge(stp
)) {
283 dump_bridge_tree(tc
, b
, 0);
289 simulate(struct test_case
*tc
, int granularity
)
293 for (time
= 0; time
< 1000 * 180; time
+= granularity
) {
297 for (i
= 0; i
< tc
->n_bridges
; i
++) {
298 stp_tick(tc
->bridges
[i
]->stp
, granularity
);
300 for (round_trips
= 0; round_trips
< granularity
; round_trips
++) {
302 for (i
= 0; i
< tc
->n_bridges
; i
++) {
303 struct bridge
*b
= tc
->bridges
[i
];
304 for (; b
->rxq_tail
!= b
->rxq_head
; b
->rxq_tail
++) {
305 struct bpdu
*bpdu
= &b
->rxq
[b
->rxq_tail
% RXQ_SIZE
];
306 stp_received_bpdu(stp_get_port(b
->stp
, bpdu
->port_no
),
307 bpdu
->data
, bpdu
->size
);
319 OVS_NO_RETURN
static void
320 err(const char *message
, ...)
321 OVS_PRINTF_FORMAT(1, 2);
324 err(const char *message
, ...)
328 fprintf(stderr
, "%s:%d:%"PRIdPTR
": ", file_name
, line_number
, pos
- line
);
329 va_start(args
, message
);
330 vfprintf(stderr
, message
, args
);
338 warn(const char *message
, ...)
339 OVS_PRINTF_FORMAT(1, 2);
342 warn(const char *message
, ...)
346 fprintf(stderr
, "%s:%d: ", file_name
, line_number
);
347 va_start(args
, message
);
348 vfprintf(stderr
, message
, args
);
360 while (isspace((unsigned char) *pos
)) {
370 if (isalpha((unsigned char) *pos
)) {
371 while (isalpha((unsigned char) *++pos
)) {
374 } else if (isdigit((unsigned char) *pos
)) {
375 if (*pos
== '0' && (pos
[1] == 'x' || pos
[1] == 'X')) {
377 while (isxdigit((unsigned char) *pos
)) {
381 while (isdigit((unsigned char) *++pos
)) {
390 token
= xmemdup0(start
, pos
- start
);
397 char *save_pos
= pos
;
398 if (token
&& isdigit((unsigned char) *token
)) {
399 *intp
= strtol(token
, NULL
, 0);
409 match(const char *want
)
411 if (token
&& !strcmp(want
, token
)) {
424 err("expected integer");
430 must_match(const char *want
)
433 err("expected \"%s\"", want
);
438 test_stp_main(int argc
, char *argv
[])
440 struct test_case
*tc
;
444 vlog_set_pattern(VLF_CONSOLE
, "%c|%p|%m");
445 vlog_set_levels(NULL
, VLF_SYSLOG
, VLL_OFF
);
448 ovs_fatal(0, "usage: test-stp INPUT.STP\n");
452 input_file
= fopen(file_name
, "r");
454 ovs_fatal(errno
, "error opening \"%s\"", file_name
);
457 tc
= new_test_case();
458 for (i
= 0; i
< 26; i
++) {
465 for (line_number
= 1; fgets(line
, sizeof line
, input_file
);
468 char *newline
, *hash
;
470 newline
= strchr(line
, '\n');
474 hash
= strchr(line
, '#');
483 if (match("bridge")) {
484 struct bridge
*bridge
;
485 int bridge_no
, port_no
;
487 bridge_no
= must_get_int();
488 if (bridge_no
< tc
->n_bridges
) {
489 bridge
= tc
->bridges
[bridge_no
];
490 } else if (bridge_no
== tc
->n_bridges
) {
491 bridge
= new_bridge(tc
, must_get_int());
493 err("bridges must be numbered consecutively from 0");
496 stp_set_bridge_priority(bridge
->stp
, must_get_int());
500 for (port_no
= 0; port_no
< STP_MAX_PORTS
; port_no
++) {
501 struct stp_port
*p
= stp_get_port(bridge
->stp
, port_no
);
502 if (!token
|| match("X")) {
504 } else if (match("_")) {
510 if (!strcmp(token
, "0")) {
512 } else if (strlen(token
) == 1
513 && islower((unsigned char)*token
)) {
514 lan
= tc
->lans
[*token
- 'a'];
516 err("%s is not a valid LAN name "
517 "(0 or a lowercase letter)", token
);
521 path_cost
= match(":") ? must_get_int() : 10;
522 if (port_no
< bridge
->n_ports
) {
523 stp_port_set_path_cost(p
, path_cost
);
525 reconnect_port(bridge
, port_no
, lan
);
526 } else if (port_no
== bridge
->n_ports
) {
527 new_port(bridge
, lan
, path_cost
);
529 err("ports must be numbered consecutively");
532 stp_port_set_priority(p
, must_get_int());
537 } else if (match("run")) {
538 simulate(tc
, must_get_int());
539 } else if (match("dump")) {
541 } else if (match("tree")) {
543 } else if (match("check")) {
546 int bridge_no
, port_no
;
548 bridge_no
= must_get_int();
549 if (bridge_no
>= tc
->n_bridges
) {
550 err("no bridge numbered %d", bridge_no
);
552 b
= tc
->bridges
[bridge_no
];
557 if (match("rootid")) {
560 rootid
= must_get_int();
562 rootid
|= (uint64_t) must_get_int() << 48;
564 rootid
|= UINT64_C(0x8000) << 48;
566 if (stp_get_designated_root(stp
) != rootid
) {
567 warn("%s: root %"PRIx64
", not %"PRIx64
,
568 stp_get_name(stp
), stp_get_designated_root(stp
),
574 if (stp_get_root_path_cost(stp
)) {
575 warn("%s: root path cost of root is %u but should be 0",
576 stp_get_name(stp
), stp_get_root_path_cost(stp
));
578 if (!stp_is_root_bridge(stp
)) {
579 warn("%s: root is %"PRIx64
", not %"PRIx64
,
581 stp_get_designated_root(stp
), stp_get_bridge_id(stp
));
583 for (port_no
= 0; port_no
< b
->n_ports
; port_no
++) {
584 struct stp_port
*p
= stp_get_port(stp
, port_no
);
585 enum stp_state state
= stp_port_get_state(p
);
586 if (!(state
& (STP_DISABLED
| STP_FORWARDING
))) {
587 warn("%s: root port %d in state %s",
588 stp_get_name(b
->stp
), port_no
,
589 stp_state_name(state
));
593 for (port_no
= 0; port_no
< STP_MAX_PORTS
; port_no
++) {
594 struct stp_port
*p
= stp_get_port(stp
, port_no
);
595 enum stp_state state
;
596 if (token
== NULL
|| match("D")) {
597 state
= STP_DISABLED
;
598 } else if (match("B")) {
599 state
= STP_BLOCKING
;
600 } else if (match("Li")) {
601 state
= STP_LISTENING
;
602 } else if (match("Le")) {
603 state
= STP_LEARNING
;
604 } else if (match("F")) {
605 state
= STP_FORWARDING
;
606 } else if (match("_")) {
609 err("unknown port state %s", token
);
611 if (stp_port_get_state(p
) != state
) {
612 warn("%s port %d: state is %s but should be %s",
613 stp_get_name(stp
), port_no
,
614 stp_state_name(stp_port_get_state(p
)),
615 stp_state_name(state
));
617 if (state
== STP_FORWARDING
) {
618 struct stp_port
*root_port
= stp_get_root_port(stp
);
620 int root_path_cost
= must_get_int();
621 if (p
!= root_port
) {
622 warn("%s: port %d is not the root port",
623 stp_get_name(stp
), port_no
);
625 warn("%s: (there is no root port)",
628 warn("%s: (port %d is the root port)",
630 stp_port_no(root_port
));
632 } else if (root_path_cost
633 != stp_get_root_path_cost(stp
)) {
634 warn("%s: root path cost is %u, should be %d",
636 stp_get_root_path_cost(stp
),
639 } else if (p
== root_port
) {
640 warn("%s: port %d is the root port but "
641 "not expected to be",
642 stp_get_name(stp
), port_no
);
652 err("trailing garbage on line");
657 for (i
= 0; i
< tc
->n_lans
; i
++) {
658 struct lan
*lan
= tc
->lans
[i
];
659 free(CONST_CAST(char *, lan
->name
));
662 for (i
= 0; i
< tc
->n_bridges
; i
++) {
663 struct bridge
*bridge
= tc
->bridges
[i
];
664 stp_unref(bridge
->stp
);
671 OVSTEST_REGISTER("test-stp", test_stp_main
);