Never free an skb that has been passed to genlmsg_reply().
[sliver-openvswitch.git] / secchan / port-watcher.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  *
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:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
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
27  * SOFTWARE.
28  *
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.
32  */
33
34 #include <config.h>
35 #include "port-watcher.h"
36 #include <arpa/inet.h>
37 #include <assert.h>
38 #include <inttypes.h>
39 #include <stdlib.h>
40 #include "dynamic-string.h"
41 #include "ofpbuf.h"
42 #include "openflow/openflow.h"
43 #include "poll-loop.h"
44 #include "port-array.h"
45 #include "rconn.h"
46 #include "timeval.h"
47 #include "vconn.h"
48 #include "xtoxll.h"
49
50 #define THIS_MODULE VLM_port_watcher
51 #include "vlog.h"
52
53 struct port_watcher_cb {
54     port_changed_cb_func *port_changed;
55     void *aux;
56 };
57
58 struct port_watcher_local_cb {
59     local_port_changed_cb_func *local_port_changed;
60     void *aux;
61 };
62
63 struct port_watcher {
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;
69     uint64_t datapath_id;
70     int n_txq;
71     struct port_watcher_cb cbs[2];
72     int n_cbs;
73     struct port_watcher_local_cb local_cbs[4];
74     int n_local_cbs;
75     char local_port_name[OFP_MAX_PORT_NAME_LEN + 1];
76 };
77
78 /* Returns the number of fields that differ from 'a' to 'b'. */
79 static int
80 opp_differs(const struct ofp_phy_port *a, const struct ofp_phy_port *b)
81 {
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));
92 }
93
94 static void
95 sanitize_opp(struct ofp_phy_port *opp)
96 {
97     size_t i;
98
99     for (i = 0; i < sizeof opp->name; i++) {
100         char c = opp->name[i];
101         if (c && (c < 0x20 || c > 0x7e)) {
102             opp->name[i] = '.';
103         }
104     }
105     opp->name[sizeof opp->name - 1] = '\0';
106 }
107
108 static void
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)
112 {
113     int i;
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);
117     }
118 }
119
120 void
121 get_port_name(const struct ofp_phy_port *port, char *name, size_t name_size)
122 {
123     char *p;
124
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) {
129             *p = '.';
130         }
131     }
132 }
133
134 static struct ofp_phy_port *
135 lookup_port(const struct port_watcher *pw, uint16_t port_no)
136 {
137     return port_array_get(&pw->ports, port_no);
138 }
139
140 static void
141 call_local_port_changed_callbacks(struct port_watcher *pw)
142 {
143     char name[OFP_MAX_PORT_NAME_LEN + 1];
144     const struct ofp_phy_port *port;
145     int i;
146
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);
150
151     /* Log the name of the local port. */
152     if (port) {
153         get_port_name(port, name, sizeof name);
154     } else {
155         name[0] = '\0';
156     }
157     if (strcmp(pw->local_port_name, name)) {
158         if (name[0]) {
159             VLOG_WARN("Identified data path local port as \"%s\".", name);
160         } else {
161             VLOG_WARN("Data path has no local port.");
162         }
163         strcpy(pw->local_port_name, name);
164     }
165
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);
170     }
171 }
172
173 static void
174 update_phy_port(struct port_watcher *pw, struct ofp_phy_port *opp,
175                 uint8_t reason)
176 {
177     struct ofp_phy_port *old;
178     uint16_t port_no;
179
180     port_no = ntohs(opp->port_no);
181     old = lookup_port(pw, port_no);
182
183     if (reason == OFPPR_DELETE && old) {
184         call_port_changed_callbacks(pw, port_no, old, NULL);
185         free(old);
186         port_array_set(&pw->ports, port_no, NULL);
187     } else if (reason == OFPPR_MODIFY || reason == OFPPR_ADD) {
188         if (old) {
189             uint32_t s_mask = htonl(OFPPS_STP_MASK);
190             opp->state = (opp->state & ~s_mask) | (old->state & s_mask);
191         }
192         if (!old || opp_differs(opp, old)) {
193             struct ofp_phy_port new = *opp;
194             sanitize_opp(&new);
195             call_port_changed_callbacks(pw, port_no, old, &new);
196             if (old) {
197                 *old = new;
198             } else {
199                 port_array_set(&pw->ports, port_no, xmemdup(&new, sizeof new));
200             }
201         }
202     }
203 }
204
205 static bool
206 port_watcher_local_packet_cb(struct relay *r, void *pw_)
207 {
208     struct port_watcher *pw = pw_;
209     struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
210     struct ofp_header *oh = msg->data;
211
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;
218         size_t n_ports;
219         size_t i;
220
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));
225         }
226
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;
235         }
236
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);
242             }
243         }
244
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);
252         }
253     }
254     return false;
255 }
256
257 static bool
258 port_watcher_remote_packet_cb(struct relay *r, void *pw_)
259 {
260     struct port_watcher *pw = pw_;
261     struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
262     struct ofp_header *oh = msg->data;
263
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);
276             }
277         }
278     }
279     return false;
280 }
281
282 static void
283 port_watcher_periodic_cb(void *pw_)
284 {
285     struct port_watcher *pw = pw_;
286
287     if (!pw->got_feature_reply
288         && time_now() >= pw->last_feature_request + 5
289         && rconn_is_connected(pw->local_rconn)) {
290         struct ofpbuf *b;
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();
294     }
295 }
296
297 static void
298 port_watcher_wait_cb(void *pw_)
299 {
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());
304         } else {
305             poll_immediate_wake();
306         }
307     }
308 }
309
310 static void
311 put_duplexes(struct ds *ds, const char *name, uint32_t features,
312              uint32_t hd_bit, uint32_t fd_bit)
313 {
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)");
318         }
319         if (features & fd_bit) {
320             ds_put_cstr(ds, "(FD)");
321         }
322     }
323 }
324
325 static void
326 put_features(struct ds *ds, const char *name, uint32_t features)
327 {
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");
338         }
339         if (features & OFPPF_AUTONEG) {
340             ds_put_cstr(ds, " AUTO_NEG");
341         }
342         if (features & OFPPF_PAUSE) {
343             ds_put_cstr(ds, " PAUSE");
344         }
345         if (features & OFPPF_PAUSE_ASYM) {
346             ds_put_cstr(ds, " PAUSE_ASYM");
347         }
348     }
349 }
350
351 static void
352 log_port_status(uint16_t port_no,
353                 const struct ofp_phy_port *old,
354                 const struct ofp_phy_port *new,
355                 void *aux)
356 {
357     if (VLOG_IS_DBG_ENABLED()) {
358         if (old && new && (opp_differs(old, new)
359                            == ((old->config != new->config)
360                                + (old->state != new->state))))
361         {
362             /* Don't care if only state or config changed. */
363         } else if (!new) {
364             if (old) {
365                 VLOG_DBG("Port %d deleted", port_no);
366             }
367         } else {
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));
373             if (curr) {
374                 put_features(&ds, ", current", curr);
375             }
376             if (supported) {
377                 put_features(&ds, ", supports", supported);
378             }
379             VLOG_DBG("Port %d %s: %s",
380                      port_no, old ? "changed" : "added", ds_cstr(&ds));
381             ds_destroy(&ds);
382         }
383     }
384 }
385
386 void
387 port_watcher_register_callback(struct port_watcher *pw,
388                                port_changed_cb_func *port_changed,
389                                void *aux)
390 {
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;
394     pw->n_cbs++;
395 }
396
397 void
398 port_watcher_register_local_port_callback(struct port_watcher *pw,
399                                           local_port_changed_cb_func *cb,
400                                           void *aux)
401 {
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;
405     pw->n_local_cbs++;
406 }
407
408 uint32_t
409 port_watcher_get_config(const struct port_watcher *pw, uint16_t port_no)
410 {
411     struct ofp_phy_port *p = lookup_port(pw, port_no);
412     return p ? ntohl(p->config) : 0;
413 }
414
415 const char *
416 port_watcher_get_name(const struct port_watcher *pw, uint16_t port_no)
417 {
418     struct ofp_phy_port *p = lookup_port(pw, port_no);
419     return p ? (const char *) p->name : NULL;
420 }
421
422 const uint8_t *
423 port_watcher_get_hwaddr(const struct port_watcher *pw, uint16_t port_no) 
424 {
425     struct ofp_phy_port *p = lookup_port(pw, port_no);
426     return p ? p->hw_addr : NULL;
427 }
428
429 void
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)
433 {
434     struct ofp_phy_port old;
435     struct ofp_phy_port *p;
436     struct ofp_port_mod *opm;
437     struct ofp_port_status *ops;
438     struct ofpbuf *b;
439
440     p = lookup_port(pw, port_no);
441     if (!p) {
442         return;
443     }
444
445     if (!((ntohl(p->state) ^ state) & s_mask) 
446             && (!((ntohl(p->config) ^ config) & c_mask))) {
447         return;
448     }
449     old = *p;
450
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);
455
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);
464
465     /* Notify the controller that the flags changed. */
466     ops = make_openflow(sizeof *ops, OFPT_PORT_STATUS, &b);
467     ops->reason = OFPPR_MODIFY;
468     ops->desc = *p;
469     rconn_send(pw->remote_rconn, b, NULL);
470 }
471
472 bool
473 port_watcher_is_ready(const struct port_watcher *pw)
474 {
475     return pw->got_feature_reply;
476 }
477
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 */
484 };
485
486 void
487 port_watcher_start(struct secchan *secchan,
488                    struct rconn *local_rconn, struct rconn *remote_rconn,
489                    struct port_watcher **pwp)
490 {
491     struct port_watcher *pw;
492
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);
501 }