Set DHCP maximum retransmission timeout to 3 seconds in secchan.
[sliver-openvswitch.git] / lib / learning-switch.c
index e118405..cc7f139 100644 (file)
@@ -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"
@@ -61,6 +62,7 @@ struct lswitch {
     int max_idle;
 
     uint64_t datapath_id;
+    uint32_t capabilities;
     time_t last_features_request;
     struct mac_learning *ml;    /* NULL to act as hub instead of switch. */
 
@@ -74,10 +76,16 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
 
 static void queue_tx(struct lswitch *, struct rconn *, struct ofpbuf *);
 static void send_features_request(struct lswitch *, struct rconn *);
+static void process_switch_features(struct lswitch *, struct rconn *,
+                                    struct ofp_switch_features *);
 static void process_packet_in(struct lswitch *, struct rconn *,
                               struct ofp_packet_in *);
 static void process_echo_request(struct lswitch *, struct rconn *,
                                  struct ofp_header *);
+static void process_port_status(struct lswitch *, struct rconn *,
+                                struct ofp_port_status *);
+static void process_phy_port(struct lswitch *, struct rconn *,
+                             const struct ofp_phy_port *);
 
 /* Creates and returns a new learning switch.
  *
@@ -116,6 +124,7 @@ min_size(uint8_t type)
 {
     return (type == OFPT_FEATURES_REPLY ? sizeof(struct ofp_switch_features)
             : type == OFPT_PACKET_IN ? offsetof (struct ofp_packet_in, data)
+            : type == OFPT_PORT_STATUS ? sizeof(struct ofp_port_status)
             : sizeof(struct ofp_header));
 }
 
@@ -141,12 +150,13 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
     if (oh->type == OFPT_ECHO_REQUEST) {
         process_echo_request(sw, rconn, msg->data);
     } else if (oh->type == OFPT_FEATURES_REPLY) {
-        struct ofp_switch_features *osf = msg->data;
-        sw->datapath_id = osf->datapath_id;
+        process_switch_features(sw, rconn, msg->data);
     } else if (sw->datapath_id == 0) {
         send_features_request(sw, rconn);
     } else if (oh->type == OFPT_PACKET_IN) {
         process_packet_in(sw, rconn, msg->data);
+    } else if (oh->type == OFPT_PORT_STATUS) {
+        process_port_status(sw, rconn, msg->data);
     } else {
         if (VLOG_IS_DBG_ENABLED()) {
             char *p = ofp_to_string(msg->data, msg->size, 2);
@@ -162,25 +172,14 @@ send_features_request(struct lswitch *sw, struct rconn *rconn)
     time_t now = time_now();
     if (now >= sw->last_features_request + 1) {
         struct ofpbuf *b;
-        struct ofp_header *ofr;
         struct ofp_switch_config *osc;
 
         /* Send OFPT_FEATURES_REQUEST. */
-        b = ofpbuf_new(0);
-        ofr = ofpbuf_put_uninit(b, sizeof *ofr);
-        memset(ofr, 0, sizeof *ofr);
-        ofr->type = OFPT_FEATURES_REQUEST;
-        ofr->version = OFP_VERSION;
-        ofr->length = htons(sizeof *ofr);
+        make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &b);
         queue_tx(sw, rconn, b);
 
         /* Send OFPT_SET_CONFIG. */
-        b = ofpbuf_new(0);
-        osc = ofpbuf_put_uninit(b, sizeof *osc);
-        memset(osc, 0, sizeof *osc);
-        osc->header.type = OFPT_SET_CONFIG;
-        osc->header.version = OFP_VERSION;
-        osc->header.length = htons(sizeof *osc);
+        osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &b);
         osc->flags = htons(OFPC_SEND_FLOW_EXP);
         osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
         queue_tx(sw, rconn, b);
@@ -203,6 +202,22 @@ queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b)
     }
 }
 
+static void
+process_switch_features(struct lswitch *sw, struct rconn *rconn,
+                        struct ofp_switch_features *osf)
+{
+    size_t n_ports = ((ntohs(osf->header.length)
+                       - offsetof(struct ofp_switch_features, ports))
+                      / sizeof *osf->ports);
+    size_t i;
+
+    sw->datapath_id = osf->datapath_id;
+    sw->capabilities = ntohl(osf->capabilities);
+    for (i = 0; i < n_ports; i++) {
+        process_phy_port(sw, rconn, &osf->ports[i]);
+    }
+}
+
 static void
 process_packet_in(struct lswitch *sw, struct rconn *rconn,
                   struct ofp_packet_in *opi)
@@ -265,3 +280,66 @@ process_echo_request(struct lswitch *sw, struct rconn *rconn,
 {
     queue_tx(sw, rconn, make_echo_reply(rq));
 }
+
+static void
+process_port_status(struct lswitch *sw, struct rconn *rconn,
+                    struct ofp_port_status *ops)
+{
+    process_phy_port(sw, rconn, &ops->desc);
+}
+
+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) < 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
+                                         | OFPPC_NO_FWD | OFPPC_NO_PACKET_IN);
+        if (!(config & (OFPPC_NO_STP | OFPPC_PORT_DOWN))
+                    && !(state & OFPPS_LINK_DOWN)) {
+            bool forward = false;
+            bool learn = false;
+            switch (state & OFPPS_STP_MASK) {
+            case OFPPS_STP_LISTEN:
+            case OFPPS_STP_BLOCK:
+                break;
+            case OFPPS_STP_LEARN:
+                learn = true;
+                break;
+            case OFPPS_STP_FORWARD:
+                forward = learn = true;
+                break;
+            }
+            if (!forward) {
+                new_config |= OFPPC_NO_RECV | OFPPC_NO_FWD;
+            }
+            if (!learn) {
+                new_config |= OFPPC_NO_PACKET_IN;
+            }
+        }
+        if (config != new_config) {
+            struct ofp_port_mod *opm;
+            struct ofpbuf *b;
+            int retval;
+
+            VLOG_WARN("port %d: config=%x new_config=%x",
+                      ntohs(opp->port_no), config, new_config);
+            opm = make_openflow(sizeof *opm, OFPT_PORT_MOD, &b);
+            opm->port_no = opp->port_no;
+            memcpy(opm->hw_addr, opp->hw_addr, OFP_ETH_ALEN);
+            opm->config = htonl(new_config);
+            opm->mask = htonl(config ^ new_config);
+            opm->advertise = htonl(0);
+            retval = rconn_send(rconn, b, NULL);
+            if (retval) {
+                if (retval != ENOTCONN) {
+                    VLOG_WARN_RL(&rl, "%s: send: %s",
+                                 rconn_get_name(rconn), strerror(retval));
+                }
+                ofpbuf_delete(b);
+            }
+        }
+    }
+}