]>
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.
26 #include "dp-packet.h"
30 #include "openvswitch/vlog.h"
45 struct lan
*ports
[STP_MAX_PORTS
];
49 struct bpdu rxq
[RXQ_SIZE
];
50 int rxq_head
, rxq_tail
;
54 struct bridge
*bridge
;
62 struct lan_conn conns
[16];
67 struct bridge
*bridges
[16];
73 static const char *file_name
;
74 static int line_number
;
75 static char line
[128];
76 static char *pos
, *token
;
77 static int n_warnings
;
79 static struct test_case
*
82 struct test_case
*tc
= xmalloc(sizeof *tc
);
89 send_bpdu(struct dp_packet
*pkt
, int port_no
, void *b_
)
91 struct bridge
*b
= b_
;
94 assert(port_no
< b
->n_ports
);
95 lan
= b
->ports
[port_no
];
97 const void *data
= dp_packet_l3(pkt
);
98 size_t size
= (char *) dp_packet_tail(pkt
) - (char *) data
;
101 for (i
= 0; i
< lan
->n_conns
; i
++) {
102 struct lan_conn
*conn
= &lan
->conns
[i
];
103 if (conn
->bridge
!= b
|| conn
->port_no
!= port_no
) {
104 struct bridge
*dst
= conn
->bridge
;
105 struct bpdu
*bpdu
= &dst
->rxq
[dst
->rxq_head
++ % RXQ_SIZE
];
106 assert(dst
->rxq_head
- dst
->rxq_tail
<= RXQ_SIZE
);
107 bpdu
->data
= xmemdup(data
, size
);
109 bpdu
->port_no
= conn
->port_no
;
113 dp_packet_delete(pkt
);
116 static struct bridge
*
117 new_bridge(struct test_case
*tc
, int id
)
119 struct bridge
*b
= xmalloc(sizeof *b
);
123 snprintf(name
, sizeof name
, "stp%x", id
);
124 b
->stp
= stp_create(name
, id
, send_bpdu
, b
);
125 assert(tc
->n_bridges
< ARRAY_SIZE(tc
->bridges
));
127 b
->rxq_head
= b
->rxq_tail
= 0;
128 tc
->bridges
[tc
->n_bridges
++] = b
;
133 new_lan(struct test_case
*tc
, const char *name
)
135 struct lan
*lan
= xmalloc(sizeof *lan
);
137 lan
->name
= xstrdup(name
);
139 assert(tc
->n_lans
< ARRAY_SIZE(tc
->lans
));
140 tc
->lans
[tc
->n_lans
++] = lan
;
145 reconnect_port(struct bridge
*b
, int port_no
, struct lan
*new_lan
)
150 assert(port_no
< b
->n_ports
);
151 old_lan
= b
->ports
[port_no
];
152 if (old_lan
== new_lan
) {
156 /* Disconnect from old_lan. */
158 for (j
= 0; j
< old_lan
->n_conns
; j
++) {
159 struct lan_conn
*c
= &old_lan
->conns
[j
];
160 if (c
->bridge
== b
&& c
->port_no
== port_no
) {
161 memmove(c
, c
+ 1, sizeof *c
* (old_lan
->n_conns
- j
- 1));
168 /* Connect to new_lan. */
169 b
->ports
[port_no
] = new_lan
;
171 int conn_no
= new_lan
->n_conns
++;
172 assert(conn_no
< ARRAY_SIZE(new_lan
->conns
));
173 new_lan
->conns
[conn_no
].bridge
= b
;
174 new_lan
->conns
[conn_no
].port_no
= port_no
;
179 new_port(struct bridge
*b
, struct lan
*lan
, int path_cost
)
181 int port_no
= b
->n_ports
++;
182 struct stp_port
*p
= stp_get_port(b
->stp
, port_no
);
183 assert(port_no
< ARRAY_SIZE(b
->ports
));
184 b
->ports
[port_no
] = NULL
;
185 stp_port_set_path_cost(p
, path_cost
);
187 reconnect_port(b
, port_no
, lan
);
191 dump(struct test_case
*tc
)
195 for (i
= 0; i
< tc
->n_bridges
; i
++) {
196 struct bridge
*b
= tc
->bridges
[i
];
197 struct stp
*stp
= b
->stp
;
200 printf("%s:", stp_get_name(stp
));
201 if (stp_is_root_bridge(stp
)) {
205 for (j
= 0; j
< b
->n_ports
; j
++) {
206 struct stp_port
*p
= stp_get_port(stp
, j
);
207 enum stp_state state
= stp_port_get_state(p
);
209 printf("\tport %d", j
);
211 printf(" (lan %s)", b
->ports
[j
]->name
);
213 printf(" (disconnected)");
215 printf(": %s", stp_state_name(state
));
216 if (p
== stp_get_root_port(stp
)) {
217 printf(" (root port, root_path_cost=%u)", stp_get_root_path_cost(stp
));
224 static void dump_lan_tree(struct test_case
*, struct lan
*, int level
);
227 dump_bridge_tree(struct test_case
*tc
, struct bridge
*b
, int level
)
235 for (i
= 0; i
< level
; i
++) {
238 printf("%s\n", stp_get_name(b
->stp
));
239 for (i
= 0; i
< b
->n_ports
; i
++) {
240 struct lan
*lan
= b
->ports
[i
];
241 struct stp_port
*p
= stp_get_port(b
->stp
, i
);
242 if (stp_port_get_state(p
) == STP_FORWARDING
&& lan
) {
243 dump_lan_tree(tc
, lan
, level
+ 1);
249 dump_lan_tree(struct test_case
*tc
, struct lan
*lan
, int level
)
257 for (i
= 0; i
< level
; i
++) {
260 printf("%s\n", lan
->name
);
261 for (i
= 0; i
< lan
->n_conns
; i
++) {
262 struct bridge
*b
= lan
->conns
[i
].bridge
;
263 dump_bridge_tree(tc
, b
, level
+ 1);
268 tree(struct test_case
*tc
)
272 for (i
= 0; i
< tc
->n_bridges
; i
++) {
273 struct bridge
*b
= tc
->bridges
[i
];
276 for (i
= 0; i
< tc
->n_lans
; i
++) {
277 struct lan
*lan
= tc
->lans
[i
];
278 lan
->reached
= false;
280 for (i
= 0; i
< tc
->n_bridges
; i
++) {
281 struct bridge
*b
= tc
->bridges
[i
];
282 struct stp
*stp
= b
->stp
;
283 if (stp_is_root_bridge(stp
)) {
284 dump_bridge_tree(tc
, b
, 0);
290 simulate(struct test_case
*tc
, int granularity
)
294 for (time
= 0; time
< 1000 * 180; time
+= granularity
) {
298 for (i
= 0; i
< tc
->n_bridges
; i
++) {
299 stp_tick(tc
->bridges
[i
]->stp
, granularity
);
301 for (round_trips
= 0; round_trips
< granularity
; round_trips
++) {
303 for (i
= 0; i
< tc
->n_bridges
; i
++) {
304 struct bridge
*b
= tc
->bridges
[i
];
305 for (; b
->rxq_tail
!= b
->rxq_head
; b
->rxq_tail
++) {
306 struct bpdu
*bpdu
= &b
->rxq
[b
->rxq_tail
% RXQ_SIZE
];
307 stp_received_bpdu(stp_get_port(b
->stp
, bpdu
->port_no
),
308 bpdu
->data
, bpdu
->size
);
320 OVS_NO_RETURN
static void
321 err(const char *message
, ...)
322 OVS_PRINTF_FORMAT(1, 2);
325 err(const char *message
, ...)
329 fprintf(stderr
, "%s:%d:%"PRIdPTR
": ", file_name
, line_number
, pos
- line
);
330 va_start(args
, message
);
331 vfprintf(stderr
, message
, args
);
339 warn(const char *message
, ...)
340 OVS_PRINTF_FORMAT(1, 2);
343 warn(const char *message
, ...)
347 fprintf(stderr
, "%s:%d: ", file_name
, line_number
);
348 va_start(args
, message
);
349 vfprintf(stderr
, message
, args
);
361 while (isspace((unsigned char) *pos
)) {
371 if (isalpha((unsigned char) *pos
)) {
372 while (isalpha((unsigned char) *++pos
)) {
375 } else if (isdigit((unsigned char) *pos
)) {
376 if (*pos
== '0' && (pos
[1] == 'x' || pos
[1] == 'X')) {
378 while (isxdigit((unsigned char) *pos
)) {
382 while (isdigit((unsigned char) *++pos
)) {
391 token
= xmemdup0(start
, pos
- start
);
398 char *save_pos
= pos
;
399 if (token
&& isdigit((unsigned char) *token
)) {
400 *intp
= strtol(token
, NULL
, 0);
410 match(const char *want
)
412 if (token
&& !strcmp(want
, token
)) {
425 err("expected integer");
431 must_match(const char *want
)
434 err("expected \"%s\"", want
);
439 test_stp_main(int argc
, char *argv
[])
441 struct test_case
*tc
;
445 vlog_set_pattern(VLF_CONSOLE
, "%c|%p|%m");
446 vlog_set_levels(NULL
, VLF_SYSLOG
, VLL_OFF
);
449 ovs_fatal(0, "usage: test-stp INPUT.STP\n");
453 input_file
= fopen(file_name
, "r");
455 ovs_fatal(errno
, "error opening \"%s\"", file_name
);
458 tc
= new_test_case();
459 for (i
= 0; i
< 26; i
++) {
466 for (line_number
= 1; fgets(line
, sizeof line
, input_file
);
469 char *newline
, *hash
;
471 newline
= strchr(line
, '\n');
475 hash
= strchr(line
, '#');
484 if (match("bridge")) {
485 struct bridge
*bridge
;
486 int bridge_no
, port_no
;
488 bridge_no
= must_get_int();
489 if (bridge_no
< tc
->n_bridges
) {
490 bridge
= tc
->bridges
[bridge_no
];
491 } else if (bridge_no
== tc
->n_bridges
) {
492 bridge
= new_bridge(tc
, must_get_int());
494 err("bridges must be numbered consecutively from 0");
497 stp_set_bridge_priority(bridge
->stp
, must_get_int());
501 for (port_no
= 0; port_no
< STP_MAX_PORTS
; port_no
++) {
502 struct stp_port
*p
= stp_get_port(bridge
->stp
, port_no
);
503 if (!token
|| match("X")) {
505 } else if (match("_")) {
511 if (!strcmp(token
, "0")) {
513 } else if (strlen(token
) == 1
514 && islower((unsigned char)*token
)) {
515 lan
= tc
->lans
[*token
- 'a'];
517 err("%s is not a valid LAN name "
518 "(0 or a lowercase letter)", token
);
522 path_cost
= match(":") ? must_get_int() : 10;
523 if (port_no
< bridge
->n_ports
) {
524 stp_port_set_path_cost(p
, path_cost
);
526 reconnect_port(bridge
, port_no
, lan
);
527 } else if (port_no
== bridge
->n_ports
) {
528 new_port(bridge
, lan
, path_cost
);
530 err("ports must be numbered consecutively");
533 stp_port_set_priority(p
, must_get_int());
538 } else if (match("run")) {
539 simulate(tc
, must_get_int());
540 } else if (match("dump")) {
542 } else if (match("tree")) {
544 } else if (match("check")) {
547 int bridge_no
, port_no
;
549 bridge_no
= must_get_int();
550 if (bridge_no
>= tc
->n_bridges
) {
551 err("no bridge numbered %d", bridge_no
);
553 b
= tc
->bridges
[bridge_no
];
558 if (match("rootid")) {
561 rootid
= must_get_int();
563 rootid
|= (uint64_t) must_get_int() << 48;
565 rootid
|= UINT64_C(0x8000) << 48;
567 if (stp_get_designated_root(stp
) != rootid
) {
568 warn("%s: root %"PRIx64
", not %"PRIx64
,
569 stp_get_name(stp
), stp_get_designated_root(stp
),
575 if (stp_get_root_path_cost(stp
)) {
576 warn("%s: root path cost of root is %u but should be 0",
577 stp_get_name(stp
), stp_get_root_path_cost(stp
));
579 if (!stp_is_root_bridge(stp
)) {
580 warn("%s: root is %"PRIx64
", not %"PRIx64
,
582 stp_get_designated_root(stp
), stp_get_bridge_id(stp
));
584 for (port_no
= 0; port_no
< b
->n_ports
; port_no
++) {
585 struct stp_port
*p
= stp_get_port(stp
, port_no
);
586 enum stp_state state
= stp_port_get_state(p
);
587 if (!(state
& (STP_DISABLED
| STP_FORWARDING
))) {
588 warn("%s: root port %d in state %s",
589 stp_get_name(b
->stp
), port_no
,
590 stp_state_name(state
));
594 for (port_no
= 0; port_no
< STP_MAX_PORTS
; port_no
++) {
595 struct stp_port
*p
= stp_get_port(stp
, port_no
);
596 enum stp_state state
;
597 if (token
== NULL
|| match("D")) {
598 state
= STP_DISABLED
;
599 } else if (match("B")) {
600 state
= STP_BLOCKING
;
601 } else if (match("Li")) {
602 state
= STP_LISTENING
;
603 } else if (match("Le")) {
604 state
= STP_LEARNING
;
605 } else if (match("F")) {
606 state
= STP_FORWARDING
;
607 } else if (match("_")) {
610 err("unknown port state %s", token
);
612 if (stp_port_get_state(p
) != state
) {
613 warn("%s port %d: state is %s but should be %s",
614 stp_get_name(stp
), port_no
,
615 stp_state_name(stp_port_get_state(p
)),
616 stp_state_name(state
));
618 if (state
== STP_FORWARDING
) {
619 struct stp_port
*root_port
= stp_get_root_port(stp
);
621 int root_path_cost
= must_get_int();
622 if (p
!= root_port
) {
623 warn("%s: port %d is not the root port",
624 stp_get_name(stp
), port_no
);
626 warn("%s: (there is no root port)",
629 warn("%s: (port %d is the root port)",
631 stp_port_no(root_port
));
633 } else if (root_path_cost
634 != stp_get_root_path_cost(stp
)) {
635 warn("%s: root path cost is %u, should be %d",
637 stp_get_root_path_cost(stp
),
640 } else if (p
== root_port
) {
641 warn("%s: port %d is the root port but "
642 "not expected to be",
643 stp_get_name(stp
), port_no
);
653 err("trailing garbage on line");
658 for (i
= 0; i
< tc
->n_lans
; i
++) {
659 struct lan
*lan
= tc
->lans
[i
];
660 free(CONST_CAST(char *, lan
->name
));
663 for (i
= 0; i
< tc
->n_bridges
; i
++) {
664 struct bridge
*bridge
= tc
->bridges
[i
];
665 stp_unref(bridge
->stp
);
672 OVSTEST_REGISTER("test-stp", test_stp_main
);