1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
4 * We are making the OpenFlow specification and associated documentation
5 * (Software) available for public use and benefit with the expectation
6 * that others will use, modify and enhance the Software and contribute
7 * those enhancements back to the community. However, since we would
8 * like to make the Software available for broadest use, with as few
9 * restrictions as possible permission is hereby granted, free of
10 * charge, to any person obtaining a copy of this Software to deal in
11 * the Software under the copyrights without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sublicense, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 * The name and trademarks of copyright holder(s) may NOT be used in
30 * advertising or publicity pertaining to the Software or any
31 * derivatives without specific, written prior permission.
35 #include "port-watcher.h"
36 #include <arpa/inet.h>
40 #include "dynamic-string.h"
42 #include "openflow/openflow.h"
43 #include "poll-loop.h"
44 #include "port-array.h"
50 #define THIS_MODULE VLM_port_watcher
53 struct port_watcher_cb {
54 port_changed_cb_func *port_changed;
58 struct port_watcher_local_cb {
59 local_port_changed_cb_func *local_port_changed;
64 struct rconn *local_rconn;
65 struct rconn *remote_rconn;
66 struct port_array ports;
67 time_t last_feature_request;
68 bool got_feature_reply;
71 struct port_watcher_cb cbs[2];
73 struct port_watcher_local_cb local_cbs[4];
75 char local_port_name[OFP_MAX_PORT_NAME_LEN + 1];
78 /* Returns the number of fields that differ from 'a' to 'b'. */
80 opp_differs(const struct ofp_phy_port *a, const struct ofp_phy_port *b)
82 BUILD_ASSERT_DECL(sizeof *a == 48); /* Trips when we add or remove fields. */
83 return ((a->port_no != b->port_no)
84 + (memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr) != 0)
85 + (memcmp(a->name, b->name, sizeof a->name) != 0)
86 + (a->config != b->config)
87 + (a->state != b->state)
88 + (a->curr != b->curr)
89 + (a->advertised != b->advertised)
90 + (a->supported != b->supported)
91 + (a->peer != b->peer));
95 sanitize_opp(struct ofp_phy_port *opp)
99 for (i = 0; i < sizeof opp->name; i++) {
100 char c = opp->name[i];
101 if (c && (c < 0x20 || c > 0x7e)) {
105 opp->name[sizeof opp->name - 1] = '\0';
109 call_port_changed_callbacks(struct port_watcher *pw, int port_no,
110 const struct ofp_phy_port *old,
111 const struct ofp_phy_port *new)
114 for (i = 0; i < pw->n_cbs; i++) {
115 port_changed_cb_func *port_changed = pw->cbs[i].port_changed;
116 (port_changed)(port_no, old, new, pw->cbs[i].aux);
121 get_port_name(const struct ofp_phy_port *port, char *name, size_t name_size)
125 memcpy(name, port->name, MIN(name_size, sizeof port->name));
126 name[name_size - 1] = '\0';
127 for (p = name; *p != '\0'; p++) {
128 if (*p < 32 || *p > 126) {
134 static struct ofp_phy_port *
135 lookup_port(const struct port_watcher *pw, uint16_t port_no)
137 return port_array_get(&pw->ports, port_no);
141 call_local_port_changed_callbacks(struct port_watcher *pw)
143 char name[OFP_MAX_PORT_NAME_LEN + 1];
144 const struct ofp_phy_port *port;
147 /* Pass the local port to the callbacks, if it exists.
148 Pass a null pointer if there is no local port. */
149 port = lookup_port(pw, OFPP_LOCAL);
151 /* Log the name of the local port. */
153 get_port_name(port, name, sizeof name);
157 if (strcmp(pw->local_port_name, name)) {
159 VLOG_WARN("Identified data path local port as \"%s\".", name);
161 VLOG_WARN("Data path has no local port.");
163 strcpy(pw->local_port_name, name);
166 /* Invoke callbacks. */
167 for (i = 0; i < pw->n_local_cbs; i++) {
168 local_port_changed_cb_func *cb = pw->local_cbs[i].local_port_changed;
169 (cb)(port, pw->local_cbs[i].aux);
174 update_phy_port(struct port_watcher *pw, struct ofp_phy_port *opp,
177 struct ofp_phy_port *old;
180 port_no = ntohs(opp->port_no);
181 old = lookup_port(pw, port_no);
183 if (reason == OFPPR_DELETE && old) {
184 call_port_changed_callbacks(pw, port_no, old, NULL);
186 port_array_set(&pw->ports, port_no, NULL);
187 } else if (reason == OFPPR_MODIFY || reason == OFPPR_ADD) {
189 uint32_t s_mask = htonl(OFPPS_STP_MASK);
190 opp->state = (opp->state & ~s_mask) | (old->state & s_mask);
192 if (!old || opp_differs(opp, old)) {
193 struct ofp_phy_port new = *opp;
195 call_port_changed_callbacks(pw, port_no, old, &new);
199 port_array_set(&pw->ports, port_no, xmemdup(&new, sizeof new));
206 port_watcher_local_packet_cb(struct relay *r, void *pw_)
208 struct port_watcher *pw = pw_;
209 struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
210 struct ofp_header *oh = msg->data;
212 if (oh->type == OFPT_FEATURES_REPLY
213 && msg->size >= offsetof(struct ofp_switch_features, ports)) {
214 struct ofp_switch_features *osf = msg->data;
215 bool seen[PORT_ARRAY_SIZE];
216 struct ofp_phy_port *p;
217 unsigned int port_no;
221 pw->got_feature_reply = true;
222 if (pw->datapath_id != osf->datapath_id) {
223 pw->datapath_id = osf->datapath_id;
224 VLOG_WARN("Datapath id is %012"PRIx64, ntohll(pw->datapath_id));
227 /* Update each port included in the message. */
228 memset(seen, false, sizeof seen);
229 n_ports = ((msg->size - offsetof(struct ofp_switch_features, ports))
230 / sizeof *osf->ports);
231 for (i = 0; i < n_ports; i++) {
232 struct ofp_phy_port *opp = &osf->ports[i];
233 update_phy_port(pw, opp, OFPPR_MODIFY);
234 seen[ntohs(opp->port_no)] = true;
237 /* Delete all the ports not included in the message. */
238 for (p = port_array_first(&pw->ports, &port_no); p;
239 p = port_array_next(&pw->ports, &port_no)) {
240 if (!seen[port_no]) {
241 update_phy_port(pw, p, OFPPR_DELETE);
245 call_local_port_changed_callbacks(pw);
246 } else if (oh->type == OFPT_PORT_STATUS
247 && msg->size >= sizeof(struct ofp_port_status)) {
248 struct ofp_port_status *ops = msg->data;
249 update_phy_port(pw, &ops->desc, ops->reason);
250 if (ops->desc.port_no == htons(OFPP_LOCAL)) {
251 call_local_port_changed_callbacks(pw);
258 port_watcher_remote_packet_cb(struct relay *r, void *pw_)
260 struct port_watcher *pw = pw_;
261 struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
262 struct ofp_header *oh = msg->data;
264 if (oh->type == OFPT_PORT_MOD
265 && msg->size >= sizeof(struct ofp_port_mod)) {
266 struct ofp_port_mod *opm = msg->data;
267 uint16_t port_no = ntohs(opm->port_no);
268 struct ofp_phy_port *pw_opp = lookup_port(pw, port_no);
269 if (pw_opp->port_no != htons(OFPP_NONE)) {
270 struct ofp_phy_port old = *pw_opp;
271 pw_opp->config = ((pw_opp->config & ~opm->mask)
272 | (opm->config & opm->mask));
273 call_port_changed_callbacks(pw, port_no, &old, pw_opp);
274 if (pw_opp->port_no == htons(OFPP_LOCAL)) {
275 call_local_port_changed_callbacks(pw);
283 port_watcher_periodic_cb(void *pw_)
285 struct port_watcher *pw = pw_;
287 if (!pw->got_feature_reply
288 && time_now() >= pw->last_feature_request + 5
289 && rconn_is_connected(pw->local_rconn)) {
291 make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &b);
292 rconn_send_with_limit(pw->local_rconn, b, &pw->n_txq, 1);
293 pw->last_feature_request = time_now();
298 port_watcher_wait_cb(void *pw_)
300 struct port_watcher *pw = pw_;
301 if (!pw->got_feature_reply && rconn_is_connected(pw->local_rconn)) {
302 if (pw->last_feature_request != TIME_MIN) {
303 poll_timer_wait(pw->last_feature_request + 5 - time_now());
305 poll_immediate_wake();
311 put_duplexes(struct ds *ds, const char *name, uint32_t features,
312 uint32_t hd_bit, uint32_t fd_bit)
314 if (features & (hd_bit | fd_bit)) {
315 ds_put_format(ds, " %s", name);
316 if (features & hd_bit) {
317 ds_put_cstr(ds, "(HD)");
319 if (features & fd_bit) {
320 ds_put_cstr(ds, "(FD)");
326 put_features(struct ds *ds, const char *name, uint32_t features)
328 if (features & (OFPPF_10MB_HD | OFPPF_10MB_FD
329 | OFPPF_100MB_HD | OFPPF_100MB_FD
330 | OFPPF_1GB_HD | OFPPF_1GB_FD | OFPPF_10GB_FD)) {
331 ds_put_cstr(ds, name);
332 put_duplexes(ds, "10M", features, OFPPF_10MB_HD, OFPPF_10MB_FD);
333 put_duplexes(ds, "100M", features,
334 OFPPF_100MB_HD, OFPPF_100MB_FD);
335 put_duplexes(ds, "1G", features, OFPPF_100MB_HD, OFPPF_100MB_FD);
336 if (features & OFPPF_10GB_FD) {
337 ds_put_cstr(ds, " 10G");
339 if (features & OFPPF_AUTONEG) {
340 ds_put_cstr(ds, " AUTO_NEG");
342 if (features & OFPPF_PAUSE) {
343 ds_put_cstr(ds, " PAUSE");
345 if (features & OFPPF_PAUSE_ASYM) {
346 ds_put_cstr(ds, " PAUSE_ASYM");
352 log_port_status(uint16_t port_no,
353 const struct ofp_phy_port *old,
354 const struct ofp_phy_port *new,
357 if (VLOG_IS_DBG_ENABLED()) {
358 if (old && new && (opp_differs(old, new)
359 == ((old->config != new->config)
360 + (old->state != new->state))))
362 /* Don't care if only state or config changed. */
365 VLOG_DBG("Port %d deleted", port_no);
368 struct ds ds = DS_EMPTY_INITIALIZER;
369 uint32_t curr = ntohl(new->curr);
370 uint32_t supported = ntohl(new->supported);
371 ds_put_format(&ds, "\"%s\", "ETH_ADDR_FMT, new->name,
372 ETH_ADDR_ARGS(new->hw_addr));
374 put_features(&ds, ", current", curr);
377 put_features(&ds, ", supports", supported);
379 VLOG_DBG("Port %d %s: %s",
380 port_no, old ? "changed" : "added", ds_cstr(&ds));
387 port_watcher_register_callback(struct port_watcher *pw,
388 port_changed_cb_func *port_changed,
391 assert(pw->n_cbs < ARRAY_SIZE(pw->cbs));
392 pw->cbs[pw->n_cbs].port_changed = port_changed;
393 pw->cbs[pw->n_cbs].aux = aux;
398 port_watcher_register_local_port_callback(struct port_watcher *pw,
399 local_port_changed_cb_func *cb,
402 assert(pw->n_local_cbs < ARRAY_SIZE(pw->local_cbs));
403 pw->local_cbs[pw->n_local_cbs].local_port_changed = cb;
404 pw->local_cbs[pw->n_local_cbs].aux = aux;
409 port_watcher_get_config(const struct port_watcher *pw, uint16_t port_no)
411 struct ofp_phy_port *p = lookup_port(pw, port_no);
412 return p ? ntohl(p->config) : 0;
416 port_watcher_get_name(const struct port_watcher *pw, uint16_t port_no)
418 struct ofp_phy_port *p = lookup_port(pw, port_no);
419 return p ? (const char *) p->name : NULL;
423 port_watcher_get_hwaddr(const struct port_watcher *pw, uint16_t port_no)
425 struct ofp_phy_port *p = lookup_port(pw, port_no);
426 return p ? p->hw_addr : NULL;
430 port_watcher_set_flags(struct port_watcher *pw, uint16_t port_no,
431 uint32_t config, uint32_t c_mask,
432 uint32_t state, uint32_t s_mask)
434 struct ofp_phy_port old;
435 struct ofp_phy_port *p;
436 struct ofp_port_mod *opm;
437 struct ofp_port_status *ops;
440 p = lookup_port(pw, port_no);
445 if (!((ntohl(p->state) ^ state) & s_mask)
446 && (!((ntohl(p->config) ^ config) & c_mask))) {
451 /* Update our idea of the flags. */
452 p->config = htonl((ntohl(p->config) & ~c_mask) | (config & c_mask));
453 p->state = htonl((ntohl(p->state) & ~s_mask) | (state & s_mask));
454 call_port_changed_callbacks(pw, port_no, &old, p);
456 /* Change the flags in the datapath. */
457 opm = make_openflow(sizeof *opm, OFPT_PORT_MOD, &b);
458 opm->port_no = p->port_no;
459 memcpy(opm->hw_addr, p->hw_addr, OFP_ETH_ALEN);
460 opm->config = p->config;
461 opm->mask = htonl(c_mask);
462 opm->advertise = htonl(0);
463 rconn_send(pw->local_rconn, b, NULL);
465 /* Notify the controller that the flags changed. */
466 ops = make_openflow(sizeof *ops, OFPT_PORT_STATUS, &b);
467 ops->reason = OFPPR_MODIFY;
469 rconn_send(pw->remote_rconn, b, NULL);
473 port_watcher_is_ready(const struct port_watcher *pw)
475 return pw->got_feature_reply;
478 static struct hook_class port_watcher_hook_class = {
479 port_watcher_local_packet_cb, /* local_packet_cb */
480 port_watcher_remote_packet_cb, /* remote_packet_cb */
481 port_watcher_periodic_cb, /* periodic_cb */
482 port_watcher_wait_cb, /* wait_cb */
483 NULL, /* closing_cb */
487 port_watcher_start(struct secchan *secchan,
488 struct rconn *local_rconn, struct rconn *remote_rconn,
489 struct port_watcher **pwp)
491 struct port_watcher *pw;
493 pw = *pwp = xcalloc(1, sizeof *pw);
494 pw->local_rconn = local_rconn;
495 pw->remote_rconn = remote_rconn;
496 pw->last_feature_request = TIME_MIN;
497 port_array_init(&pw->ports);
498 pw->local_port_name[0] = '\0';
499 port_watcher_register_callback(pw, log_port_status, NULL);
500 add_hook(secchan, &port_watcher_hook_class, pw);