static int find_portno(struct datapath *dp)
{
int i;
- for (i = 0; i < OFPP_MAX; i++)
+ for (i = 0; i < DP_MAX_PORTS; i++)
if (dp->ports[i] == NULL)
return i;
return -EXFULL;
INIT_WORK(&p->port_task, NULL);
if (port_no != OFPP_LOCAL)
rcu_assign_pointer(dev->br_port, p);
- if (port_no < OFPP_MAX)
+ if (port_no < DP_MAX_PORTS)
rcu_assign_pointer(dp->ports[port_no], p);
list_add_rcu(&p->node, &dp->port_list);
void dp_set_origin(struct datapath *dp, uint16_t in_port,
struct sk_buff *skb)
{
- struct net_bridge_port *p = (in_port < OFPP_MAX ? dp->ports[in_port]
- : in_port == OFPP_LOCAL ? dp->local_port
- : NULL);
+ struct net_bridge_port *p;
+ p = (in_port < DP_MAX_PORTS ? dp->ports[in_port]
+ : in_port == OFPP_LOCAL ? dp->local_port
+ : NULL);
if (p)
skb->dev = p->dev;
else
return dev ? dp_dev_recv(dev, skb) : -ESRCH;
}
- case 0 ... OFPP_MAX-1: {
+ case 0 ... DP_MAX_PORTS - 1: {
struct net_bridge_port *p = dp->ports[out_port];
if (p == NULL)
goto bad_port;
int port_count;
/* Overallocate. */
- port_max_len = sizeof(struct ofp_phy_port) * OFPP_MAX;
+ port_max_len = sizeof(struct ofp_phy_port) * DP_MAX_PORTS;
ofr = alloc_openflow_skb(dp, sizeof(*ofr) + port_max_len,
OFPT_FEATURES_REPLY, sender, &skb);
if (!ofr)
{
unsigned long int flags;
int port_no = ntohs(opm->port_no);
- struct net_bridge_port *p = (port_no < OFPP_MAX ? dp->ports[port_no]
- : port_no == OFPP_LOCAL ? dp->local_port
- : NULL);
+ struct net_bridge_port *p;
+ p = (port_no < DP_MAX_PORTS ? dp->ports[port_no]
+ : port_no == OFPP_LOCAL ? dp->local_port
+ : NULL);
/* Make sure the port id hasn't changed since this was sent */
if (!p || memcmp(opm->hw_addr, p->dev->dev_addr, ETH_ALEN))
ops = body;
n_ports = 0;
- for (i = s->port; i < OFPP_MAX && n_ports < max_ports; i++) {
+ for (i = s->port; i < DP_MAX_PORTS && n_ports < max_ports; i++) {
struct net_bridge_port *p = dp->ports[i];
struct net_device_stats *stats;
if (!p)
#ifndef DATAPATH_H
#define DATAPATH_H 1
+#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/netlink.h>
#include <linux/netdevice.h>
struct sk_buff;
+#define DP_MAX_PORTS 255
+
struct datapath {
int dp_idx;
uint16_t miss_send_len;
/* Switch ports. */
- struct net_bridge_port *ports[OFPP_MAX];
+ struct net_bridge_port *ports[DP_MAX_PORTS];
struct net_bridge_port *local_port; /* OFPP_LOCAL port. */
struct list_head port_list; /* All ports, including local_port. */
};
openflow-netlink.h \
packets.h \
poll-loop.h \
+ port-array.h \
queue.h \
random.h \
rconn.h \
/* Port numbering. Physical ports are numbered starting from 0. */
enum ofp_port {
/* Maximum number of physical switch ports. */
- OFPP_MAX = 255,
+ OFPP_MAX = 0xff00,
/* Fake output "ports". */
OFPP_IN_PORT = 0xfff8, /* Send the packet out the input port. This
struct ofp_action_output {
uint16_t type; /* OFPAT_OUTPUT. */
uint16_t len; /* Length is 8. */
- uint16_t port; /* Ouptut port. */
+ uint16_t port; /* Output port. */
uint16_t max_len; /* Max length to send to controller. */
};
OFP_ASSERT(sizeof(struct ofp_action_output) == 8);
--- /dev/null
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef PORT_ARRAY_H
+#define PORT_ARRAY_H 1
+
+#include <assert.h>
+#include "openflow.h"
+#include "util.h"
+
+static inline uint16_t
+port_array_extract_bits__(uint16_t data, int start, int count)
+{
+ return (data >> start) & ((1u << count) - 1);
+}
+
+/* Level 1: most-significant bits. */
+#define PORT_ARRAY_L1_BITS 5
+#define PORT_ARRAY_L1_SHIFT (PORT_ARRAY_L3_BITS + PORT_ARRAY_L2_BITS)
+#define PORT_ARRAY_L1_SIZE (1u << PORT_ARRAY_L1_BITS)
+#define PORT_ARRAY_L1(IDX) \
+ port_array_extract_bits__(IDX, PORT_ARRAY_L1_SHIFT, PORT_ARRAY_L1_BITS)
+
+/* Level 2: middle bits. */
+#define PORT_ARRAY_L2_BITS 5
+#define PORT_ARRAY_L2_SHIFT PORT_ARRAY_L3_BITS
+#define PORT_ARRAY_L2_SIZE (1u << PORT_ARRAY_L2_BITS)
+#define PORT_ARRAY_L2(IDX) \
+ port_array_extract_bits__(IDX, PORT_ARRAY_L2_SHIFT, PORT_ARRAY_L2_BITS)
+
+/* Level 3: least-significant bits. */
+#define PORT_ARRAY_L3_BITS 6
+#define PORT_ARRAY_L3_SHIFT 0
+#define PORT_ARRAY_L3_SIZE (1u << PORT_ARRAY_L3_BITS)
+#define PORT_ARRAY_L3(IDX) \
+ port_array_extract_bits__(IDX, PORT_ARRAY_L3_SHIFT, PORT_ARRAY_L3_BITS)
+
+#define PORT_ARRAY_SIZE (1u << (PORT_ARRAY_L1_BITS \
+ + PORT_ARRAY_L2_BITS \
+ + PORT_ARRAY_L3_BITS))
+
+BUILD_ASSERT_DECL(PORT_ARRAY_SIZE > 0xffff);
+
+/* A "sparse array" of up to 65536 elements (numbered 0...65535), implemented
+ * as a 3-level trie. Most efficient when the elements are clustered
+ * together. */
+struct port_array {
+ struct port_array_l2 *l1[1u << PORT_ARRAY_L1_BITS];
+};
+
+struct port_array_l2 {
+ struct port_array_l3 *l2[1u << PORT_ARRAY_L2_BITS];
+};
+
+struct port_array_l3 {
+ void *l3[1u << PORT_ARRAY_L3_BITS];
+};
+
+/* Returns the value of the element numbered 'idx' in 'pa', or a null pointer
+ * if no element numbered 'idx' has been set. */
+static inline void *
+port_array_get(const struct port_array *pa, uint16_t idx)
+{
+ unsigned int l1_idx = PORT_ARRAY_L1(idx);
+ unsigned int l2_idx = PORT_ARRAY_L2(idx);
+ unsigned int l3_idx = PORT_ARRAY_L3(idx);
+ return pa->l1[l1_idx]->l2[l2_idx]->l3[l3_idx];
+}
+
+void port_array_init(struct port_array *);
+void port_array_destroy(struct port_array *);
+void port_array_set(struct port_array *, uint16_t idx, void *);
+void *port_array_first(const struct port_array *, unsigned int *);
+void *port_array_next(const struct port_array *, unsigned int *);
+
+#endif /* port-array.h */
ofpbuf.c \
ofp-print.c \
poll-loop.c \
+ port-array.c \
queue.c \
random.c \
rconn.c \
#include "openflow.h"
#include "queue.h"
#include "rconn.h"
+#include "stp.h"
#include "timeval.h"
#include "vconn.h"
#include "xtoxll.h"
process_phy_port(struct lswitch *sw, struct rconn *rconn,
const struct ofp_phy_port *opp)
{
- if (sw->capabilities & OFPC_STP && ntohs(opp->port_no) < OFPP_MAX) {
+ if (sw->capabilities & OFPC_STP && ntohs(opp->port_no) < STP_MAX_PORTS) {
uint32_t config = ntohl(opp->config);
uint32_t state = ntohl(opp->state);
uint32_t new_config = config & ~(OFPPC_NO_RECV | OFPPC_NO_RECV_STP
int verbosity)
{
const struct ofp_switch_features *osf = oh;
- struct ofp_phy_port port_list[OFPP_MAX];
+ struct ofp_phy_port *port_list;
int n_ports;
int i;
}
n_ports = (len - sizeof *osf) / sizeof *osf->ports;
- memcpy(port_list, osf->ports, (len - sizeof *osf));
- qsort(port_list, n_ports, sizeof port_list[0], compare_ports);
+ port_list = xmemdup(osf->ports, len - sizeof *osf);
+ qsort(port_list, n_ports, sizeof *port_list, compare_ports);
for (i = 0; i < n_ports; i++) {
ofp_print_phy_port(string, &port_list[i]);
}
+ free(port_list);
}
/* Pretty-print the struct ofp_switch_config of 'len' bytes at 'oh' to 'string'
--- /dev/null
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include <config.h>
+#include "port-array.h"
+
+static struct port_array_l2 l2_sentinel;
+static struct port_array_l3 l3_sentinel;
+static bool inited;
+
+/* Initializes 'pa' as an empty port_array. */
+void
+port_array_init(struct port_array *pa)
+{
+ size_t i;
+ if (!inited) {
+ inited = true;
+ for (i = 0; i < PORT_ARRAY_L2_SIZE; i++) {
+ l2_sentinel.l2[i] = &l3_sentinel;
+ }
+ }
+ for (i = 0; i < PORT_ARRAY_L1_SIZE; i++) {
+ pa->l1[i] = &l2_sentinel;
+ }
+}
+
+/* Sets 'pa' element numbered 'idx' to 'p'. */
+void
+port_array_set(struct port_array *pa, uint16_t idx, void *p)
+{
+ struct port_array_l2 **l2p, *l2;
+ struct port_array_l3 **l3p, *l3;
+
+ /* Traverse level 1. */
+ l2p = &pa->l1[PORT_ARRAY_L1(idx)];
+ if (*l2p == &l2_sentinel) {
+ *l2p = xmemdup(&l2_sentinel, sizeof l2_sentinel);
+ }
+ l2 = *l2p;
+
+ /* Traverse level 2. */
+ l3p = &l2->l2[PORT_ARRAY_L2(idx)];
+ if (*l3p == &l3_sentinel) {
+ *l3p = xmemdup(&l3_sentinel, sizeof l3_sentinel);
+ }
+ l3 = *l3p;
+
+ /* Set level 3. */
+ l3->l3[PORT_ARRAY_L3(idx)] = p;
+}
+
+static void *
+next(const struct port_array *pa, unsigned int *idxp)
+{
+ unsigned int idx = *idxp;
+
+ /* Using shift-right directly here, instead of PORT_ARRAY_L1(idx), ensures
+ * that with an initially too-big value of '*idxp' we will skip the outer
+ * loop and return NULL. */
+ unsigned int l1_idx = idx >> PORT_ARRAY_L1_SHIFT;
+ unsigned int l2_idx = PORT_ARRAY_L2(idx);
+ unsigned int l3_idx = PORT_ARRAY_L3(idx);
+ while (l1_idx < PORT_ARRAY_L1_SIZE) {
+ struct port_array_l2 *l2 = pa->l1[l1_idx];
+ if (l2 != &l2_sentinel) {
+ while (l2_idx < PORT_ARRAY_L2_SIZE) {
+ struct port_array_l3 *l3 = l2->l2[l2_idx];
+ if (l3 != &l3_sentinel) {
+ while (l3_idx < PORT_ARRAY_L3_SIZE) {
+ void *p = l3->l3[l3_idx];
+ if (p) {
+ *idxp = ((l1_idx << PORT_ARRAY_L1_SHIFT)
+ | (l2_idx << PORT_ARRAY_L2_SHIFT)
+ | (l3_idx << PORT_ARRAY_L3_SHIFT));
+ return p;
+ }
+ l3_idx++;
+ }
+ }
+ l2_idx++;
+ l3_idx = 0;
+ }
+ }
+ l1_idx++;
+ l2_idx = 0;
+ l3_idx = 0;
+ }
+ *idxp = PORT_ARRAY_SIZE;
+ return NULL;
+}
+
+/* Returns the value of the lowest-numbered non-empty element of 'pa', and sets
+ * '*idxp' to that element's index. If 'pa' is entirely empty, returns a null
+ * pointer and sets '*idxp' to 65536. */
+void *
+port_array_first(const struct port_array *pa, unsigned int *idxp)
+{
+ *idxp = 0;
+ return next(pa, idxp);
+}
+
+/* Returns the value of the lowest-numbered non-empty element of 'pa' greater
+ * than the initial value of '*idxp', and sets '*idxp' to that element's index.
+ * If 'pa' contains no non-empty elements with indexes greater than the initial
+ * value of '*idxp', returns a null pointer and sets '*idxp' to 65536. */
+void *
+port_array_next(const struct port_array *pa, unsigned int *idxp)
+{
+ ++*idxp;
+ return next(pa, idxp);
+}
#include "openflow.h"
#include "packets.h"
#include "poll-loop.h"
+#include "port-array.h"
#include "rconn.h"
#include "stp.h"
#include "timeval.h"
struct rconn *remote,
struct port_watcher **);
static uint32_t port_watcher_get_config(const struct port_watcher *,
- int port_no);
-static void port_watcher_set_flags(struct port_watcher *, int port_no,
+ uint16_t port_no);
+static const char *port_watcher_get_name(const struct port_watcher *,
+ uint16_t port_no) UNUSED;
+static const uint8_t *port_watcher_get_hwaddr(const struct port_watcher *,
+ uint16_t port_no);
+static void port_watcher_set_flags(struct port_watcher *, uint16_t port_no,
uint32_t config, uint32_t c_mask,
uint32_t state, uint32_t s_mask);
struct port_watcher {
struct rconn *local_rconn;
struct rconn *remote_rconn;
- struct ofp_phy_port ports[OFPP_MAX + 1];
+ struct port_array ports;
time_t last_feature_request;
bool got_feature_reply;
int n_txq;
opp->name[sizeof opp->name - 1] = '\0';
}
-static int
-port_no_to_pw_idx(int port_no)
-{
- return (port_no < OFPP_MAX ? port_no
- : port_no == OFPP_LOCAL ? OFPP_MAX
- : -1);
-}
-
static void
call_port_changed_callbacks(struct port_watcher *pw, int port_no,
const struct ofp_phy_port *old,
const struct ofp_phy_port *new)
{
- if (opp_differs(old, new)) {
- int i;
- for (i = 0; i < pw->n_cbs; i++) {
- port_changed_cb_func *port_changed = pw->cbs[i].port_changed;
- (port_changed)(port_no, old, new, pw->cbs[i].aux);
- }
+ int i;
+ for (i = 0; i < pw->n_cbs; i++) {
+ port_changed_cb_func *port_changed = pw->cbs[i].port_changed;
+ (port_changed)(port_no, old, new, pw->cbs[i].aux);
}
}
}
}
+static struct ofp_phy_port *
+lookup_port(const struct port_watcher *pw, uint16_t port_no)
+{
+ return port_array_get(&pw->ports, port_no);
+}
+
static void
call_local_port_changed_callbacks(struct port_watcher *pw)
{
/* Pass the local port to the callbacks, if it exists.
Pass a null pointer if there is no local port. */
- port = &pw->ports[port_no_to_pw_idx(OFPP_LOCAL)];
- if (port->port_no != htons(OFPP_LOCAL)) {
- port = NULL;
- }
+ port = lookup_port(pw, OFPP_LOCAL);
/* Log the name of the local port. */
if (port) {
static void
update_phy_port(struct port_watcher *pw, struct ofp_phy_port *opp,
- uint8_t reason, bool seen[OFPP_MAX + 1])
+ uint8_t reason)
{
- struct ofp_phy_port *pw_opp;
- struct ofp_phy_port old;
+ struct ofp_phy_port *old;
uint16_t port_no;
- int idx;
port_no = ntohs(opp->port_no);
- idx = port_no_to_pw_idx(port_no);
- if (idx < 0) {
- return;
- }
-
- if (seen) {
- seen[idx] = true;
- }
-
- pw_opp = &pw->ports[idx];
- old = *pw_opp;
- if (reason == OFPPR_DELETE) {
- memset(pw_opp, 0, sizeof *pw_opp);
- pw_opp->port_no = htons(OFPP_NONE);
- } else if (reason == OFPPR_MODIFY || reason == OFPPR_ADD) {
- *pw_opp = *opp;
- sanitize_opp(pw_opp);
+ old = lookup_port(pw, port_no);
+
+ if (reason == OFPPR_DELETE && old) {
+ call_port_changed_callbacks(pw, port_no, old, NULL);
+ free(old);
+ port_array_set(&pw->ports, port_no, NULL);
+ } else if ((reason == OFPPR_MODIFY || reason == OFPPR_ADD)
+ && (!old || opp_differs(opp, old))) {
+ struct ofp_phy_port new = *opp;
+ sanitize_opp(&new);
+ call_port_changed_callbacks(pw, port_no, old, &new);
+ if (old) {
+ *old = new;
+ } else {
+ port_array_set(&pw->ports, port_no, xmemdup(&new, sizeof new));
+ }
}
- call_port_changed_callbacks(pw, port_no, &old, pw_opp);
}
static bool
if (oh->type == OFPT_FEATURES_REPLY
&& msg->size >= offsetof(struct ofp_switch_features, ports)) {
struct ofp_switch_features *osf = msg->data;
- bool seen[ARRAY_SIZE(pw->ports)];
+ bool seen[PORT_ARRAY_SIZE];
+ struct ofp_phy_port *p;
+ unsigned int port_no;
size_t n_ports;
size_t i;
pw->got_feature_reply = true;
/* Update each port included in the message. */
- memset(seen, 0, sizeof seen);
+ memset(seen, false, sizeof seen);
n_ports = ((msg->size - offsetof(struct ofp_switch_features, ports))
/ sizeof *osf->ports);
for (i = 0; i < n_ports; i++) {
struct ofp_phy_port *opp = &osf->ports[i];
- update_phy_port(pw, opp, OFPPR_MODIFY, seen);
+ update_phy_port(pw, opp, OFPPR_MODIFY);
+ seen[ntohs(opp->port_no)] = true;
}
/* Delete all the ports not included in the message. */
- for (i = 0; i < ARRAY_SIZE(pw->ports); i++) {
- if (!seen[i]) {
- update_phy_port(pw, &pw->ports[i], OFPPR_DELETE, NULL);
+ for (p = port_array_first(&pw->ports, &port_no); p;
+ p = port_array_next(&pw->ports, &port_no)) {
+ if (!seen[port_no]) {
+ update_phy_port(pw, p, OFPPR_DELETE);
}
}
} else if (oh->type == OFPT_PORT_STATUS
&& msg->size >= sizeof(struct ofp_port_status)) {
struct ofp_port_status *ops = msg->data;
- update_phy_port(pw, &ops->desc, ops->reason, NULL);
+ update_phy_port(pw, &ops->desc, ops->reason);
if (ops->desc.port_no == htons(OFPP_LOCAL)) {
call_local_port_changed_callbacks(pw);
}
&& msg->size >= sizeof(struct ofp_port_mod)) {
struct ofp_port_mod *opm = msg->data;
uint16_t port_no = ntohs(opm->port_no);
- int idx = port_no_to_pw_idx(port_no);
- if (idx >= 0) {
- struct ofp_phy_port *pw_opp = &pw->ports[idx];
- if (pw_opp->port_no != htons(OFPP_NONE)) {
- struct ofp_phy_port old = *pw_opp;
- pw_opp->config = ((pw_opp->config & ~opm->mask)
- | (opm->config & opm->mask));
- call_port_changed_callbacks(pw, port_no, &old, pw_opp);
- if (pw_opp->port_no == htons(OFPP_LOCAL)) {
- call_local_port_changed_callbacks(pw);
- }
+ struct ofp_phy_port *pw_opp = lookup_port(pw, port_no);
+ if (pw_opp->port_no != htons(OFPP_NONE)) {
+ struct ofp_phy_port old = *pw_opp;
+ pw_opp->config = ((pw_opp->config & ~opm->mask)
+ | (opm->config & opm->mask));
+ call_port_changed_callbacks(pw, port_no, &old, pw_opp);
+ if (pw_opp->port_no == htons(OFPP_LOCAL)) {
+ call_local_port_changed_callbacks(pw);
}
}
}
void *aux)
{
if (VLOG_IS_DBG_ENABLED()) {
- bool was_enabled = old->port_no != htons(OFPP_NONE);
- bool now_enabled = new->port_no != htons(OFPP_NONE);
- uint32_t curr = ntohl(new->curr);
- uint32_t supported = ntohl(new->supported);
- struct ds ds;
-
- if (((old->config != new->config) || (old->state != new->state))
- && opp_differs(old, new) == 1) {
- /* Don't care if only flags changed. */
- return;
- }
-
- ds_init(&ds);
- ds_put_format(&ds, "\"%s\", "ETH_ADDR_FMT, new->name,
- ETH_ADDR_ARGS(new->hw_addr));
- if (curr) {
- put_features(&ds, ", current", curr);
- }
- if (supported) {
- put_features(&ds, ", supports", supported);
- }
- if (was_enabled != now_enabled) {
- if (now_enabled) {
- VLOG_DBG("Port %d added: %s", port_no, ds_cstr(&ds));
- } else {
+ if (old && new && (opp_differs(old, new)
+ == ((old->config != new->config)
+ + (old->state != new->state))))
+ {
+ /* Don't care if only state or config changed. */
+ } else if (!new) {
+ if (old) {
VLOG_DBG("Port %d deleted", port_no);
}
} else {
- VLOG_DBG("Port %d changed: %s", port_no, ds_cstr(&ds));
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ uint32_t curr = ntohl(new->curr);
+ uint32_t supported = ntohl(new->supported);
+ ds_put_format(&ds, "\"%s\", "ETH_ADDR_FMT, new->name,
+ ETH_ADDR_ARGS(new->hw_addr));
+ if (curr) {
+ put_features(&ds, ", current", curr);
+ }
+ if (supported) {
+ put_features(&ds, ", supports", supported);
+ }
+ VLOG_DBG("Port %d %s: %s",
+ port_no, old ? "changed" : "added", ds_cstr(&ds));
+ ds_destroy(&ds);
}
- ds_destroy(&ds);
}
}
}
static uint32_t
-port_watcher_get_config(const struct port_watcher *pw, int port_no)
+port_watcher_get_config(const struct port_watcher *pw, uint16_t port_no)
+{
+ struct ofp_phy_port *p = lookup_port(pw, port_no);
+ return p ? ntohl(p->config) : 0;
+}
+
+static const char *
+port_watcher_get_name(const struct port_watcher *pw, uint16_t port_no)
+{
+ struct ofp_phy_port *p = lookup_port(pw, port_no);
+ return p ? (const char *) p->name : NULL;
+}
+
+static const uint8_t *
+port_watcher_get_hwaddr(const struct port_watcher *pw, uint16_t port_no)
{
- int idx = port_no_to_pw_idx(port_no);
- return idx >= 0 ? ntohl(pw->ports[idx].config) : 0;
+ struct ofp_phy_port *p = lookup_port(pw, port_no);
+ return p ? p->hw_addr : NULL;
}
static void
-port_watcher_set_flags(struct port_watcher *pw, int port_no,
+port_watcher_set_flags(struct port_watcher *pw, uint16_t port_no,
uint32_t config, uint32_t c_mask,
uint32_t state, uint32_t s_mask)
{
struct ofp_port_mod *opm;
struct ofp_port_status *ops;
struct ofpbuf *b;
- int idx;
- idx = port_no_to_pw_idx(port_no);
- if (idx < 0) {
+ p = lookup_port(pw, port_no);
+ if (!p) {
+ VLOG_WARN_RL(&vrl, "attempting to set flags on nonexistent port %"
+ PRIu16, port_no);
return;
}
- p = &pw->ports[idx];
if (!((ntohl(p->state) ^ state) & s_mask)
&& (!((ntohl(p->config) ^ config) & c_mask))) {
return;
struct port_watcher **pwp)
{
struct port_watcher *pw;
- int i;
pw = *pwp = xcalloc(1, sizeof *pw);
pw->local_rconn = local_rconn;
pw->remote_rconn = remote_rconn;
pw->last_feature_request = TIME_MIN;
- for (i = 0; i < OFPP_MAX; i++) {
- pw->ports[i].port_no = htons(OFPP_NONE);
- }
+ port_array_init(&pw->ports);
pw->local_port_name[0] = '\0';
port_watcher_register_callback(pw, log_port_status, NULL);
return make_hook(port_watcher_local_packet_cb,
static void
snat_config(const struct nx_snat_config *sc, struct snat_data *snat)
{
- int idx;
- struct port_watcher *pw = snat->pw;
- struct ofp_phy_port *pw_opp;
struct snat_port_conf *c, *spc=NULL;
- uint16_t port_no;
+ const uint8_t *netdev_name;
- port_no = ntohs(sc->port);
- idx = port_no_to_pw_idx(port_no);
- if (idx < 0) {
- return;
- }
-
- pw_opp = &pw->ports[idx];
- if (htons(pw_opp->port_no) != port_no) {
+ netdev_name = (const uint8_t *) port_watcher_get_name(snat->pw,
+ ntohs(sc->port));
+ if (!netdev_name) {
return;
}
list_push_back(&snat->port_list, &spc->node);
}
memcpy(&spc->config, sc, sizeof(spc->config));
- snat_add_rules(sc, pw_opp->name);
+ snat_add_rules(sc, netdev_name);
} else if (spc) {
- snat_del_rules(pw_opp->name);
+ snat_del_rules(netdev_name);
list_remove(&spc->node);
}
}
struct snat_port_conf *c;
/* We're only interested in ports that went away */
- if (new->port_no != htons(OFPP_NONE)) {
+ if (old && !new) {
return;
}
send_bpdu(const void *bpdu, size_t bpdu_size, int port_no, void *stp_)
{
struct stp_data *stp = stp_;
+ const uint8_t *port_mac;
struct eth_header *eth;
struct llc_header *llc;
struct ofpbuf pkt, *opo;
+ port_mac = port_watcher_get_hwaddr(stp->pw, port_no);
+ if (!port_mac) {
+ VLOG_WARN_RL(&vrl, "cannot send BPDU on missing port %d", port_no);
+ return;
+ }
+
/* Packet skeleton. */
ofpbuf_init(&pkt, ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
eth = ofpbuf_put_uninit(&pkt, sizeof *eth);
/* 802.2 header. */
memcpy(eth->eth_dst, stp_eth_addr, ETH_ADDR_LEN);
- memcpy(eth->eth_src, stp->pw->ports[port_no].hw_addr, ETH_ADDR_LEN);
+ memcpy(eth->eth_src, port_mac, ETH_ADDR_LEN);
eth->eth_type = htons(pkt.size - ETH_HEADER_LEN);
/* LLC header. */
static bool
stp_is_port_supported(uint16_t port_no)
{
- /* We should be able to support STP on all possible OpenFlow physical
- * ports. (But we don't support STP on OFPP_LOCAL.) */
- BUILD_ASSERT_DECL(STP_MAX_PORTS >= OFPP_MAX);
return port_no < STP_MAX_PORTS;
}
}
p = stp_get_port(stp->stp, port_no);
- if (new->port_no == htons(OFPP_NONE)
+ if (!new
|| new->config & htonl(OFPPC_NO_STP | OFPPC_PORT_DOWN)
|| new->state & htonl(OFPPS_LINK_DOWN)) {
stp_port_disable(p);
void *cb_aux;
};
+#define DP_MAX_PORTS 255
+BUILD_ASSERT_DECL(DP_MAX_PORTS <= OFPP_MAX);
+
struct datapath {
/* Remote connections. */
struct remote *controller; /* Connection to controller. */
uint16_t miss_send_len;
/* Switch ports. */
- struct sw_port ports[OFPP_MAX];
+ struct sw_port ports[DP_MAX_PORTS];
struct list port_list; /* List of ports, for flooding. */
};
void
output_packet(struct datapath *dp, struct ofpbuf *buffer, int out_port)
{
- if (out_port >= 0 && out_port < OFPP_MAX) {
+ if (out_port >= 0 && out_port < DP_MAX_PORTS) {
struct sw_port *p = &dp->ports[out_port];
if (p->netdev != NULL && !(p->config & OFPPC_PORT_DOWN)) {
if (!netdev_send(p->netdev, buffer)) {
} else if (out_port == OFPP_IN_PORT) {
output_packet(dp, buffer, in_port);
} else if (out_port == OFPP_TABLE) {
- struct sw_port *p = in_port < OFPP_MAX ? &dp->ports[in_port] : 0;
+ struct sw_port *p = in_port < DP_MAX_PORTS ? &dp->ports[in_port] : 0;
if (run_flow_through_tables(dp, buffer, p)) {
ofpbuf_delete(buffer);
}
dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm)
{
int port_no = ntohs(opm->port_no);
- if (port_no < OFPP_MAX) {
+ if (port_no < DP_MAX_PORTS) {
struct sw_port *p = &dp->ports[port_no];
/* Make sure the port id hasn't changed since this was sent */
struct port_stats_state *s = state;
int i;
- for (i = s->port; i < OFPP_MAX; i++) {
+ for (i = s->port; i < DP_MAX_PORTS; i++) {
struct sw_port *p = &dp->ports[i];
struct ofp_port_stats *ops;
if (!p->netdev) {