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 "stp-secchan.h"
36 #include <arpa/inet.h>
41 #include "openflow/openflow.h"
42 #include "poll-loop.h"
43 #include "port-watcher.h"
49 #define THIS_MODULE VLM_stp_secchan
54 struct port_watcher *pw;
55 struct rconn *local_rconn;
56 struct rconn *remote_rconn;
57 long long int last_tick_256ths;
61 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
64 stp_local_packet_cb(struct relay *r, void *stp_)
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;
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);
84 if (!get_ofp_packet_eth_header(r, &opi, ð)
85 || !eth_addr_equals(eth->eth_dst, stp_eth_addr)) {
89 port_no = ntohs(opi->in_port);
90 if (port_no >= STP_MAX_PORTS) {
91 /* STP only supports 255 ports. */
94 if (port_watcher_get_config(stp->pw, port_no) & OFPPC_NO_STP) {
95 /* We're not doing STP on this port. */
99 if (opi->reason == OFPR_ACTION) {
100 /* The controller set up a flow for this, so we won't intercept it. */
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");
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",
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;
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);
132 return time_msec() * 256 / 1000;
136 stp_periodic_cb(void *stp_)
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;
143 if (!port_watcher_is_ready(stp->pw)) {
144 /* Can't start STP until we know port flags, because port flags can
148 if (elapsed_256ths <= 0) {
152 stp_tick(stp->stp, MIN(INT_MAX, elapsed_256ths));
153 stp->last_tick_256ths = now_256ths;
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);
159 if (s_state != STP_DISABLED) {
160 VLOG_WARN("STP: Port %d entered %s state",
161 port_no, stp_state_name(s_state));
163 if (!(port_watcher_get_config(stp->pw, port_no) & OFPPC_NO_STP)) {
164 uint32_t p_config = 0;
168 p_state = OFPPS_STP_LISTEN;
171 p_state = OFPPS_STP_LEARN;
175 p_state = OFPPS_STP_FORWARD;
178 p_state = OFPPS_STP_BLOCK;
181 VLOG_DBG_RL(&rl, "STP: Port %d has bad state %x",
183 p_state = OFPPS_STP_FORWARD;
186 if (!stp_forward_in_state(s_state)) {
187 p_config = OFPPC_NO_FLOOD;
189 port_watcher_set_flags(stp->pw, port_no,
190 p_config, OFPPC_NO_FLOOD,
191 p_state, OFPPS_STP_MASK);
193 /* We don't own those flags. */
199 stp_wait_cb(void *stp_ UNUSED)
201 poll_timer_wait(1000);
205 send_bpdu(const void *bpdu, size_t bpdu_size, int port_no, void *stp_)
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;
213 port_mac = port_watcher_get_hwaddr(stp->pw, port_no);
215 VLOG_WARN_RL(&rl, "cannot send BPDU on missing port %d", port_no);
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);
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);
231 llc->llc_dsap = STP_LLC_DSAP;
232 llc->llc_ssap = STP_LLC_SSAP;
233 llc->llc_cntl = STP_LLC_CNTL;
235 opo = make_unbuffered_packet_out(&pkt, OFPP_NONE, port_no);
237 rconn_send_with_limit(stp->local_rconn, opo, &stp->n_txq, OFPP_MAX);
241 stp_is_port_supported(uint16_t port_no)
243 return port_no < STP_MAX_PORTS;
247 stp_port_changed_cb(uint16_t port_no,
248 const struct ofp_phy_port *old,
249 const struct ofp_phy_port *new,
252 struct stp_data *stp = stp_;
255 if (!stp_is_port_supported(port_no)) {
259 p = stp_get_port(stp->stp, port_no);
261 || new->config & htonl(OFPPC_NO_STP | OFPPC_PORT_DOWN)
262 || new->state & htonl(OFPPS_LINK_DOWN)) {
267 if (new->curr & (OFPPF_10MB_HD | OFPPF_10MB_FD)) {
269 } else if (new->curr & (OFPPF_100MB_HD | OFPPF_100MB_FD)) {
271 } else if (new->curr & (OFPPF_1GB_HD | OFPPF_1GB_FD)) {
273 } else if (new->curr & OFPPF_10GB_FD) {
276 stp_port_set_speed(p, speed);
281 stp_local_port_changed_cb(const struct ofp_phy_port *port, void *stp_)
283 struct stp_data *stp = stp_;
285 stp_set_bridge_id(stp->stp, eth_addr_to_uint64(port->hw_addr));
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 */
298 stp_start(struct secchan *secchan, const struct settings *s,
299 struct port_watcher *pw,
300 struct rconn *local, struct rconn *remote)
302 uint8_t dpid[ETH_ADDR_LEN];
303 struct stp_data *stp;
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);
309 stp->local_rconn = local;
310 stp->remote_rconn = remote;
311 stp->last_tick_256ths = time_256ths();
313 port_watcher_register_callback(pw, stp_port_changed_cb, stp);
314 port_watcher_register_local_port_callback(pw, stp_local_port_changed_cb,
316 add_hook(secchan, &stp_hook_class, stp);