X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fstp.c;h=3d293b6c6137c5fd2bf3817c9d8eba41e991a1ff;hb=81a76618be9ea195a1e4a881ba9591728891d10b;hp=62c2ea8acaf3c5203e8cb664ec2717459c09a1b7;hpb=80740385d2700b1a03d28a02338d02509fd0b697;p=sliver-openvswitch.git diff --git a/lib/stp.c b/lib/stp.c index 62c2ea8ac..3d293b6c6 100644 --- a/lib/stp.c +++ b/lib/stp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ #include "byte-order.h" #include "ofpbuf.h" #include "packets.h" +#include "unixctl.h" #include "util.h" #include "vlog.h" @@ -101,6 +102,8 @@ struct stp_port { }; struct stp { + struct list node; /* Node in all_stps list. */ + /* Static bridge data. */ char *name; /* Human-readable name for log messages. */ stp_identifier bridge_id; /* 8.5.3.7: This bridge. */ @@ -131,11 +134,14 @@ struct stp { struct stp_port ports[STP_MAX_PORTS]; /* Interface to client. */ + bool fdb_needs_flush; /* MAC learning tables needs flushing. */ struct stp_port *first_changed_port; void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux); void *aux; }; +static struct list all_stps = LIST_INITIALIZER(&all_stps); + #define FOR_EACH_ENABLED_PORT(PORT, STP) \ for ((PORT) = stp_next_enabled_port((STP), (STP)->ports); \ (PORT); \ @@ -145,7 +151,7 @@ stp_next_enabled_port(const struct stp *stp, const struct stp_port *port) { for (; port < &stp->ports[ARRAY_SIZE(stp->ports)]; port++) { if (port->state != STP_DISABLED) { - return (struct stp_port *) port; + return CONST_CAST(struct stp_port *, port); } } return NULL; @@ -192,13 +198,21 @@ static void stp_update_bridge_timers(struct stp *); static int clamp(int x, int min, int max); static int ms_to_timer(int ms); -static int ms_to_timer_remainder(int ms); static int timer_to_ms(int timer); static void stp_start_timer(struct stp_timer *, int value); static void stp_stop_timer(struct stp_timer *); static bool stp_timer_expired(struct stp_timer *, int elapsed, int timeout); static void stp_send_bpdu(struct stp_port *, const void *, size_t); +static void stp_unixctl_tcn(struct unixctl_conn *, int argc, + const char *argv[], void *aux); + +void +stp_init(void) +{ + unixctl_command_register("stp/tcn", "[bridge]", 0, 1, stp_unixctl_tcn, + NULL); +} /* Creates and returns a new STP instance that initially has no ports enabled. * @@ -256,6 +270,7 @@ stp_create(const char *name, stp_identifier bridge_id, p->path_cost = 19; /* Recommended default for 100 Mb/s link. */ stp_initialize_port(p, STP_DISABLED); } + list_push_back(&all_stps, &stp->node); return stp; } @@ -264,6 +279,7 @@ void stp_destroy(struct stp *stp) { if (stp) { + list_remove(&stp->node); free(stp->name); free(stp); } @@ -281,7 +297,7 @@ stp_tick(struct stp *stp, int ms) * are called too frequently. */ ms = clamp(ms, 0, INT_MAX - 1000) + stp->elapsed_remainder; elapsed = ms_to_timer(ms); - stp->elapsed_remainder = ms_to_timer_remainder(ms); + stp->elapsed_remainder = ms - timer_to_ms(elapsed); if (!elapsed) { return; } @@ -449,6 +465,17 @@ stp_get_forward_delay(const struct stp *stp) return timer_to_ms(stp->bridge_forward_delay); } +/* Returns true if something has happened to 'stp' which necessitates flushing + * the client's MAC learning table. Calling this function resets 'stp' so that + * future calls will return false until flushing is required again. */ +bool +stp_check_and_reset_fdb_flush(struct stp *stp) +{ + bool needs_flush = stp->fdb_needs_flush; + stp->fdb_needs_flush = false; + return needs_flush; +} + /* Returns the port in 'stp' with index 'port_no', which must be between 0 and * STP_MAX_PORTS. */ struct stp_port * @@ -1041,6 +1068,8 @@ stp_set_port_state(struct stp_port *p, enum stp_state state) static void stp_topology_change_detection(struct stp *stp) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + if (stp_is_root_bridge(stp)) { stp->topology_change = true; stp_start_timer(&stp->topology_change_timer, 0); @@ -1048,7 +1077,9 @@ stp_topology_change_detection(struct stp *stp) stp_transmit_tcn(stp); stp_start_timer(&stp->tcn_timer, 0); } + stp->fdb_needs_flush = true; stp->topology_change_detected = true; + VLOG_INFO_RL(&rl, "%s: detected topology change.", stp->name); } static void @@ -1096,6 +1127,9 @@ stp_received_config_bpdu(struct stp *stp, struct stp_port *p, if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE_ACK) { stp_topology_change_acknowledged(stp); } + if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE) { + stp->fdb_needs_flush = true; + } } } else if (stp_is_designated_port(p)) { stp_transmit_config(p); @@ -1253,14 +1287,6 @@ ms_to_timer(int ms) return ms * 0x100 / 1000; } -/* Returns the number of leftover milliseconds when 'ms' is converted to STP - * timer ticks. */ -static int -ms_to_timer_remainder(int ms) -{ - return ms * 0x100 % 1000; -} - /* Returns the number of whole milliseconds in 'timer' STP timer ticks. There * are 256 STP timer ticks per second. */ static int @@ -1321,3 +1347,41 @@ stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size) p->stp->send_bpdu(pkt, stp_port_no(p), p->stp->aux); p->tx_count++; } + +/* Unixctl. */ + +static struct stp * +stp_find(const char *name) +{ + struct stp *stp; + + LIST_FOR_EACH (stp, node, &all_stps) { + if (!strcmp(stp->name, name)) { + return stp; + } + } + return NULL; +} + +static void +stp_unixctl_tcn(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) +{ + if (argc > 1) { + struct stp *stp = stp_find(argv[1]); + + if (!stp) { + unixctl_command_reply_error(conn, "no such stp object"); + return; + } + stp_topology_change_detection(stp); + } else { + struct stp *stp; + + LIST_FOR_EACH (stp, node, &all_stps) { + stp_topology_change_detection(stp); + } + } + + unixctl_command_reply(conn, "OK"); +}