meta-flow: Correctly set destination MAC in mf_set_flow_value().
[sliver-openvswitch.git] / ofproto / in-band.c
index e04ae60..cd9c050 100644 (file)
 #include <stdlib.h>
 #include "classifier.h"
 #include "dhcp.h"
-#include "dpif.h"
 #include "flow.h"
 #include "netdev.h"
 #include "netlink.h"
 #include "odp-util.h"
 #include "ofproto.h"
 #include "ofpbuf.h"
+#include "ofproto-provider.h"
 #include "openflow/openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
-#include "private.h"
 #include "timeval.h"
 #include "vlog.h"
 
@@ -73,9 +72,21 @@ struct in_band_remote {
     struct netdev *remote_netdev; /* Device to send to next-hop MAC. */
 };
 
+/* What to do to an in_band_rule. */
+enum in_band_op {
+    ADD,                       /* Add the rule to ofproto's flow table. */
+    DELETE                     /* Delete the rule from ofproto's flow table. */
+};
+
+/* A rule to add to or delete from ofproto's flow table.  */
+struct in_band_rule {
+    struct cls_rule cls_rule;
+    enum in_band_op op;
+};
+
 struct in_band {
     struct ofproto *ofproto;
-    int queue_id, prev_queue_id;
+    int queue_id;
 
     /* Remote information. */
     time_t next_remote_refresh; /* Refresh timer. */
@@ -87,12 +98,8 @@ struct in_band {
     uint8_t local_mac[ETH_ADDR_LEN]; /* Current MAC. */
     struct netdev *local_netdev;     /* Local port's network device. */
 
-    /* Local and remote addresses that are installed as flows. */
-    uint8_t installed_local_mac[ETH_ADDR_LEN];
-    struct sockaddr_in *remote_addrs;
-    size_t n_remote_addrs;
-    uint8_t *remote_macs;
-    size_t n_remote_macs;
+    /* Flow tracking. */
+    struct hmap rules;          /* Contains "struct in_band_rule"s. */
 };
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
@@ -123,7 +130,7 @@ refresh_remote(struct in_band *ib, struct in_band_remote *r)
     {
         netdev_close(r->remote_netdev);
 
-        retval = netdev_open_default(next_hop_dev, &r->remote_netdev);
+        retval = netdev_open(next_hop_dev, "system", &r->remote_netdev);
         if (retval) {
             VLOG_WARN_RL(&rl, "cannot open netdev %s (next hop "
                          "to controller "IP_FMT"): %s",
@@ -259,8 +266,8 @@ in_band_rule_check(const struct flow *flow,
         unsigned int left;
 
         NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
-            if (nl_attr_type(a) == ODP_ACTION_ATTR_OUTPUT
-                && nl_attr_get_u32(a) == ODPP_LOCAL) {
+            if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT
+                && nl_attr_get_u32(a) == OVSP_LOCAL) {
                 return true;
             }
         }
@@ -271,47 +278,68 @@ in_band_rule_check(const struct flow *flow,
 }
 
 static void
-make_rules(struct in_band *ib,
-           void (*cb)(struct in_band *, const struct cls_rule *))
+add_rule(struct in_band *ib, const struct cls_rule *cls_rule)
 {
+    uint32_t hash = cls_rule_hash(cls_rule, 0);
+    struct in_band_rule *rule;
+
+    HMAP_FOR_EACH_WITH_HASH (rule, cls_rule.hmap_node, hash, &ib->rules) {
+        if (cls_rule_equal(&rule->cls_rule, cls_rule)) {
+            rule->op = ADD;
+            return;
+        }
+    }
+
+    rule = xmalloc(sizeof *rule);
+    rule->cls_rule = *cls_rule;
+    rule->op = ADD;
+    hmap_insert(&ib->rules, &rule->cls_rule.hmap_node, hash);
+}
+
+static void
+update_rules(struct in_band *ib)
+{
+    struct in_band_rule *ib_rule;
+    struct in_band_remote *r;
     struct cls_rule rule;
-    size_t i;
 
-    if (!eth_addr_is_zero(ib->installed_local_mac)) {
+    /* Mark all the existing rules for deletion.  (Afterward we will re-add any
+     * rules that are still valid.) */
+    HMAP_FOR_EACH (ib_rule, cls_rule.hmap_node, &ib->rules) {
+        ib_rule->op = DELETE;
+    }
+
+    if (ib->n_remotes && !eth_addr_is_zero(ib->local_mac)) {
         /* (a) Allow DHCP requests sent from the local port. */
         cls_rule_init_catchall(&rule, IBR_FROM_LOCAL_DHCP);
-        cls_rule_set_in_port(&rule, ODPP_LOCAL);
+        cls_rule_set_in_port(&rule, OFPP_LOCAL);
         cls_rule_set_dl_type(&rule, htons(ETH_TYPE_IP));
-        cls_rule_set_dl_src(&rule, ib->installed_local_mac);
+        cls_rule_set_dl_src(&rule, ib->local_mac);
         cls_rule_set_nw_proto(&rule, IPPROTO_UDP);
         cls_rule_set_tp_src(&rule, htons(DHCP_CLIENT_PORT));
         cls_rule_set_tp_dst(&rule, htons(DHCP_SERVER_PORT));
-        cb(ib, &rule);
+        add_rule(ib, &rule);
 
         /* (b) Allow ARP replies to the local port's MAC address. */
         cls_rule_init_catchall(&rule, IBR_TO_LOCAL_ARP);
         cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-        cls_rule_set_dl_dst(&rule, ib->installed_local_mac);
+        cls_rule_set_dl_dst(&rule, ib->local_mac);
         cls_rule_set_nw_proto(&rule, ARP_OP_REPLY);
-        cb(ib, &rule);
+        add_rule(ib, &rule);
 
         /* (c) Allow ARP requests from the local port's MAC address.  */
         cls_rule_init_catchall(&rule, IBR_FROM_LOCAL_ARP);
         cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-        cls_rule_set_dl_src(&rule, ib->installed_local_mac);
+        cls_rule_set_dl_src(&rule, ib->local_mac);
         cls_rule_set_nw_proto(&rule, ARP_OP_REQUEST);
-        cb(ib, &rule);
+        add_rule(ib, &rule);
     }
 
-    for (i = 0; i < ib->n_remote_macs; i++) {
-        const uint8_t *remote_mac = &ib->remote_macs[i * ETH_ADDR_LEN];
+    for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) {
+        const uint8_t *remote_mac = r->remote_mac;
 
-        if (i > 0) {
-            const uint8_t *prev_mac = &ib->remote_macs[(i - 1) * ETH_ADDR_LEN];
-            if (eth_addr_equals(remote_mac, prev_mac)) {
-                /* Skip duplicates. */
-                continue;
-            }
+        if (eth_addr_is_zero(remote_mac)) {
+            continue;
         }
 
         /* (d) Allow ARP replies to the next hop's MAC address. */
@@ -319,185 +347,111 @@ make_rules(struct in_band *ib,
         cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
         cls_rule_set_dl_dst(&rule, remote_mac);
         cls_rule_set_nw_proto(&rule, ARP_OP_REPLY);
-        cb(ib, &rule);
+        add_rule(ib, &rule);
 
         /* (e) Allow ARP requests from the next hop's MAC address. */
         cls_rule_init_catchall(&rule, IBR_FROM_NEXT_HOP_ARP);
         cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
         cls_rule_set_dl_src(&rule, remote_mac);
         cls_rule_set_nw_proto(&rule, ARP_OP_REQUEST);
-        cb(ib, &rule);
+        add_rule(ib, &rule);
     }
 
-    for (i = 0; i < ib->n_remote_addrs; i++) {
-        const struct sockaddr_in *a = &ib->remote_addrs[i];
-
-        if (!i || a->sin_addr.s_addr != a[-1].sin_addr.s_addr) {
-            /* (f) Allow ARP replies containing the remote's IP address as a
-             * target. */
-            cls_rule_init_catchall(&rule, IBR_TO_REMOTE_ARP);
-            cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-            cls_rule_set_nw_proto(&rule, ARP_OP_REPLY);
-            cls_rule_set_nw_dst(&rule, a->sin_addr.s_addr);
-            cb(ib, &rule);
-
-            /* (g) Allow ARP requests containing the remote's IP address as a
-             * source. */
-            cls_rule_init_catchall(&rule, IBR_FROM_REMOTE_ARP);
-            cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-            cls_rule_set_nw_proto(&rule, ARP_OP_REQUEST);
-            cls_rule_set_nw_src(&rule, a->sin_addr.s_addr);
-            cb(ib, &rule);
-        }
-
-        if (!i
-            || a->sin_addr.s_addr != a[-1].sin_addr.s_addr
-            || a->sin_port != a[-1].sin_port) {
-            /* (h) Allow TCP traffic to the remote's IP and port. */
-            cls_rule_init_catchall(&rule, IBR_TO_REMOTE_TCP);
-            cls_rule_set_dl_type(&rule, htons(ETH_TYPE_IP));
-            cls_rule_set_nw_proto(&rule, IPPROTO_TCP);
-            cls_rule_set_nw_dst(&rule, a->sin_addr.s_addr);
-            cls_rule_set_tp_dst(&rule, a->sin_port);
-            cb(ib, &rule);
-
-            /* (i) Allow TCP traffic from the remote's IP and port. */
-            cls_rule_init_catchall(&rule, IBR_FROM_REMOTE_TCP);
-            cls_rule_set_dl_type(&rule, htons(ETH_TYPE_IP));
-            cls_rule_set_nw_proto(&rule, IPPROTO_TCP);
-            cls_rule_set_nw_src(&rule, a->sin_addr.s_addr);
-            cls_rule_set_tp_src(&rule, a->sin_port);
-            cb(ib, &rule);
-        }
-    }
-}
-
-static void
-drop_rule(struct in_band *ib, const struct cls_rule *rule)
-{
-    ofproto_delete_flow(ib->ofproto, rule);
-}
+    for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) {
+        const struct sockaddr_in *a = &r->remote_addr;
 
-/* Drops from the flow table all of the flows set up by 'ib', then clears out
- * the information about the installed flows so that they can be filled in
- * again if necessary. */
-static void
-drop_rules(struct in_band *ib)
-{
-    /* Drop rules. */
-    make_rules(ib, drop_rule);
+        /* (f) Allow ARP replies containing the remote's IP address as a
+         * target. */
+        cls_rule_init_catchall(&rule, IBR_TO_REMOTE_ARP);
+        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
+        cls_rule_set_nw_proto(&rule, ARP_OP_REPLY);
+        cls_rule_set_nw_dst(&rule, a->sin_addr.s_addr);
+        add_rule(ib, &rule);
 
-    /* Clear out state. */
-    memset(ib->installed_local_mac, 0, sizeof ib->installed_local_mac);
+        /* (g) Allow ARP requests containing the remote's IP address as a
+         * source. */
+        cls_rule_init_catchall(&rule, IBR_FROM_REMOTE_ARP);
+        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
+        cls_rule_set_nw_proto(&rule, ARP_OP_REQUEST);
+        cls_rule_set_nw_src(&rule, a->sin_addr.s_addr);
+        add_rule(ib, &rule);
 
-    free(ib->remote_addrs);
-    ib->remote_addrs = NULL;
-    ib->n_remote_addrs = 0;
+        /* (h) Allow TCP traffic to the remote's IP and port. */
+        cls_rule_init_catchall(&rule, IBR_TO_REMOTE_TCP);
+        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_IP));
+        cls_rule_set_nw_proto(&rule, IPPROTO_TCP);
+        cls_rule_set_nw_dst(&rule, a->sin_addr.s_addr);
+        cls_rule_set_tp_dst(&rule, a->sin_port);
+        add_rule(ib, &rule);
 
-    free(ib->remote_macs);
-    ib->remote_macs = NULL;
-    ib->n_remote_macs = 0;
+        /* (i) Allow TCP traffic from the remote's IP and port. */
+        cls_rule_init_catchall(&rule, IBR_FROM_REMOTE_TCP);
+        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_IP));
+        cls_rule_set_nw_proto(&rule, IPPROTO_TCP);
+        cls_rule_set_nw_src(&rule, a->sin_addr.s_addr);
+        cls_rule_set_tp_src(&rule, a->sin_port);
+        add_rule(ib, &rule);
+    }
 }
 
-static void
-add_rule(struct in_band *ib, const struct cls_rule *rule)
+/* Updates the OpenFlow flow table for the current state of in-band control.
+ * Returns true ordinarily.  Returns false if no remotes are configured on 'ib'
+ * and 'ib' doesn't have any rules left to remove from the OpenFlow flow
+ * table.  Thus, a false return value means that the caller can destroy 'ib'
+ * without leaving extra flows hanging around in the flow table. */
+bool
+in_band_run(struct in_band *ib)
 {
     struct {
         struct nx_action_set_queue nxsq;
-        struct ofp_action_output oao;
+        union ofp_action oa;
     } actions;
+    const void *a;
+    size_t na;
 
-    memset(&actions, 0, sizeof actions);
-
-    actions.oao.type = htons(OFPAT_OUTPUT);
-    actions.oao.len = htons(sizeof actions.oao);
-    actions.oao.port = htons(OFPP_NORMAL);
-    actions.oao.max_len = htons(0);
+    struct in_band_rule *rule, *next;
 
+    memset(&actions, 0, sizeof actions);
+    actions.oa.output.type = htons(OFPAT_OUTPUT);
+    actions.oa.output.len = htons(sizeof actions.oa);
+    actions.oa.output.port = htons(OFPP_NORMAL);
+    actions.oa.output.max_len = htons(0);
     if (ib->queue_id < 0) {
-        ofproto_add_flow(ib->ofproto, rule,
-                         (union ofp_action *) &actions.oao, 1);
+        a = &actions.oa;
+        na = sizeof actions.oa / sizeof(union ofp_action);
     } else {
         actions.nxsq.type = htons(OFPAT_VENDOR);
         actions.nxsq.len = htons(sizeof actions.nxsq);
         actions.nxsq.vendor = htonl(NX_VENDOR_ID);
         actions.nxsq.subtype = htons(NXAST_SET_QUEUE);
         actions.nxsq.queue_id = htonl(ib->queue_id);
-
-        ofproto_add_flow(ib->ofproto, rule, (union ofp_action *) &actions,
-                         sizeof actions / sizeof(union ofp_action));
-    }
-}
-
-/* Inserts flows into the flow table for the current state of 'ib'. */
-static void
-add_rules(struct in_band *ib)
-{
-    make_rules(ib, add_rule);
-}
-
-static int
-compare_addrs(const void *a_, const void *b_)
-{
-    const struct sockaddr_in *a = a_;
-    const struct sockaddr_in *b = b_;
-    int cmp;
-
-    cmp = memcmp(&a->sin_addr.s_addr,
-                 &b->sin_addr.s_addr,
-                 sizeof a->sin_addr.s_addr);
-    if (cmp) {
-        return cmp;
+        a = &actions;
+        na = sizeof actions / sizeof(union ofp_action);
     }
-    return memcmp(&a->sin_port, &b->sin_port, sizeof a->sin_port);
-}
-
-static int
-compare_macs(const void *a, const void *b)
-{
-    return eth_addr_compare_3way(a, b);
-}
 
-void
-in_band_run(struct in_band *ib)
-{
-    bool local_change, remote_change, queue_id_change;
-    struct in_band_remote *r;
+    refresh_local(ib);
+    refresh_remotes(ib);
 
-    local_change = refresh_local(ib);
-    remote_change = refresh_remotes(ib);
-    queue_id_change = ib->queue_id != ib->prev_queue_id;
-    if (!local_change && !remote_change && !queue_id_change) {
-        /* Nothing changed, nothing to do. */
-        return;
-    }
-    ib->prev_queue_id = ib->queue_id;
+    update_rules(ib);
 
-    /* Drop old rules. */
-    drop_rules(ib);
+    HMAP_FOR_EACH_SAFE (rule, next, cls_rule.hmap_node, &ib->rules) {
+        switch (rule->op) {
+        case ADD:
+            ofproto_add_flow(ib->ofproto, &rule->cls_rule, a, na);
+            break;
 
-    /* Figure out new rules. */
-    memcpy(ib->installed_local_mac, ib->local_mac, ETH_ADDR_LEN);
-    ib->remote_addrs = xmalloc(ib->n_remotes * sizeof *ib->remote_addrs);
-    ib->n_remote_addrs = 0;
-    ib->remote_macs = xmalloc(ib->n_remotes * ETH_ADDR_LEN);
-    ib->n_remote_macs = 0;
-    for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) {
-        ib->remote_addrs[ib->n_remote_addrs++] = r->remote_addr;
-        if (!eth_addr_is_zero(r->remote_mac)) {
-            memcpy(&ib->remote_macs[ib->n_remote_macs * ETH_ADDR_LEN],
-                   r->remote_mac, ETH_ADDR_LEN);
-            ib->n_remote_macs++;
+        case DELETE:
+            if (ofproto_delete_flow(ib->ofproto, &rule->cls_rule)) {
+                /* ofproto doesn't have the rule anymore so there's no reason
+                 * for us to track it any longer. */
+                hmap_remove(&ib->rules, &rule->cls_rule.hmap_node);
+                free(rule);
+            }
+            break;
         }
     }
 
-    /* Sort, to allow make_rules() to easily skip duplicates. */
-    qsort(ib->remote_addrs, ib->n_remote_addrs, sizeof *ib->remote_addrs,
-          compare_addrs);
-    qsort(ib->remote_macs, ib->n_remote_macs, ETH_ADDR_LEN, compare_macs);
-
-    /* Add new rules. */
-    add_rules(ib);
+    return ib->n_remotes || !hmap_is_empty(&ib->rules);
 }
 
 void
@@ -508,14 +462,6 @@ in_band_wait(struct in_band *in_band)
     poll_timer_wait_until(wakeup * 1000);
 }
 
-/* ofproto has flushed all flows from the flow table and it is calling us back
- * to allow us to reinstall the ones that are important to us. */
-void
-in_band_flushed(struct in_band *in_band)
-{
-    add_rules(in_band);
-}
-
 int
 in_band_create(struct ofproto *ofproto, const char *local_name,
                struct in_band **in_bandp)
@@ -525,7 +471,7 @@ in_band_create(struct ofproto *ofproto, const char *local_name,
     int error;
 
     *in_bandp = NULL;
-    error = netdev_open_default(local_name, &local_netdev);
+    error = netdev_open(local_name, "system", &local_netdev);
     if (error) {
         VLOG_ERR("failed to initialize in-band control: cannot open "
                  "datapath local port %s (%s)", local_name, strerror(error));
@@ -534,10 +480,11 @@ in_band_create(struct ofproto *ofproto, const char *local_name,
 
     in_band = xzalloc(sizeof *in_band);
     in_band->ofproto = ofproto;
-    in_band->queue_id = in_band->prev_queue_id = -1;
+    in_band->queue_id = -1;
     in_band->next_remote_refresh = TIME_MIN;
     in_band->next_local_refresh = TIME_MIN;
     in_band->local_netdev = local_netdev;
+    hmap_init(&in_band->rules);
 
     *in_bandp = in_band;
 
@@ -548,7 +495,13 @@ void
 in_band_destroy(struct in_band *ib)
 {
     if (ib) {
-        drop_rules(ib);
+        struct in_band_rule *rule, *next;
+
+        HMAP_FOR_EACH_SAFE (rule, next, cls_rule.hmap_node, &ib->rules) {
+            hmap_remove(&ib->rules, &rule->cls_rule.hmap_node);
+            free(rule);
+        }
+        hmap_destroy(&ib->rules);
         in_band_set_remotes(ib, NULL, 0);
         netdev_close(ib->local_netdev);
         free(ib);