Drop controller-bound traffic that arrives on the controller's port.
authorBen Pfaff <blp@nicira.com>
Tue, 5 Aug 2008 20:05:56 +0000 (13:05 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 7 Aug 2008 17:10:11 +0000 (10:10 -0700)
Before, if a hub connected a number of OpenFlow switches and the controller,
then in-band control traffic from one of the OpenFlow switches would be
seen by each of the other switches and forwarded up to the controller as
an ofp_packet_in message.  That message would then be seen by all of the
other OpenFlow switches and also forwarded, and so on in an infinite loop.

This change prevents this situation by keeping secchan from forwarding
ofp_packet_in messages for a packet destined to the controller that
arrives on the port where the controller is located.

This code has at least two weaknesses.  First, if the controller's port
changes, then the flows set up to drop packets will not be deleted.  This
should not be a major problem: if this inadvertently kills a switch's
connection to the controller, then the switch will realize it after it
stops receiving data and re-connect.  Its new connection will have new
flow data and therefore its packets will not be dropped.

Second, the notion of the "controller's port" does not take into account
the possibility of loops in the network topology.  We need spanning tree
protocol for that.

include/vconn.h
lib/vconn.c
secchan/secchan.c

index 4b0eaa7..22a1da8 100644 (file)
@@ -83,6 +83,8 @@ void *make_openflow(size_t openflow_len, uint8_t type, struct buffer **);
 void *make_openflow_xid(size_t openflow_len, uint8_t type,
                         uint32_t xid, struct buffer **);
 void update_openflow_length(struct buffer *);
+struct buffer *make_add_flow(const struct flow *, uint32_t buffer_id,
+                             uint16_t max_idle, size_t n_actions);
 struct buffer *make_add_simple_flow(const struct flow *,
                                     uint32_t buffer_id, uint16_t out_port,
                                     uint16_t max_idle);
index 7af59f3..332f5de 100644 (file)
@@ -488,11 +488,11 @@ update_openflow_length(struct buffer *buffer)
 }
 
 struct buffer *
-make_add_simple_flow(const struct flow *flow,
-                     uint32_t buffer_id, uint16_t out_port, uint16_t max_idle)
+make_add_flow(const struct flow *flow, uint32_t buffer_id, uint16_t max_idle,
+              size_t n_actions)
 {
     struct ofp_flow_mod *ofm;
-    size_t size = sizeof *ofm + sizeof ofm->actions[0];
+    size_t size = sizeof *ofm + n_actions * sizeof ofm->actions[0];
     struct buffer *out = buffer_new(size);
     ofm = buffer_put_uninit(out, size);
     memset(ofm, 0, size);
@@ -513,10 +513,19 @@ make_add_simple_flow(const struct flow *flow,
     ofm->command = htons(OFPFC_ADD);
     ofm->max_idle = htons(max_idle);
     ofm->buffer_id = htonl(buffer_id);
+    return out;
+}
+
+struct buffer *
+make_add_simple_flow(const struct flow *flow,
+                     uint32_t buffer_id, uint16_t out_port, uint16_t max_idle)
+{
+    struct buffer *buffer = make_add_flow(flow, buffer_id, max_idle, 1);
+    struct ofp_flow_mod *ofm = buffer->data;
     ofm->actions[0].type = htons(OFPAT_OUTPUT);
     ofm->actions[0].arg.output.max_len = htons(0);
     ofm->actions[0].arg.output.port = htons(out_port);
-    return out;
+    return buffer;
 }
 
 struct buffer *
index 50ebebb..3053198 100644 (file)
@@ -451,9 +451,8 @@ queue_tx(struct rconn *rc, struct buffer *b)
     }
 }
 
-static bool
-is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN], struct netdev *netdev,
-                  struct rconn *controller)
+static const uint8_t *
+get_controller_mac(struct netdev *netdev, struct rconn *controller)
 {
     static uint32_t ip, last_nonzero_ip;
     static uint8_t mac[ETH_ADDR_LEN], last_nonzero_mac[ETH_ADDR_LEN];
@@ -498,7 +497,14 @@ is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN], struct netdev *netdev,
          * Otherwise, we can afford to wait a little while. */
         next_refresh = now + (!ip || have_mac ? 10 : 1);
     }
-    return !eth_addr_is_zero(mac) && eth_addr_equals(mac, dl_addr);
+    return !eth_addr_is_zero(mac) ? mac : NULL;
+}
+
+static bool
+is_controller_mac(const uint8_t mac[ETH_ADDR_LEN],
+                  const uint8_t *controller_mac)
+{
+    return controller_mac && eth_addr_equals(mac, controller_mac);
 }
 
 static bool
@@ -513,6 +519,7 @@ in_band_packet_cb(struct relay *r, int half, void *in_band_)
     struct buffer pkt;
     struct flow flow;
     uint16_t in_port, out_port;
+    const uint8_t *controller_mac;
 
     if (half != HALF_LOCAL || r->is_mgmt_conn) {
         return false;
@@ -537,9 +544,13 @@ in_band_packet_cb(struct relay *r, int half, void *in_band_)
     flow_extract(&pkt, in_port, &flow);
 
     /* Deal with local stuff. */
+    controller_mac = get_controller_mac(in_band->of_device,
+                                        r->halves[HALF_REMOTE].rconn);
     if (in_port == OFPP_LOCAL) {
+        /* Sent by secure channel. */
         out_port = mac_learning_lookup(in_band->ml, flow.dl_dst);
     } else if (eth_addr_equals(flow.dl_dst, in_band->mac)) {
+        /* Sent to secure channel. */
         out_port = OFPP_LOCAL;
         if (mac_learning_learn(in_band->ml, flow.dl_src, in_port)) {
             VLOG_DBG("learned that "ETH_ADDR_FMT" is on port %"PRIu16,
@@ -547,9 +558,16 @@ in_band_packet_cb(struct relay *r, int half, void *in_band_)
         }
     } else if (flow.dl_type == htons(ETH_TYPE_ARP)
                && eth_addr_is_broadcast(flow.dl_dst)
-               && is_controller_mac(flow.dl_src, in_band->of_device,
-                                    r->halves[HALF_REMOTE].rconn)) {
+               && is_controller_mac(flow.dl_src, controller_mac)) {
+        /* ARP sent by controller. */
         out_port = OFPP_FLOOD;
+    } else if (is_controller_mac(flow.dl_dst, controller_mac)
+               && in_port == mac_learning_lookup(in_band->ml,
+                                                 controller_mac)) {
+        /* Drop controller traffic that arrives on the controller port. */
+        queue_tx(rc, make_add_flow(&flow, ntohl(opi->buffer_id),
+                                   in_band->s->max_idle, 0));
+        return true;
     } else {
         return false;
     }