Never free an skb that has been passed to genlmsg_reply().
[sliver-openvswitch.git] / secchan / stp-secchan.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 "stp-secchan.h"
36 #include <arpa/inet.h>
37 #include <inttypes.h>
38 #include "flow.h"
39 #include "secchan.h"
40 #include "ofpbuf.h"
41 #include "openflow/openflow.h"
42 #include "poll-loop.h"
43 #include "port-watcher.h"
44 #include "rconn.h"
45 #include "stp.h"
46 #include "timeval.h"
47 #include "vconn.h"
48
49 #define THIS_MODULE VLM_stp_secchan
50 #include "vlog.h"
51
52 struct stp_data {
53     struct stp *stp;
54     struct port_watcher *pw;
55     struct rconn *local_rconn;
56     struct rconn *remote_rconn;
57     long long int last_tick_256ths;
58     int n_txq;
59 };
60
61 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
62
63 static bool
64 stp_local_packet_cb(struct relay *r, void *stp_)
65 {
66     struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
67     struct ofp_header *oh;
68     struct stp_data *stp = stp_;
69     struct ofp_packet_in *opi;
70     struct eth_header *eth;
71     struct llc_header *llc;
72     struct ofpbuf payload;
73     uint16_t port_no;
74     struct flow flow;
75
76     oh = msg->data;
77     if (oh->type == OFPT_FEATURES_REPLY
78         && msg->size >= offsetof(struct ofp_switch_features, ports)) {
79         struct ofp_switch_features *osf = msg->data;
80         osf->capabilities |= htonl(OFPC_STP);
81         return false;
82     }
83
84     if (!get_ofp_packet_eth_header(r, &opi, &eth)
85         || !eth_addr_equals(eth->eth_dst, stp_eth_addr)) {
86         return false;
87     }
88
89     port_no = ntohs(opi->in_port);
90     if (port_no >= STP_MAX_PORTS) {
91         /* STP only supports 255 ports. */
92         return false;
93     }
94     if (port_watcher_get_config(stp->pw, port_no) & OFPPC_NO_STP) {
95         /* We're not doing STP on this port. */
96         return false;
97     }
98
99     if (opi->reason == OFPR_ACTION) {
100         /* The controller set up a flow for this, so we won't intercept it. */
101         return false;
102     }
103
104     get_ofp_packet_payload(opi, &payload);
105     flow_extract(&payload, port_no, &flow);
106     if (flow.dl_type != htons(OFP_DL_TYPE_NOT_ETH_TYPE)) {
107         VLOG_DBG("non-LLC frame received on STP multicast address");
108         return false;
109     }
110     llc = ofpbuf_at_assert(&payload, sizeof *eth, sizeof *llc);
111     if (llc->llc_dsap != STP_LLC_DSAP) {
112         VLOG_DBG("bad DSAP 0x%02"PRIx8" received on STP multicast address",
113                  llc->llc_dsap);
114         return false;
115     }
116
117     /* Trim off padding on payload. */
118     if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
119         payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN;
120     }
121     if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
122         struct stp_port *p = stp_get_port(stp->stp, port_no);
123         stp_received_bpdu(p, payload.data, payload.size);
124     }
125
126     return true;
127 }
128
129 static long long int
130 time_256ths(void)
131 {
132     return time_msec() * 256 / 1000;
133 }
134
135 static void
136 stp_periodic_cb(void *stp_)
137 {
138     struct stp_data *stp = stp_;
139     long long int now_256ths = time_256ths();
140     long long int elapsed_256ths = now_256ths - stp->last_tick_256ths;
141     struct stp_port *p;
142
143     if (!port_watcher_is_ready(stp->pw)) {
144         /* Can't start STP until we know port flags, because port flags can
145          * disable STP. */
146         return;
147     }
148     if (elapsed_256ths <= 0) {
149         return;
150     }
151
152     stp_tick(stp->stp, MIN(INT_MAX, elapsed_256ths));
153     stp->last_tick_256ths = now_256ths;
154
155     while (stp_get_changed_port(stp->stp, &p)) {
156         int port_no = stp_port_no(p);
157         enum stp_state s_state = stp_port_get_state(p);
158
159         if (s_state != STP_DISABLED) {
160             VLOG_WARN("STP: Port %d entered %s state",
161                       port_no, stp_state_name(s_state));
162         }
163         if (!(port_watcher_get_config(stp->pw, port_no) & OFPPC_NO_STP)) {
164             uint32_t p_config = 0;
165             uint32_t p_state;
166             switch (s_state) {
167             case STP_LISTENING:
168                 p_state = OFPPS_STP_LISTEN;
169                 break;
170             case STP_LEARNING:
171                 p_state = OFPPS_STP_LEARN;
172                 break;
173             case STP_DISABLED:
174             case STP_FORWARDING:
175                 p_state = OFPPS_STP_FORWARD;
176                 break;
177             case STP_BLOCKING:
178                 p_state = OFPPS_STP_BLOCK;
179                 break;
180             default:
181                 VLOG_DBG_RL(&rl, "STP: Port %d has bad state %x",
182                             port_no, s_state);
183                 p_state = OFPPS_STP_FORWARD;
184                 break;
185             }
186             if (!stp_forward_in_state(s_state)) {
187                 p_config = OFPPC_NO_FLOOD;
188             }
189             port_watcher_set_flags(stp->pw, port_no, 
190                                    p_config, OFPPC_NO_FLOOD,
191                                    p_state, OFPPS_STP_MASK);
192         } else {
193             /* We don't own those flags. */
194         }
195     }
196 }
197
198 static void
199 stp_wait_cb(void *stp_ UNUSED)
200 {
201     poll_timer_wait(1000);
202 }
203
204 static void
205 send_bpdu(const void *bpdu, size_t bpdu_size, int port_no, void *stp_)
206 {
207     struct stp_data *stp = stp_;
208     const uint8_t *port_mac;
209     struct eth_header *eth;
210     struct llc_header *llc;
211     struct ofpbuf pkt, *opo;
212
213     port_mac = port_watcher_get_hwaddr(stp->pw, port_no);
214     if (!port_mac) {
215         VLOG_WARN_RL(&rl, "cannot send BPDU on missing port %d", port_no);
216         return;
217     }
218
219     /* Packet skeleton. */
220     ofpbuf_init(&pkt, ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
221     eth = ofpbuf_put_uninit(&pkt, sizeof *eth);
222     llc = ofpbuf_put_uninit(&pkt, sizeof *llc);
223     ofpbuf_put(&pkt, bpdu, bpdu_size);
224
225     /* 802.2 header. */
226     memcpy(eth->eth_dst, stp_eth_addr, ETH_ADDR_LEN);
227     memcpy(eth->eth_src, port_mac, ETH_ADDR_LEN);
228     eth->eth_type = htons(pkt.size - ETH_HEADER_LEN);
229
230     /* LLC header. */
231     llc->llc_dsap = STP_LLC_DSAP;
232     llc->llc_ssap = STP_LLC_SSAP;
233     llc->llc_cntl = STP_LLC_CNTL;
234
235     opo = make_unbuffered_packet_out(&pkt, OFPP_NONE, port_no);
236     ofpbuf_uninit(&pkt);
237     rconn_send_with_limit(stp->local_rconn, opo, &stp->n_txq, OFPP_MAX);
238 }
239
240 static bool
241 stp_is_port_supported(uint16_t port_no)
242 {
243     return port_no < STP_MAX_PORTS;
244 }
245
246 static void
247 stp_port_changed_cb(uint16_t port_no,
248                     const struct ofp_phy_port *old,
249                     const struct ofp_phy_port *new,
250                     void *stp_)
251 {
252     struct stp_data *stp = stp_;
253     struct stp_port *p;
254
255     if (!stp_is_port_supported(port_no)) {
256         return;
257     }
258
259     p = stp_get_port(stp->stp, port_no);
260     if (!new
261         || new->config & htonl(OFPPC_NO_STP | OFPPC_PORT_DOWN)
262         || new->state & htonl(OFPPS_LINK_DOWN)) {
263         stp_port_disable(p);
264     } else {
265         int speed = 0;
266         stp_port_enable(p);
267         if (new->curr & (OFPPF_10MB_HD | OFPPF_10MB_FD)) {
268             speed = 10;
269         } else if (new->curr & (OFPPF_100MB_HD | OFPPF_100MB_FD)) {
270             speed = 100;
271         } else if (new->curr & (OFPPF_1GB_HD | OFPPF_1GB_FD)) {
272             speed = 1000;
273         } else if (new->curr & OFPPF_100MB_FD) {
274             speed = 10000;
275         }
276         stp_port_set_speed(p, speed);
277     }
278 }
279
280 static void
281 stp_local_port_changed_cb(const struct ofp_phy_port *port, void *stp_)
282 {
283     struct stp_data *stp = stp_;
284     if (port) {
285         stp_set_bridge_id(stp->stp, eth_addr_to_uint64(port->hw_addr));
286     }
287 }
288
289 static struct hook_class stp_hook_class = {
290     stp_local_packet_cb,        /* local_packet_cb */
291     NULL,                       /* remote_packet_cb */
292     stp_periodic_cb,            /* periodic_cb */
293     stp_wait_cb,                /* wait_cb */
294     NULL,                       /* closing_cb */
295 };
296
297 void
298 stp_start(struct secchan *secchan, const struct settings *s,
299           struct port_watcher *pw,
300           struct rconn *local, struct rconn *remote)
301 {
302     uint8_t dpid[ETH_ADDR_LEN];
303     struct stp_data *stp;
304
305     stp = xcalloc(1, sizeof *stp);
306     eth_addr_random(dpid);
307     stp->stp = stp_create("stp", eth_addr_to_uint64(dpid), send_bpdu, stp);
308     stp->pw = pw;
309     stp->local_rconn = local;
310     stp->remote_rconn = remote;
311     stp->last_tick_256ths = time_256ths();
312
313     port_watcher_register_callback(pw, stp_port_changed_cb, stp);
314     port_watcher_register_local_port_callback(pw, stp_local_port_changed_cb,
315                                               stp);
316     add_hook(secchan, &stp_hook_class, stp);
317 }