secchan: Implement in-band control using wildcard rules.
authorBen Pfaff <blp@nicira.com>
Wed, 18 Mar 2009 00:02:20 +0000 (17:02 -0700)
committerBen Pfaff <blp@nicira.com>
Wed, 18 Mar 2009 00:25:52 +0000 (17:25 -0700)
Until now, there has been a problem with flows set up by the controller
overriding in-band control decisions: if the controller sets up, say, a
flow that matches every packet, then the in-band control flow miss handler
will never be called, and the connection to the controller may well fail.

This commit fixes the problem by implementing in-band control in terms
of wildcard rules that have a higher priority than any rule that can be
set up by the OpenFlow controller.

This should also fix the issue that in-band control does not respect VLANs
and other structure set up by the vswitch, since the rules set up by
in-band control use OFPP_NORMAL.

secchan/in-band.c
secchan/in-band.h
secchan/ofproto.c

index 0379e7483ed0155d907fcd957813ebea7df6cab5..b84a775110a39381c46b57bd732f052d57427901 100644 (file)
@@ -48,6 +48,7 @@
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "packets.h"
+#include "poll-loop.h"
 #include "rconn.h"
 #include "status.h"
 #include "timeval.h"
 #define THIS_MODULE VLM_in_band
 #include "vlog.h"
 
+#define IB_BASE_PRIORITY 18181800
+
+enum {
+    IBR_FROM_LOCAL_PORT,        /* Sent by secure channel. */
+    IBR_TO_LOCAL_PORT,          /* Sent to secure channel.  */
+    IBR_ARP_FROM_CTL,           /* ARP from the controller. */
+    IBR_TO_CTL_OFP_SRC,         /* To controller, OpenFlow source port. */
+    IBR_TO_CTL_OFP_DST,         /* To controller, OpenFlow dest port. */
+    IBR_FROM_CTL_OFP_SRC,       /* From controller, OpenFlow source port. */
+    IBR_FROM_CTL_OFP_DST,       /* From controller, OpenFlow dest port. */
+#if OFP_TCP_PORT != OFP_SSL_PORT
+#error Need to support separate TCP and SSL flows.
+#endif
+    N_IB_RULES
+};
+
+struct ib_rule {
+    bool installed;
+    flow_t flow;
+    uint32_t wildcards;
+    unsigned int priority;
+};
+
 struct in_band {
-    struct mac_learning *mac_learning;
+    struct ofproto *ofproto;
     struct netdev *netdev;
     struct rconn *controller;
     struct status_category *ss_cat;
@@ -72,6 +96,9 @@ struct in_band {
     /* Keeping track of the local port's MAC address. */
     uint8_t local_mac[ETH_ADDR_LEN]; /* Current MAC. */
     time_t next_local_refresh;  /* Next time to refresh MAC address. */
+
+    /* Rules that we set up. */
+    struct ib_rule rules[N_IB_RULES];
 };
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
@@ -123,14 +150,6 @@ get_controller_mac(struct in_band *ib)
     return !eth_addr_is_zero(ib->mac) ? ib->mac : NULL;
 }
 
-static bool
-is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN],
-                  struct in_band *in_band)
-{
-    const uint8_t *mac = get_controller_mac(in_band);
-    return mac && eth_addr_equals(mac, dl_addr);
-}
-
 static const uint8_t *
 get_local_mac(struct in_band *ib)
 {
@@ -145,94 +164,6 @@ get_local_mac(struct in_band *ib)
     return !eth_addr_is_zero(ib->local_mac) ? ib->local_mac : NULL;
 }
 
-static bool
-is_local_mac(const uint8_t dl_addr[ETH_ADDR_LEN], struct in_band *ib)
-{
-    const uint8_t *local_mac = get_local_mac(ib);
-    return local_mac && eth_addr_equals(dl_addr, local_mac);
-}
-
-static void
-in_band_learn_mac(struct in_band *in_band,
-                  uint16_t in_port, const uint8_t src_mac[ETH_ADDR_LEN])
-{
-    if (mac_learning_learn(in_band->mac_learning, src_mac, 0, in_port)) {
-        VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
-                    ETH_ADDR_ARGS(src_mac), in_port);
-    }
-}
-
-bool
-in_band_handle_flow_miss(struct in_band *in_band, struct ofproto *ofproto,
-                         const flow_t *flow, const struct ofpbuf *payload)
-{
-    /* -1 (FLOOD) is coincidentally the value returned by mac_learning_lookup()
-     * when it doesn't have a entry for that address. */
-    enum { FLOOD = -1, DROP = -2 };
-    union ofp_action action;
-    int out_port;
-
-    /* Deal with local stuff. */
-    if (flow->in_port == ODPP_LOCAL) {
-        /* Sent by secure channel. */
-        out_port = mac_learning_lookup(in_band->mac_learning, flow->dl_dst, 0);
-    } else if (is_local_mac(flow->dl_dst, in_band)) {
-        /* Sent to secure channel. */
-        out_port = ODPP_LOCAL;
-        in_band_learn_mac(in_band, flow->in_port, flow->dl_src);
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP)
-               && eth_addr_is_broadcast(flow->dl_dst)
-               && is_controller_mac(flow->dl_src, in_band)) {
-        /* ARP sent by controller. */
-        out_port = FLOOD;
-    } else if ((is_controller_mac(flow->dl_dst, in_band) ||
-                is_controller_mac(flow->dl_src, in_band))
-               && flow->dl_type == htons(ETH_TYPE_IP)
-               && flow->nw_proto == IP_TYPE_TCP
-               && (flow->tp_src == htons(OFP_TCP_PORT) ||
-                   flow->tp_src == htons(OFP_SSL_PORT) ||
-                   flow->tp_dst == htons(OFP_TCP_PORT) ||
-                   flow->tp_dst == htons(OFP_SSL_PORT))) {
-        /* Traffic to or from controller.  Switch it by hand. */
-        in_band_learn_mac(in_band, flow->in_port, flow->dl_src);
-        out_port = mac_learning_lookup(in_band->mac_learning, flow->dl_dst, 0);
-    } else {
-        const uint8_t *controller_mac = get_controller_mac(in_band);
-        if (flow->dl_type == htons(ETH_TYPE_ARP)
-            && eth_addr_is_broadcast(flow->dl_dst)
-            && is_controller_mac(flow->dl_src, in_band)) {
-            /* ARP sent by controller. */
-            out_port = FLOOD;
-        } else if (is_controller_mac(flow->dl_dst, in_band)
-                   && flow->in_port == mac_learning_lookup(
-                       in_band->mac_learning, controller_mac, 0)) {
-            /* Drop controller traffic that arrives on the controller port. */
-            out_port = DROP;
-        } else {
-            return false;
-        }
-    }
-
-    memset(&action, 0, sizeof action);
-    action.output.type = htons(OFPAT_OUTPUT);
-    action.output.len = htons(sizeof action);
-    if (flow->in_port == out_port || out_port == DROP) {
-        /* Set up a flow to drop packets. */
-        ofproto_add_flow(ofproto, flow, 0, UINT16_MAX, NULL, 0, NULL, -1);
-    } else if (out_port != FLOOD) {
-        /* The output port is known, so add a new flow. */
-        action.output.port = htons(odp_port_to_ofp_port(out_port));
-        ofproto_add_flow(ofproto, flow, 0, UINT16_MAX,
-                         &action, 1, payload, -1);
-    } else {
-        /* We don't know that MAC.  Send along the packet without setting up a
-         * flow. */
-        action.output.port = htons(OFPP_FLOOD);
-        ofproto_send_packet(ofproto, flow, &action, 1, payload);
-    }
-    return true;
-}
-
 static void
 in_band_status_cb(struct status_reply *sr, void *in_band_)
 {
@@ -263,20 +194,132 @@ in_band_status_cb(struct status_reply *sr, void *in_band_)
     }
 }
 
+static void
+drop_flow(struct in_band *in_band, int rule_idx)
+{
+    struct ib_rule *rule = &in_band->rules[rule_idx];
+
+    if (rule->installed) {
+        rule->installed = false;
+        ofproto_delete_flow(in_band->ofproto, &rule->flow, rule->wildcards,
+                            rule->priority);
+    }
+}
+
+/* out_port and fixed_fields are assumed never to change. */
+static void
+setup_flow(struct in_band *in_band, int rule_idx, const flow_t *flow,
+           uint32_t fixed_fields, uint16_t out_port)
+{
+    struct ib_rule *rule = &in_band->rules[rule_idx];
+
+    if (!rule->installed || memcmp(flow, &rule->flow, sizeof *flow)) {
+        union ofp_action action;
+
+        drop_flow(in_band, rule_idx);
+
+        rule->installed = true;
+        rule->flow = *flow;
+        rule->wildcards = OFPFW_ALL & ~fixed_fields;
+        rule->priority = IB_BASE_PRIORITY + (N_IB_RULES - rule_idx);
+
+        action.type = htons(OFPAT_OUTPUT);
+        action.output.len = htons(sizeof action);
+        action.output.port = htons(out_port);
+        action.output.max_len = htons(0);
+        ofproto_add_flow(in_band->ofproto, &rule->flow, rule->wildcards,
+                         rule->priority, &action, 1, NULL, 0);
+    }
+}
+
 void
 in_band_run(struct in_band *in_band)
 {
-    mac_learning_run(in_band->mac_learning, NULL);
+    const uint8_t *controller_mac;
+    const uint8_t *local_mac;
+    flow_t flow;
+
+    if (time_now() < MIN(in_band->next_refresh, in_band->next_local_refresh)) {
+        return;
+    }
+    controller_mac = get_controller_mac(in_band);
+    local_mac = get_local_mac(in_band);
+
+    /* Switch traffic sent by the secure channel. */
+    memset(&flow, 0, sizeof flow);
+    flow.in_port = ODPP_LOCAL;
+    setup_flow(in_band, IBR_FROM_LOCAL_PORT, &flow, OFPFW_IN_PORT,
+               OFPP_NORMAL);
+
+    /* Deliver traffic sent to the secure channel to the local port. */
+    if (local_mac) {
+        memset(&flow, 0, sizeof flow);
+        memcpy(flow.dl_dst, local_mac, ETH_ADDR_LEN);
+        setup_flow(in_band, IBR_TO_LOCAL_PORT, &flow, OFPFW_DL_DST,
+                   OFPP_LOCAL);
+    } else {
+        drop_flow(in_band, IBR_TO_LOCAL_PORT);
+    }
+
+    if (controller_mac) {
+        /* Switch ARP requests sent by the controller.  (OFPP_NORMAL will "do
+         * the right thing" regarding VLANs here.) */
+        memset(&flow, 0, sizeof flow);
+        flow.dl_type = htons(ETH_TYPE_ARP);
+        memcpy(flow.dl_dst, eth_addr_broadcast, ETH_ADDR_LEN);
+        memcpy(flow.dl_src, controller_mac, ETH_ADDR_LEN);
+        setup_flow(in_band, IBR_ARP_FROM_CTL, &flow,
+                   OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_DL_SRC,
+                   OFPP_NORMAL);
+
+        /* OpenFlow traffic to or from the controller.
+         *
+         * (A given field's value is completely ignored if it is wildcarded,
+         * which is why we can get away with using a single 'flow' in each
+         * case here.) */
+        memset(&flow, 0, sizeof flow);
+        flow.dl_type = htons(ETH_TYPE_IP);
+        memcpy(flow.dl_src, controller_mac, ETH_ADDR_LEN);
+        memcpy(flow.dl_dst, controller_mac, ETH_ADDR_LEN);
+        flow.nw_proto = IP_TYPE_TCP;
+        flow.tp_src = htons(OFP_TCP_PORT);
+        flow.tp_dst = htons(OFP_TCP_PORT);
+        setup_flow(in_band, IBR_TO_CTL_OFP_SRC, &flow,
+                   (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO
+                    | OFPFW_TP_SRC), OFPP_NORMAL);
+        setup_flow(in_band, IBR_TO_CTL_OFP_DST, &flow,
+                   (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO
+                    | OFPFW_TP_DST), OFPP_NORMAL);
+        setup_flow(in_band, IBR_FROM_CTL_OFP_SRC, &flow,
+                   (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO
+                    | OFPFW_TP_SRC), OFPP_NORMAL);
+        setup_flow(in_band, IBR_FROM_CTL_OFP_DST, &flow,
+                   (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO
+                    | OFPFW_TP_DST), OFPP_NORMAL);
+    } else {
+        drop_flow(in_band, IBR_ARP_FROM_CTL);
+        drop_flow(in_band, IBR_TO_CTL_OFP_DST);
+        drop_flow(in_band, IBR_TO_CTL_OFP_SRC);
+        drop_flow(in_band, IBR_FROM_CTL_OFP_DST);
+        drop_flow(in_band, IBR_FROM_CTL_OFP_SRC);
+    }
 }
 
 void
 in_band_wait(struct in_band *in_band)
 {
-    mac_learning_wait(in_band->mac_learning);
+    time_t now = time_now();
+    time_t wakeup = MIN(in_band->next_refresh, in_band->next_local_refresh);
+    if (wakeup > now) {
+        poll_timer_wait((wakeup - now) * 1000);
+    } else {
+        poll_immediate_wake();
+    }
 }
 
 int
-in_band_create(struct dpif *dpif, struct switch_status *ss,
+in_band_create(struct ofproto *ofproto,
+               struct dpif *dpif, struct switch_status *ss,
                struct rconn *controller, struct in_band **in_bandp)
 {
     struct in_band *in_band;
@@ -298,7 +341,7 @@ in_band_create(struct dpif *dpif, struct switch_status *ss,
     }
 
     in_band = xcalloc(1, sizeof *in_band);
-    in_band->mac_learning = mac_learning_create();
+    in_band->ofproto = ofproto;
     in_band->netdev = netdev;
     in_band->controller = controller;
     in_band->ss_cat = switch_status_register(ss, "in-band",
@@ -309,13 +352,14 @@ in_band_create(struct dpif *dpif, struct switch_status *ss,
     *in_bandp = in_band;
     return 0;
 }
+
 void
 in_band_destroy(struct in_band *in_band)
 {
     if (in_band) {
-        mac_learning_destroy(in_band->mac_learning);
         netdev_close(in_band->netdev);
         switch_status_unregister(in_band->ss_cat);
         /* We don't own the rconn. */
     }
 }
+
index 570f65bf458658b7bbb7bb74b4c0032b8b96b80e..979580c7dd960e21fbb8c41074d7aed165601918 100644 (file)
@@ -44,12 +44,10 @@ struct secchan;
 struct settings;
 struct switch_status;
 
-int in_band_create(struct dpif *, struct switch_status *,
+int in_band_create(struct ofproto *, struct dpif *, struct switch_status *,
                    struct rconn *controller, struct in_band **);
 void in_band_destroy(struct in_band *);
 void in_band_run(struct in_band *);
 void in_band_wait(struct in_band *);
-bool in_band_handle_flow_miss(struct in_band *, struct ofproto *,
-                              const flow_t *, const struct ofpbuf *payload);
 
 #endif /* in-band.h */
index 6a932e94807c3ef65e2308ce1d293534e9f24938..a47928309b86d3a452710735961a9a13ba85e7f4 100644 (file)
@@ -2721,12 +2721,6 @@ handle_odp_msg(struct ofproto *p, struct ofpbuf *packet)
     if (!rule) {
         struct ofport *port;
 
-        if (p->in_band && in_band_handle_flow_miss(p->in_band, p,
-                                                   &flow, &payload)) {
-            ofpbuf_delete(packet);
-            return;
-        }
-
         if (p->fail_open && fail_open_handle_flow_miss(p->fail_open, p,
                                                        &flow, &payload)) {
             ofpbuf_delete(packet);