Support up to 0xff00 ports in OpenFlow, without changing the implemented max.
authorBen Pfaff <blp@nicira.com>
Tue, 14 Oct 2008 17:53:27 +0000 (10:53 -0700)
committerBen Pfaff <blp@nicira.com>
Tue, 14 Oct 2008 17:53:27 +0000 (10:53 -0700)
datapath/datapath.c
datapath/datapath.h
include/Makefile.am
include/openflow.h
include/port-array.h [new file with mode: 0644]
lib/Makefile.am
lib/learning-switch.c
lib/ofp-print.c
lib/port-array.c [new file with mode: 0644]
secchan/secchan.c
switch/datapath.c

index 885593f..8f01386 100644 (file)
@@ -325,7 +325,7 @@ err_unlock:
 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;
@@ -354,7 +354,7 @@ static struct net_bridge_port *new_nbp(struct datapath *dp,
        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);
 
@@ -567,9 +567,10 @@ output_all(struct datapath *dp, struct sk_buff *skb, int flood)
 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 
@@ -633,7 +634,7 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port,
                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;
@@ -840,7 +841,7 @@ dp_send_features_reply(struct datapath *dp, const struct sender *sender)
        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)
@@ -930,9 +931,10 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm)
 {
        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))
@@ -1524,7 +1526,7 @@ static int port_stats_dump(struct datapath *dp, void *state,
        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)
index bf5d4d6..1747c24 100644 (file)
@@ -3,6 +3,7 @@
 #ifndef DATAPATH_H
 #define DATAPATH_H 1
 
+#include <linux/kernel.h>
 #include <linux/mutex.h>
 #include <linux/netlink.h>
 #include <linux/netdevice.h>
@@ -40,6 +41,8 @@
 
 struct sk_buff;
 
+#define DP_MAX_PORTS 255
+
 struct datapath {
        int dp_idx;
 
@@ -59,7 +62,7 @@ struct datapath {
        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. */
 };
index 20803f0..2d5fa86 100644 (file)
@@ -24,6 +24,7 @@ noinst_HEADERS = \
        openflow-netlink.h \
        packets.h \
        poll-loop.h \
+       port-array.h \
        queue.h \
        random.h \
        rconn.h \
index b067811..202fd5a 100644 (file)
@@ -76,7 +76,7 @@
 /* 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 
@@ -342,7 +342,7 @@ enum ofp_action_type {
 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);
diff --git a/include/port-array.h b/include/port-array.h
new file mode 100644 (file)
index 0000000..8fa8f4c
--- /dev/null
@@ -0,0 +1,106 @@
+/* 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 */
index be29812..0e25828 100644 (file)
@@ -20,6 +20,7 @@ libopenflow_a_SOURCES = \
        ofpbuf.c \
        ofp-print.c \
        poll-loop.c \
+       port-array.c \
        queue.c \
        random.c \
        rconn.c \
index 842af37..cc7f139 100644 (file)
@@ -47,6 +47,7 @@
 #include "openflow.h"
 #include "queue.h"
 #include "rconn.h"
+#include "stp.h"
 #include "timeval.h"
 #include "vconn.h"
 #include "xtoxll.h"
@@ -291,7 +292,7 @@ static void
 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
index 75f5b5c..fab43e2 100644 (file)
@@ -601,7 +601,7 @@ ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
                           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;
 
@@ -617,11 +617,12 @@ ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
     }
     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'
diff --git a/lib/port-array.c b/lib/port-array.c
new file mode 100644 (file)
index 0000000..31e9a64
--- /dev/null
@@ -0,0 +1,141 @@
+/* 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);
+}
index 4e7e3e5..9795c90 100644 (file)
@@ -62,6 +62,7 @@
 #include "openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "port-array.h"
 #include "rconn.h"
 #include "stp.h"
 #include "timeval.h"
@@ -193,8 +194,12 @@ static struct hook port_watcher_create(struct rconn *local,
                                        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);
 
@@ -629,7 +634,7 @@ struct port_watcher_local_cb {
 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;
@@ -670,25 +675,15 @@ sanitize_opp(struct ofp_phy_port *opp)
     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);
     }
 }
 
@@ -706,6 +701,12 @@ get_port_name(const struct ofp_phy_port *port, char *name, size_t name_size)
     }
 }
 
+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)
 {
@@ -715,10 +716,7 @@ 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) {
@@ -744,33 +742,29 @@ call_local_port_changed_callbacks(struct port_watcher *pw)
 
 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
@@ -783,25 +777,29 @@ port_watcher_local_packet_cb(struct relay *r, void *pw_)
     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);
             }
         }
 
@@ -809,7 +807,7 @@ port_watcher_local_packet_cb(struct relay *r, void *pw_)
     } 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);
         }
@@ -828,17 +826,14 @@ port_watcher_remote_packet_cb(struct relay *r, void *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);
             }
         }
     }
@@ -921,37 +916,31 @@ log_port_status(uint16_t port_no,
                 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);
     }
 }
 
@@ -978,14 +967,28 @@ port_watcher_register_local_port_callback(struct port_watcher *pw,
 }
 
 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)
 {
@@ -994,14 +997,14 @@ port_watcher_set_flags(struct port_watcher *pw, int port_no,
     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;
@@ -1040,15 +1043,12 @@ port_watcher_create(struct rconn *local_rconn, struct rconn *remote_rconn,
                     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,
@@ -1190,20 +1190,12 @@ snat_del_rules(const uint8_t *dev_name)
 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;
     }
 
@@ -1224,9 +1216,9 @@ snat_config(const struct nx_snat_config *sc, struct snat_data *snat)
             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);
     }
 }
@@ -1274,7 +1266,7 @@ snat_port_changed_cb(uint16_t port_no,
     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;
     }
 
@@ -1467,10 +1459,17 @@ static void
 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);
@@ -1479,7 +1478,7 @@ send_bpdu(const void *bpdu, size_t bpdu_size, int port_no, void *stp_)
 
     /* 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. */
@@ -1495,9 +1494,6 @@ send_bpdu(const void *bpdu, size_t bpdu_size, int port_no, void *stp_)
 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;
 }
 
@@ -1515,7 +1511,7 @@ stp_port_changed_cb(uint16_t port_no,
     }
 
     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);
index 4321f94..98c8efc 100644 (file)
@@ -116,6 +116,9 @@ struct remote {
     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. */
@@ -134,7 +137,7 @@ struct datapath {
     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. */
 };
 
@@ -533,7 +536,7 @@ output_all(struct datapath *dp, struct ofpbuf *buffer, int in_port, int flood)
 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)) {
@@ -567,7 +570,7 @@ dp_output_port(struct datapath *dp, struct ofpbuf *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);
         }
@@ -682,7 +685,7 @@ void
 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 */
@@ -1288,7 +1291,7 @@ static int port_stats_dump(struct datapath *dp, void *state,
     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) {