/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "byte-order.h"
#include "connectivity.h"
#include "ofpbuf.h"
+#include "ovs-atomic.h"
+#include "dp-packet.h"
#include "packets.h"
#include "seq.h"
#include "unixctl.h"
#include "util.h"
-#include "vlog.h"
+#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(stp);
+static struct vlog_rate_limit stp_rl = VLOG_RATE_LIMIT_INIT(60, 60);
+
#define STP_PROTOCOL_ID 0x0000
#define STP_PROTOCOL_VERSION 0x00
#define STP_TYPE_CONFIG 0x00
struct stp_port {
struct stp *stp;
+ char *port_name; /* Human-readable name for log messages. */
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. */
};
struct stp {
- struct list node; /* Node in all_stps list. */
+ struct ovs_list node; /* Node in all_stps list. */
/* Static bridge data. */
char *name; /* Human-readable name for log messages. */
/* 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 (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux);
void *aux;
- atomic_int ref_cnt;
+ struct ovs_refcount ref_cnt;
};
static struct ovs_mutex mutex;
-static struct list all_stps__ = LIST_INITIALIZER(&all_stps__);
-static struct list *const all_stps OVS_GUARDED_BY(mutex) = &all_stps__;
+static struct ovs_list all_stps__ = OVS_LIST_INITIALIZER(&all_stps__);
+static struct ovs_list *const all_stps OVS_GUARDED_BY(mutex) = &all_stps__;
#define FOR_EACH_ENABLED_PORT(PORT, STP) \
for ((PORT) = stp_next_enabled_port((STP), (STP)->ports); \
*/
struct stp *
stp_create(const char *name, stp_identifier bridge_id,
- void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux),
+ void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux),
void *aux)
{
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
p->path_cost = 19; /* Recommended default for 100 Mb/s link. */
stp_initialize_port(p, STP_DISABLED);
}
- atomic_init(&stp->ref_cnt, 1);
+ ovs_refcount_init(&stp->ref_cnt);
list_push_back(all_stps, &stp->node);
ovs_mutex_unlock(&mutex);
{
struct stp *stp = CONST_CAST(struct stp *, stp_);
if (stp) {
- int orig;
- atomic_add(&stp->ref_cnt, 1, &orig);
- ovs_assert(orig > 0);
+ ovs_refcount_ref(&stp->ref_cnt);
}
return stp;
}
void
stp_unref(struct stp *stp)
{
- int orig;
-
- if (!stp) {
- return;
- }
+ if (stp && ovs_refcount_unref_relaxed(&stp->ref_cnt) == 1) {
+ size_t i;
- atomic_sub(&stp->ref_cnt, 1, &orig);
- ovs_assert(orig > 0);
- if (orig == 1) {
ovs_mutex_lock(&mutex);
list_remove(&stp->node);
ovs_mutex_unlock(&mutex);
free(stp->name);
+
+ for (i = 0; i < STP_MAX_PORTS; i++) {
+ free(stp->ports[i].port_name);
+ }
free(stp);
}
}
case STP_BLOCKING:
return "blocking";
default:
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
}
/* Returns true if 'state' is one in which packets received on a port should
* be forwarded, false otherwise.
- *
- * Returns true if 'state' is STP_DISABLED, since presumably in that case the
- * port should still work, just not have STP applied to it. */
+ */
bool
stp_forward_in_state(enum stp_state state)
{
- return (state & (STP_DISABLED | STP_FORWARDING)) != 0;
+ return (state & STP_FORWARDING) != 0;
}
/* Returns true if 'state' is one in which MAC learning should be done on
* packets received on a port, false otherwise.
- *
- * Returns true if 'state' is STP_DISABLED, since presumably in that case the
- * port should still work, just not have STP applied to it. */
+ */
bool
stp_learn_in_state(enum stp_state state)
{
- return (state & (STP_DISABLED | STP_LEARNING | STP_FORWARDING)) != 0;
+ return (state & (STP_LEARNING | STP_FORWARDING)) != 0;
+}
+
+/* Returns true if 'state' is one in which bpdus should be forwarded on a
+ * port, false otherwise.
+ *
+ * Returns true if 'state' is STP_DISABLED, since in that case the port does
+ * not generate the bpdu and should just forward it (e.g. patch port on pif
+ * bridge). */
+bool
+stp_should_forward_bpdu(enum stp_state state)
+{
+ return (state &
+ ( STP_DISABLED | STP_LISTENING | STP_LEARNING
+ | STP_FORWARDING)) != 0;
}
/* Returns the name for the given 'role' (for use in debugging and log
case STP_ROLE_DISABLED:
return "disabled";
default:
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
}
return stp;
}
+void
+stp_port_set_name(struct stp_port *p, const char *name)
+{
+ char *old;
+
+ ovs_mutex_lock(&mutex);
+ old = p->port_name;
+ p->port_name = xstrdup(name);
+ free(old);
+ ovs_mutex_unlock(&mutex);
+}
+
/* Sets the 'aux' member of 'p'.
*
* The 'aux' member will be reset to NULL when stp_port_disable() is
return;
}
if (p->hold_timer.active) {
+ VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu pending",
+ stp->name, p->port_name);
p->config_pending = true;
} else {
struct stp_config_bpdu config;
if (ntohs(config.message_age) < stp->max_age) {
p->topology_change_ack = false;
p->config_pending = false;
+ VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu",
+ stp->name, p->port_name);
stp_send_bpdu(p, &config, sizeof config);
stp_start_timer(&p->hold_timer, 0);
}
{
struct stp_port *p = stp->root_port;
struct stp_tcn_bpdu tcn_bpdu;
+
if (!p) {
return;
}
+ VLOG_DBG_RL(&stp_rl, "bridge: %s, root port: %s, transmit tcn", stp->name,
+ p->port_name);
tcn_bpdu.header.protocol_id = htons(STP_PROTOCOL_ID);
tcn_bpdu.header.protocol_version = STP_PROTOCOL_VERSION;
tcn_bpdu.header.bpdu_type = STP_TYPE_TCN;
{
struct stp *stp = p->stp;
bool root = stp_is_root_bridge(stp);
+
+ VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, message age timer expired",
+ stp->name, p->port_name);
stp_become_designated_port(p);
stp_configuration_update(stp);
stp_port_state_selection(stp);
{
ovs_assert(state & (STP_DISABLED | STP_BLOCKING));
stp_become_designated_port(p);
- stp_set_port_state(p, state);
+
+ if (!p->state && state == STP_DISABLED) {
+ p->state = state; /* Do not trigger state change when initializing. */
+ } else {
+ stp_set_port_state(p, state);
+ }
p->topology_change_ack = false;
p->config_pending = false;
p->change_detection_enabled = true;
{
struct eth_header *eth;
struct llc_header *llc;
- struct ofpbuf *pkt;
+ struct dp_packet *pkt;
/* Skeleton. */
- pkt = ofpbuf_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
- pkt->l2 = eth = ofpbuf_put_zeros(pkt, sizeof *eth);
- llc = ofpbuf_put_zeros(pkt, sizeof *llc);
- pkt->l3 = ofpbuf_put(pkt, bpdu, bpdu_size);
+ pkt = dp_packet_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
+ eth = dp_packet_put_zeros(pkt, sizeof *eth);
+ llc = dp_packet_put_zeros(pkt, sizeof *llc);
+ dp_packet_reset_offsets(pkt);
+ dp_packet_set_l3(pkt, dp_packet_put(pkt, bpdu, bpdu_size));
/* 802.2 header. */
- memcpy(eth->eth_dst, eth_addr_stp, ETH_ADDR_LEN);
+ eth->eth_dst = eth_addr_stp;
/* p->stp->send_bpdu() must fill in source address. */
- eth->eth_type = htons(pkt->size - ETH_HEADER_LEN);
+ eth->eth_type = htons(dp_packet_size(pkt) - ETH_HEADER_LEN);
/* LLC header. */
llc->llc_dsap = STP_LLC_DSAP;