struct stp_port {
struct stp *stp;
+ void *aux; /* Auxiliary data the user may retrieve. */
int port_id; /* 8.5.5.1: Unique port identifier. */
enum stp_state state; /* 8.5.5.2: Current state. */
int path_cost; /* 8.5.5.3: Cost of tx/rx on this port. */
stp->bridge_id |= (uint64_t) STP_DEFAULT_BRIDGE_PRIORITY << 48;
}
- stp->rq_max_age = 6000;
- stp->rq_hello_time = 2000;
- stp->rq_forward_delay = 4000;
+ stp->rq_max_age = STP_DEFAULT_MAX_AGE;
+ stp->rq_hello_time = STP_DEFAULT_HELLO_TIME;
+ stp->rq_forward_delay = STP_DEFAULT_FWD_DELAY;
stp_update_bridge_timers(stp);
stp->max_age = stp->bridge_max_age;
stp->hello_time = stp->bridge_hello_time;
return (state & (STP_DISABLED | STP_LEARNING | STP_FORWARDING)) != 0;
}
+/* Returns the name for the given 'role' (for use in debugging and log
+ * messages). */
+const char *
+stp_role_name(enum stp_role role)
+{
+ switch (role) {
+ case STP_ROLE_ROOT:
+ return "root";
+ case STP_ROLE_DESIGNATED:
+ return "designated";
+ case STP_ROLE_ALTERNATE:
+ return "alternate";
+ case STP_ROLE_DISABLED:
+ return "disabled";
+ default:
+ NOT_REACHED();
+ }
+}
+
/* Notifies the STP entity that bridge protocol data unit 'bpdu', which is
* 'bpdu_size' bytes in length, was received on port 'p'.
*
return p->stp;
}
+/* Sets the 'aux' member of 'p'.
+ *
+ * The 'aux' member will be reset to NULL when stp_port_disable() is
+ * called or stp_port_enable() is called when the port is in a Disabled
+ * state. */
+void
+stp_port_set_aux(struct stp_port *p, void *aux)
+{
+ p->aux = aux;
+}
+
+/* Returns the 'aux' member of 'p'. */
+void *
+stp_port_get_aux(struct stp_port *p)
+{
+ return p->aux;
+}
+
/* Returns the index of port 'p' within its bridge. */
int
stp_port_no(const struct stp_port *p)
return p - stp->ports;
}
+/* Returns the port ID for 'p'. */
+int
+stp_port_get_id(const struct stp_port *p)
+{
+ return p->port_id;
+}
+
/* Returns the state of port 'p'. */
enum stp_state
stp_port_get_state(const struct stp_port *p)
return p->state;
}
+/* Returns the role of port 'p'. */
+enum stp_role
+stp_port_get_role(const struct stp_port *p)
+{
+ struct stp_port *root_port = stp_get_root_port(p->stp);
+
+ if (root_port && root_port->port_id == p->port_id) {
+ return STP_ROLE_ROOT;
+ } else if (stp_is_designated_port(p)) {
+ return STP_ROLE_DESIGNATED;
+ } else if (p->state == STP_DISABLED) {
+ return STP_ROLE_DISABLED;
+ } else {
+ return STP_ROLE_ALTERNATE;
+ }
+}
+
/* Disables STP on port 'p'. */
void
stp_port_disable(struct stp_port *p)
if (stp_is_root_bridge(stp) && !root) {
stp_become_root_bridge(stp);
}
+ p->aux = NULL;
}
}
}
}
+/* Convert 'speed' (measured in Mb/s) into the path cost. */
+uint16_t
+stp_convert_speed_to_cost(unsigned int speed)
+{
+ return speed >= 10000 ? 2 /* 10 Gb/s. */
+ : speed >= 1000 ? 4 /* 1 Gb/s. */
+ : speed >= 100 ? 19 /* 100 Mb/s. */
+ : speed >= 16 ? 62 /* 16 Mb/s. */
+ : speed >= 10 ? 100 /* 10 Mb/s. */
+ : speed >= 4 ? 250 /* 4 Mb/s. */
+ : 19; /* 100 Mb/s (guess). */
+}
+
/* Sets the path cost of port 'p' to 'path_cost'. Lower values are generally
* used to indicate faster links. Use stp_port_set_speed() to automatically
* generate a default path cost from a link speed. */
void
stp_port_set_speed(struct stp_port *p, unsigned int speed)
{
- stp_port_set_path_cost(p, (speed >= 10000 ? 2 /* 10 Gb/s. */
- : speed >= 1000 ? 4 /* 1 Gb/s. */
- : speed >= 100 ? 19 /* 100 Mb/s. */
- : speed >= 16 ? 62 /* 16 Mb/s. */
- : speed >= 10 ? 100 /* 10 Mb/s. */
- : speed >= 4 ? 250 /* 4 Mb/s. */
- : 19)); /* 100 Mb/s (guess). */
+ stp_port_set_path_cost(p, stp_convert_speed_to_cost(speed));
}
/* Enables topology change detection on port 'p'. */
config.header.bpdu_type = STP_TYPE_CONFIG;
config.flags = 0;
if (p->topology_change_ack) {
- config.flags |= htons(STP_CONFIG_TOPOLOGY_CHANGE_ACK);
+ config.flags |= STP_CONFIG_TOPOLOGY_CHANGE_ACK;
}
if (stp->topology_change) {
- config.flags |= htons(STP_CONFIG_TOPOLOGY_CHANGE);
+ config.flags |= STP_CONFIG_TOPOLOGY_CHANGE;
}
config.root_id = htonll(stp->designated_root);
config.root_path_cost = htonl(stp->root_path_cost);
stp->max_age = ntohs(config->max_age);
stp->hello_time = ntohs(config->hello_time);
stp->forward_delay = ntohs(config->forward_delay);
- stp->topology_change = config->flags & htons(STP_CONFIG_TOPOLOGY_CHANGE);
+ stp->topology_change = config->flags & STP_CONFIG_TOPOLOGY_CHANGE;
}
static bool
if (p == stp->root_port) {
stp_record_config_timeout_values(stp, config);
stp_config_bpdu_generation(stp);
- if (config->flags & htons(STP_CONFIG_TOPOLOGY_CHANGE_ACK)) {
+ if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE_ACK) {
stp_topology_change_acknowledged(stp);
}
}
p->topology_change_ack = false;
p->config_pending = false;
p->change_detection_enabled = true;
+ p->aux = NULL;
stp_stop_timer(&p->message_age_timer);
stp_stop_timer(&p->forward_delay_timer);
stp_stop_timer(&p->hold_timer);
#define STP_DEFAULT_BRIDGE_PRIORITY 32768
#define STP_DEFAULT_PORT_PRIORITY 128
+/* Default time values. */
+#define STP_DEFAULT_MAX_AGE 20000
+#define STP_DEFAULT_HELLO_TIME 2000
+#define STP_DEFAULT_FWD_DELAY 15000
+
/* Bridge identifier. Top 16 bits are a priority value (numerically lower
* values are higher priorities). Bottom 48 bits are MAC address of bridge. */
typedef uint64_t stp_identifier;
+
+#define STP_ID_FMT "%04"PRIx16".%012"PRIx64
+#define STP_ID_ARGS(stp_id) \
+ (uint16_t)((stp_id) >> 48), \
+ (uint64_t)((stp_id) & 0xffffffffffffULL)
+
+#define STP_PORT_ID_FMT "%04"PRIx16
+
/* Basic STP functionality. */
#define STP_MAX_PORTS 255
struct stp *stp_create(const char *name, stp_identifier bridge_id,
/* State of an STP port.
*
* A port is in exactly one state at any given time, but distinct bits are used
- * for states to allow testing for more than one state with a bit mask. */
+ * for states to allow testing for more than one state with a bit mask.
+ *
+ * The STP_DISABLED state means that the port is disabled by management.
+ * In our implementation, this state means that the port does not
+ * participate in the spanning tree, but it still forwards traffic as if
+ * it were in the STP_FORWARDING state. This may be different from
+ * other implementations.
+ *
+ * The following diagram describes the various states and what they are
+ * allowed to do in OVS:
+ *
+ * FWD LRN TX_BPDU RX_BPDU
+ * --- --- ------- -------
+ * Disabled Y - - -
+ * Blocking - - - Y
+ * Listening - - Y Y
+ * Learning - Y Y Y
+ * Forwarding Y Y Y Y
+ *
+ * Once again, note that the disabled state forwards traffic, which is
+ * likely different than the spec would indicate.
+ */
enum stp_state {
- STP_DISABLED = 1 << 0, /* 8.4.5: Disabled by management. */
+ STP_DISABLED = 1 << 0, /* 8.4.5: See note above. */
STP_LISTENING = 1 << 1, /* 8.4.2: Not learning or relaying frames. */
STP_LEARNING = 1 << 2, /* 8.4.3: Learning but not relaying frames. */
STP_FORWARDING = 1 << 3, /* 8.4.4: Learning and relaying frames. */
bool stp_forward_in_state(enum stp_state);
bool stp_learn_in_state(enum stp_state);
+/* Role of an STP port. */
+enum stp_role {
+ STP_ROLE_ROOT, /* Path to root bridge. */
+ STP_ROLE_DESIGNATED, /* Path to LAN segments. */
+ STP_ROLE_ALTERNATE, /* Backup path to root bridge. */
+ STP_ROLE_DISABLED /* Port does not participate in STP. */
+};
+const char *stp_role_name(enum stp_role);
+
void stp_received_bpdu(struct stp_port *, const void *bpdu, size_t bpdu_size);
struct stp *stp_port_get_stp(struct stp_port *);
+void stp_port_set_aux(struct stp_port *, void *);
+void *stp_port_get_aux(struct stp_port *);
int stp_port_no(const struct stp_port *);
+int stp_port_get_id(const struct stp_port *);
enum stp_state stp_port_get_state(const struct stp_port *);
+enum stp_role stp_port_get_role(const struct stp_port *);
void stp_port_enable(struct stp_port *);
void stp_port_disable(struct stp_port *);
void stp_port_set_priority(struct stp_port *, uint8_t new_priority);
+uint16_t stp_convert_speed_to_cost(unsigned int speed);
void stp_port_set_path_cost(struct stp_port *, uint16_t path_cost);
void stp_port_set_speed(struct stp_port *, unsigned int speed);
void stp_port_enable_change_detection(struct stp_port *);