From 4a4424a7398f09651e6102ebd08a01203e310d98 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 14 Oct 2008 10:53:27 -0700 Subject: [PATCH] Support up to 0xff00 ports in OpenFlow, without changing the implemented max. --- datapath/datapath.c | 24 +++-- datapath/datapath.h | 5 +- include/Makefile.am | 1 + include/openflow.h | 4 +- include/port-array.h | 106 +++++++++++++++++++ lib/Makefile.am | 1 + lib/learning-switch.c | 3 +- lib/ofp-print.c | 7 +- lib/port-array.c | 141 +++++++++++++++++++++++++ secchan/secchan.c | 236 +++++++++++++++++++++--------------------- switch/datapath.c | 13 ++- 11 files changed, 398 insertions(+), 143 deletions(-) create mode 100644 include/port-array.h create mode 100644 lib/port-array.c diff --git a/datapath/datapath.c b/datapath/datapath.c index 885593f63..8f01386a9 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -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) diff --git a/datapath/datapath.h b/datapath/datapath.h index bf5d4d68d..1747c2477 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -3,6 +3,7 @@ #ifndef DATAPATH_H #define DATAPATH_H 1 +#include #include #include #include @@ -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. */ }; diff --git a/include/Makefile.am b/include/Makefile.am index 20803f0e3..2d5fa8626 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -24,6 +24,7 @@ noinst_HEADERS = \ openflow-netlink.h \ packets.h \ poll-loop.h \ + port-array.h \ queue.h \ random.h \ rconn.h \ diff --git a/include/openflow.h b/include/openflow.h index b067811e7..202fd5a01 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -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 index 000000000..8fa8f4c66 --- /dev/null +++ b/include/port-array.h @@ -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 +#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 */ diff --git a/lib/Makefile.am b/lib/Makefile.am index be2981283..0e2582815 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -20,6 +20,7 @@ libopenflow_a_SOURCES = \ ofpbuf.c \ ofp-print.c \ poll-loop.c \ + port-array.c \ queue.c \ random.c \ rconn.c \ diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 842af37d0..cc7f13939 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -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 diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 75f5b5cc8..fab43e28e 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -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 index 000000000..31e9a6478 --- /dev/null +++ b/lib/port-array.c @@ -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 +#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); +} diff --git a/secchan/secchan.c b/secchan/secchan.c index 4e7e3e589..9795c90cc 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -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); diff --git a/switch/datapath.c b/switch/datapath.c index 4321f94fd..98c8efcf6 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -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) { -- 2.43.0