Implement revalidation.
authorBen Pfaff <blp@nicira.com>
Wed, 24 Dec 2008 01:06:17 +0000 (17:06 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 24 Dec 2008 01:06:17 +0000 (17:06 -0800)
vswitchd/automake.mk
vswitchd/bridge.c
vswitchd/flowtrack.c [new file with mode: 0644]
vswitchd/flowtrack.h [new file with mode: 0644]

index c53ece765a60ba1c65f73f4cef023726cc5831ad..d059a07f3f24931fa2bbe6b5440f7f5a8278152a 100644 (file)
@@ -7,6 +7,8 @@ vswitchd_vswitchd_SOURCES = \
        vswitchd/bridge.h \
        vswitchd/cfg.c \
        vswitchd/cfg.h \
+       vswitchd/flowtrack.c \
+       vswitchd/flowtrack.h \
        vswitchd/vswitchd.c
 vswitchd_vswitchd_LDADD = lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
 
index 1f51a599dace27785e254e079005e3a22ba599df..35bd3d7e92324b4aeecf7f43e351853528f56a1e 100644 (file)
@@ -48,6 +48,7 @@
 #include "dirs.h"
 #include "dpif.h"
 #include "flow.h"
+#include "flowtrack.h"
 #include "hash.h"
 #include "list.h"
 #include "mac-learning.h"
@@ -103,6 +104,7 @@ struct bridge {
     int txqlen;                 /* # of messages queued to send on 'rconn'. */
     struct mac_learning *ml;    /* MAC learning table, or null not to learn. */
     int flow_idle_time;         /* Idle time for flows we set up. */
+    bool sent_config;           /* Successfully sent config request? */
 
     /* Kernel datapath information. */
     int dp_idx;                 /* Kernel datapath index. */
@@ -111,6 +113,10 @@ struct bridge {
     /* Bridge ports. */
     struct port **ports;
     size_t n_ports, allocated_ports;
+
+    /* Flow tracking. */
+    struct ft *ft;
+    struct tag_set revalidate_set;
 };
 
 /* List of all bridges. */
@@ -134,6 +140,7 @@ static bool bridge_is_backlogged(const struct bridge *);
 static int bridge_fetch_dp_ifaces(struct bridge *, struct svec *iface_names);
 
 static void bridge_process_msg(struct bridge *, struct ofpbuf *);
+static void revalidate_flow(struct bridge *br, struct ft_flow *f);
 
 static void port_create(struct bridge *, const char *name);
 static void port_reconfigure(struct port *);
@@ -323,6 +330,8 @@ bridge_create(const char *name)
     br->name = xstrdup(name);
     br->ml = mac_learning_create();
     br->flow_idle_time = 5;
+    br->sent_config = false;
+    br->ft = ft_create();
 
     /* Create kernel datapath. */
     for (;;) {
@@ -423,6 +432,7 @@ bridge_destroy(struct bridge *br)
         for (i = 0; i < br->n_ports; i++) {
             port_destroy(br->ports[i]);
         }
+        ft_destroy(br->ft);
         mac_learning_destroy(br->ml);
         free(br->ports);
         free(br);
@@ -475,6 +485,23 @@ bridge_run_one(struct bridge *br)
     int iteration;
 
     rconn_run(br->rconn);
+    if (rconn_is_connected(br->rconn) && !br->sent_config) {
+        struct ofp_switch_config *osc;
+        struct ofpbuf *msg;
+        int retval;
+
+        osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &msg);
+        osc->flags = htons(OFPC_SEND_FLOW_EXP | OFPC_FRAG_NORMAL);
+        osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
+        retval = rconn_send(br->rconn, msg, &br->txqlen);
+        if (retval) {
+            ofpbuf_delete(msg);
+        } else {
+            br->sent_config = true;
+        }
+    }
+
+    tag_set_init(&br->revalidate_set);
     for (iteration = 0; iteration < 50 && !bridge_is_backlogged(br);
          iteration++) {
         struct ofpbuf *msg = rconn_recv(br->rconn);
@@ -485,9 +512,20 @@ bridge_run_one(struct bridge *br)
         bridge_process_msg(br, msg);
         ofpbuf_delete(msg);
     }
+    hmap_shrink(&br->ft->flows);
     if (br->ml) {
         mac_learning_run(br->ml, &br->revalidate_set);
     }
+    if (!tag_set_is_empty(&br->revalidate_set)) {
+        struct ft_flow *f, *next;
+
+        HMAP_FOR_EACH_SAFE (f, next, struct ft_flow, node, &br->ft->flows) {
+            if (tag_set_intersects(&br->revalidate_set, f->tags)) {
+                revalidate_flow(br, f);
+            }
+        }
+    }
+
     if (process_exited(br->secchan)) {
         int status = process_status(br->secchan);
         VLOG_ERR("%s: secchan subprocess with pid %ld died unexpectedly (%s)",
@@ -682,6 +720,7 @@ sanitize_opp(struct ofp_phy_port *opp)
 typedef void packet_handler_func(struct bridge *, void *);
 static packet_handler_func process_echo_request;
 static packet_handler_func process_packet_in;
+static packet_handler_func process_flow_expired;
 
 static void
 bridge_process_msg(struct bridge *br, struct ofpbuf *msg)
@@ -705,7 +744,7 @@ bridge_process_msg(struct bridge *br, struct ofpbuf *msg)
         },
         {
             OFPT_FLOW_EXPIRED,
-            NULL
+            process_flow_expired
         },
     };
     const size_t n_processors = ARRAY_SIZE(processors);
@@ -733,7 +772,9 @@ bridge_process_msg(struct bridge *br, struct ofpbuf *msg)
 static void
 queue_tx(struct bridge *br, struct ofpbuf *msg)
 {
-    int retval = rconn_send(br->rconn, msg, &br->txqlen);
+    int retval;
+    update_openflow_length(msg);
+    retval = rconn_send(br->rconn, msg, &br->txqlen);
     if (retval) {
         ofpbuf_delete(msg);
         /* No point in logging: rconn_send() only fails due to disconnection,
@@ -742,11 +783,6 @@ queue_tx(struct bridge *br, struct ofpbuf *msg)
     }
 }
 
-struct output {
-    uint16_t vlan;
-    uint16_t dp_ifidx;
-};
-
 static struct iface *
 choose_output_iface(const struct port *port, const struct flow *flow)
 {
@@ -766,8 +802,8 @@ choose_output_iface(const struct port *port, const struct flow *flow)
 }
 
 static void
-set_output(struct output *p, const struct flow *flow,
-           const struct port *in_port, const struct port *out_port)
+set_dst(struct ft_dst *p, const struct flow *flow,
+        const struct port *in_port, const struct port *out_port)
 {
     p->vlan = (out_port->vlan ? OFP_VLAN_NONE
               : in_port->vlan ? in_port->vlan
@@ -775,6 +811,14 @@ set_output(struct output *p, const struct flow *flow,
     p->dp_ifidx = choose_output_iface(out_port, flow)->dp_ifidx;
 }
 
+static void
+swap_dst(struct ft_dst *p, struct ft_dst *q)
+{
+    struct ft_dst tmp = *p;
+    *p = *q;
+    *q = tmp;
+}
+
 static void *
 add_action_header(struct ofpbuf *buf, uint16_t type)
 {
@@ -805,106 +849,130 @@ add_vlan_action(struct ofpbuf *buf, uint16_t old_vlan, uint16_t new_vlan)
     }
 }
 
-static void
-put_actions(const struct bridge *br, const struct flow *flow, uint16_t vlan,
-            const struct port *in_port, const struct port *out_port,
-            struct ofpbuf *buf, void **actions, size_t *actions_len)
+static size_t
+compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
+             const struct port *in_port, const struct port *out_port,
+             struct ft_dst dsts[])
 {
-    struct output outs[DP_MAX_PORTS];
-    struct output *p;
-    size_t n_outs;
-    uint16_t orig_vlan, cur_vlan;
-    size_t actions_ofs;
-
-    if (*actions) {
-        ofpbuf_put(buf, *actions, *actions_len);
-        return;
-    }
-
-    n_outs = 0;
     if (out_port == FLOOD_PORT) {
         /* Flood. */
+        struct ft_dst *dst;       /* Next element in 'dsts'. */
+        struct ft_dst *vlan_dsts; /* First 'dsts' with vlan != flow->dl_vlan */
         size_t i;
 
+        dst = vlan_dsts = dsts;
         for (i = 0; i < br->n_ports; i++) {
-            struct port *op = br->ports[i];
-            if (op != in_port && (!op->vlan || vlan == op->vlan)) {
-                set_output(&outs[n_outs++], flow, in_port, op);
+            struct port *port = br->ports[i];
+            if (port != in_port && (!port->vlan || vlan == port->vlan)) {
+                /* Put destinations for original VLAN at the front, so that we
+                 * don't have to add actions to set the VLAN tag for those. */
+                set_dst(dst, flow, in_port, port);
+                if (dst->vlan == ntohs(flow->dl_vlan)) {
+                    swap_dst(dst, vlan_dsts++);
+                }
+                dst++;
             }
         }
+
+        /* XXX Sort 'vlan_dsts' through 'dst' by VLAN, to reduce number of
+         * VLAN-setting actions to minimum. */
+        return dst - dsts;
     } else if (out_port) {
         /* Unicast. */
-        set_output(&outs[n_outs++], flow, in_port, out_port);
+        set_dst(dsts, flow, in_port, out_port);
+        return 1;
+    } else {
+        /* Drop. */
+        return 0;
     }
+}
 
-    actions_ofs = buf->size;
-
-    orig_vlan = ntohs(flow->dl_vlan);
-    for (p = outs; p < &outs[n_outs]; p++) {
-        if (orig_vlan == p->vlan) {
-            add_output_action(buf, p->dp_ifidx);
-        }
-    }
-    cur_vlan = orig_vlan;
-    for (p = outs; p < &outs[n_outs]; p++) {
-        if (p->vlan != orig_vlan) {
-            if (p->vlan != cur_vlan) {
-                add_vlan_action(buf, cur_vlan, p->vlan);
-                cur_vlan = p->vlan;
-            }
-            add_output_action(buf, p->dp_ifidx);
+static void
+put_actions(const struct ft_dst dsts[], size_t n_dsts, uint16_t flow_vlan,
+            struct ofpbuf *buf)
+{
+    const struct ft_dst *p;
+    uint16_t vlan = flow_vlan;
+    for (p = dsts; p < &dsts[n_dsts]; p++) {
+        if (p->vlan != vlan) {
+            add_vlan_action(buf, vlan, p->vlan);
+            vlan = p->vlan;
         }
+        add_output_action(buf, p->dp_ifidx);
     }
-
-    *actions = (char *) buf->data + actions_ofs;
-    *actions_len = buf->size - actions_ofs;
 }
 
+struct received_packet {
+    struct ofpbuf *buf;      /* NULL if no packet data. */
+    uint32_t buffer_id;      /* Host byte order; UINT32_MAX if not buffered. */
+};
+
 static void
-send_packets(struct bridge *br, const struct flow *flow, uint32_t buffer_id,
-             uint16_t vlan, uint16_t in_ifidx,
-             const void *pkt_data, size_t pkt_len,
+send_packets(struct bridge *br, const struct flow *flow,
+             const struct received_packet *pkt, uint16_t vlan,
              const struct port *in_port, const struct port *out_port,
-             bool setup_flow)
+             tag_type tags, bool setup_flow)
 {
-    struct ofpbuf *fbuf = NULL;
-    struct ofpbuf *pbuf = NULL;
+    struct ft_dst dsts[DP_MAX_PORTS];
+    size_t actions_len;         /* Estimated length of actions, in bytes. */
+    size_t n_dsts;
 
-    void *actions = NULL;
-    size_t actions_len = sizeof(struct ofp_action_header) * 4; /* Estimated. */
+    n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts);
+    actions_len = (sizeof(struct ofp_action_header) + 2) * n_dsts;
 
     if (setup_flow) {
-        fbuf = make_add_flow(flow, buffer_id, br->flow_idle_time, actions_len);
-        put_actions(br, flow, vlan, in_port, out_port, fbuf,
-                    &actions, &actions_len);
-        update_openflow_length(fbuf);
+        struct ft_flow *f;
+        bool queue;
+
+        f = ft_lookup(br->ft, flow);
+        if (f) {
+            if (!ftd_equal(dsts, n_dsts, f->dsts, f->n_dsts)) {
+                ftf_set_dsts(f, dsts, n_dsts);
+                queue = true;
+            } else {
+                /* Correct flow is already in the flow table, nothing to do.
+                 * This should only happen on revalidate, since
+                 * process_packet_in() will delete any flow entry for a
+                 * no-match packet-in message.  (If it could happen in other
+                 * circumstances then we'd want to arrange to send a packet-out
+                 * below, but there's no need.) */
+                queue = false;
+            }
+            f->tags = tags;
+        } else {
+            ft_insert(br->ft, ftf_create(flow, dsts, n_dsts, tags));
+            queue = true;
+        }
 
-        /* Defer sending 'fbuf' to the end of the function: sending it can free
-         * it immediately, but we may need to reuse the cached actions for
-         * 'pbuf', below ('actions' points inside 'fbuf'). */
+        if (queue) {
+            struct ofpbuf *fbuf = make_add_flow(flow, pkt->buffer_id,
+                                                br->flow_idle_time,
+                                                actions_len);
+            put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), fbuf);
+            queue_tx(br, fbuf);
+        }
+    } else {
+        struct ft_flow *f = ft_lookup(br->ft, flow);
+        if (f) {
+            /* XXX delete flow from ft, queue delete-flow openflow message */
+        }
     }
 
-    if (!setup_flow || buffer_id == UINT32_MAX) {
+    if (pkt->buffer_id == UINT32_MAX ? pkt->buf != NULL : !setup_flow) {
+        size_t pkt_size = pkt->buf ? pkt->buf->size : 0;
         struct ofp_packet_out *opo;
+        struct ofpbuf *pbuf;
 
-        pbuf = ofpbuf_new(sizeof *opo + actions_len + pkt_len);
+        pbuf = ofpbuf_new(sizeof *opo + actions_len + pkt_size);
         opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, pbuf);
-        opo->buffer_id = htonl(buffer_id);
-        opo->in_port = htons(in_ifidx);
-        put_actions(br, flow, vlan, in_port, out_port, pbuf,
-                    &actions, &actions_len);
+        opo->buffer_id = htonl(pkt->buffer_id);
+        opo->in_port = flow->in_port;
+        put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), pbuf);
         opo = pbuf->data;
         opo->actions_len = htons(actions_len);
-        if (buffer_id == UINT32_MAX) {
-            ofpbuf_put(pbuf, pkt_data, pkt_len);
+        if (pkt->buffer_id == UINT32_MAX) {
+            ofpbuf_put(pbuf, pkt->buf->data, pkt->buf->size);
         }
-        update_openflow_length(pbuf);
-    }
-
-    if (fbuf) {
-        queue_tx(br, fbuf);
-    }
-    if (pbuf) {
         queue_tx(br, pbuf);
     }
 }
@@ -919,38 +987,37 @@ is_bcast_arp_reply(const struct flow *flow, const struct ofpbuf *pkt)
 }
 
 static void
-process_packet_in(struct bridge *br, void *opi_)
+process_flow(struct bridge *br, const struct flow *flow,
+             struct received_packet *pkt)
 {
-    struct ofp_packet_in *opi = opi_;
-    uint16_t in_ifidx = ntohs(opi->in_port);
-
-    struct ofpbuf pkt;
-    struct flow flow;
+    uint16_t in_ifidx = ntohs(flow->in_port);
     struct iface *in_iface;
     struct port *in_port;
     struct port *out_port = NULL; /* By default, drop the packet/flow. */
+    tag_type tags = 0;
     int vlan;
 
-    /* Validate Openflow message. */
-    if (check_ofp_message_array(&opi->header, OFPT_PACKET_IN,
-                                offsetof(struct ofp_packet_in, data),
-                                1, &pkt.size)) {
-        return;
-    }
+    /* Find the interface and port structure for the received packet. */
+    if (in_ifidx >= ARRAY_SIZE(br->ifaces) || !br->ifaces[in_ifidx]) {
+        struct ft_flow *f;
 
-    /* Extract flow data from 'opi' into 'flow'. */
-    pkt.data = opi->data;
-    flow_extract(&pkt, in_ifidx, &flow);
+        if (pkt->buf) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
+                         "interface %"PRIu16, br->name, in_ifidx); 
+        }
 
-    /* Find the interface and port structure for the received packet. */
-    if (in_ifidx < 0
-        || in_ifidx >= ARRAY_SIZE(br->ifaces)
-        || !br->ifaces[in_ifidx]) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-        VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
-                     "interface %"PRIu16, br->name, in_ifidx);
-        queue_tx(br, make_add_flow(&flow, ntohl(opi->buffer_id),
+        queue_tx(br, make_add_flow(flow, pkt->buffer_id,
                                    br->flow_idle_time, 0));
+
+        f = ft_lookup(br->ft, flow);
+        if (f) {
+            ftf_set_dsts(f, NULL, 0);
+            f->tags = tags;
+        } else {
+            ft_insert(br->ft, ftf_create(flow, NULL, 0, tags));
+        }
+
         return;
     }
     in_iface = br->ifaces[in_ifidx];
@@ -963,19 +1030,21 @@ process_packet_in(struct bridge *br, void *opi_)
      * former case, the packet has an 802.1Q header that specifies VLAN 0,
      * presumably to allow a priority to be specified.  In the latter case, the
      * packet does not have any 802.1Q header.) */
-    vlan = ntohs(flow.dl_vlan);
+    vlan = ntohs(flow->dl_vlan);
     if (vlan == OFP_VLAN_NONE) {
         vlan = 0;
     }
     if (in_port->vlan) {
         if (vlan) {
             /* XXX support double tagging? */
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-            VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
-                         "packet received on port %s configured with implicit "
-                         "VLAN %"PRIu16,
-                         br->name, ntohs(flow.dl_vlan),
-                         in_port->name, in_port->vlan);
+            if (pkt->buf) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
+                             "packet received on port %s configured with "
+                             "implicit VLAN %"PRIu16,
+                             br->name, ntohs(flow->dl_vlan),
+                             in_port->name, in_port->vlan);
+            }
             goto done;
         }
         vlan = in_port->vlan;
@@ -985,7 +1054,8 @@ process_packet_in(struct bridge *br, void *opi_)
      * avoid receiving duplicates. */
     if (in_port->n_ifaces > 0
         && in_port->active_iface != in_iface->port_ifidx
-        && eth_addr_is_multicast(flow.dl_dst)) {
+        && eth_addr_is_multicast(flow->dl_dst)) {
+        //tags |= in_iface->inactive_iface_tag;
         goto done;
     }
 
@@ -998,17 +1068,21 @@ process_packet_in(struct bridge *br, void *opi_)
         /* XXX flush learning table entries when port indexes change due to
          * reconfiguration */
 
-        /* If the packet arrived on a bonded port, don't learn from it unless
-         * we haven't learned any port at all for that address (because we
-         * probably sent the packet on one bonded interface and got it back on
-         * the other). */
-        if (in_port->n_ifaces > 1) {
-            uint16_t src_idx = mac_learning_lookup(br->ml, flow.dl_src, vlan);
+        if (!pkt->buf) {
+            /* Don't try to learn from revalidation. */
+            may_learn = false;
+        } else if (in_port->n_ifaces > 1) {
+            /* If the packet arrived on a bonded port, don't learn from it
+             * unless we haven't learned any port at all for that address
+             * (because we probably sent the packet on one bonded interface and
+             * got it back on the other). */
+            /* XXX invalidation? */
+            uint16_t src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan);
             may_learn = src_idx == OFPP_FLOOD || src_idx == in_port->port_idx;
 
             /* Broadcast ARP replies are an exception to this rule: the host
              * has moved to another switch. */
-            if (!may_learn && is_bcast_arp_reply(&flow, &pkt)) {
+            if (!may_learn && is_bcast_arp_reply(flow, pkt->buf)) {
                 may_learn = true;
             }
         } else {
@@ -1016,19 +1090,25 @@ process_packet_in(struct bridge *br, void *opi_)
         }
 
         /* Learn source MAC. */
-        if (may_learn &&
-            mac_learning_learn(br->ml, flow.dl_src, vlan, in_port->port_idx)) {
-            /* The log messages here could actually be useful in debugging, so
-             * keep the rate limit relatively high. */
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
-            VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is on "
-                        "port %s in VLAN %d",
-                        br->name, ETH_ADDR_ARGS(flow.dl_src),
-                        in_port->name, vlan);
+        if (may_learn) {
+            tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src,
+                                                  vlan, in_port->port_idx);
+            if (rev_tag) {
+                /* The log messages here could actually be useful in debugging,
+                 * so keep the rate limit relatively high. */
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30,
+                                                                        300);
+                VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
+                            "on port %s in VLAN %d",
+                            br->name, ETH_ADDR_ARGS(flow->dl_src),
+                            in_port->name, vlan);
+                tag_set_add(&br->revalidate_set, rev_tag);
+            }
         }
 
         /* Determine output port. */
-        out_port_idx = mac_learning_lookup(br->ml, flow.dl_dst, vlan);
+        out_port_idx = mac_learning_lookup_tag(br->ml, flow->dl_dst, vlan,
+                                               &tags);
         if (out_port_idx < br->n_ports) {
             out_port = br->ports[out_port_idx];
         }
@@ -1052,12 +1132,51 @@ process_packet_in(struct bridge *br, void *opi_)
      *     them.
      */
 done:
-    send_packets(br, &flow, ntohl(opi->buffer_id), vlan,
-                 in_ifidx, pkt.data, pkt.size, in_port, out_port,
+    send_packets(br, flow, pkt, vlan, in_port, out_port, tags,
                  (br->flow_idle_time >= 0
                   && (in_port->n_ifaces < 2
-                      || flow.dl_type != htons(ETH_TYPE_ARP)
-                      || !eth_addr_is_broadcast(flow.dl_dst))));
+                      || flow->dl_type != htons(ETH_TYPE_ARP)
+                      || !eth_addr_is_broadcast(flow->dl_dst))));
+}
+
+static void
+revalidate_flow(struct bridge *br, struct ft_flow *f)
+{
+    struct received_packet pkt;
+    pkt.buf = NULL;
+    pkt.buffer_id = UINT32_MAX;
+    process_flow(br, &f->flow, &pkt);
+}
+
+static void
+process_packet_in(struct bridge *br, void *opi_)
+{
+    struct ofp_packet_in *opi = opi_;
+    struct received_packet pkt;
+    struct ofpbuf buf;
+    struct flow flow;
+
+    if (check_ofp_message_array(&opi->header, OFPT_PACKET_IN,
+                                offsetof(struct ofp_packet_in, data),
+                                1, &buf.size)) {
+        return;
+    }
+    buf.data = opi->data;
+    pkt.buf = &buf;
+    pkt.buffer_id = ntohl(opi->buffer_id);
+    flow_extract(&buf, ntohs(opi->in_port), &flow);
+
+    if (opi->reason == OFPR_NO_MATCH) {
+        /* Delete any existing flow from the flow table.  It must not really be
+         * there (otherwise we wouldn't be getting a packet-in). */
+        struct ft_flow *f = ft_lookup(br->ft, &flow);
+        if (f) {
+            ft_remove(br->ft, f);
+            ftf_destroy(f);
+        }
+    }
+
+    process_flow(br, &flow, &pkt);
 }
 
 static void
@@ -1066,6 +1185,43 @@ process_echo_request(struct bridge *br, void *rq_)
     struct ofp_header *rq = rq_;
     queue_tx(br, make_echo_reply(rq));
 }
+
+static void
+process_flow_expired(struct bridge *br, void *ofe_)
+{
+    struct ofp_flow_expired *ofe = ofe_;
+    struct ft_flow *f;
+    struct flow flow;
+
+    if (check_ofp_message(&ofe->header, OFPT_FLOW_EXPIRED, sizeof *ofe)) {
+        return;
+    }
+
+    if (ofe->match.wildcards != htonl(0)) {
+        /* We don't use flows with wildcards, so there's nothing to do. */
+        return;
+    }
+    flow.nw_src = ofe->match.nw_src;
+    flow.nw_dst = ofe->match.nw_dst;
+    flow.in_port = ofe->match.in_port;
+    flow.dl_vlan = ofe->match.dl_vlan;
+    flow.dl_type = ofe->match.dl_type;
+    flow.tp_src = ofe->match.tp_src;
+    flow.tp_dst = ofe->match.tp_dst;
+    memcpy(flow.dl_src, ofe->match.dl_src, ETH_ADDR_LEN);
+    memcpy(flow.dl_dst, ofe->match.dl_dst, ETH_ADDR_LEN);
+    flow.nw_proto = ofe->match.nw_proto;
+    flow.reserved = 0;
+
+    f = ft_lookup(br->ft, &flow);
+    if (f) {
+        ft_remove(br->ft, f);
+        ftf_destroy(f);
+    } else if (VLOG_IS_DBG_ENABLED()) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_DBG_RL(&rl, "received flow expiration for flow not in table");
+    }
+}
 \f
 /* Port functions. */
 
@@ -1103,6 +1259,13 @@ port_reconfigure(struct port *port)
     }
     if (bonded) {
         cfg_get_all_keys(&new_ifaces, "bonding.%s.slave", port->name);
+        if (!new_ifaces.n) {
+            VLOG_ERR("port %s: no interfaces specified for bonded port",
+                     port->name);
+        } else if (new_ifaces.n == 1) {
+            VLOG_WARN("port %s: only 1 interface specified for bonded port",
+                      port->name);
+        }
     } else {
         svec_init(&new_ifaces);
         svec_add(&new_ifaces, port->name);
@@ -1139,8 +1302,8 @@ port_reconfigure(struct port *port)
                           port->name, port->vlan);
             }
         } else {
-            /* It's possible that bonded, VLAN-tagged ports make sense but they
-             * are not worth implementing yet. */
+            /* It's possible that bonded, VLAN-tagged ports make sense.  Maybe
+             * they even work as-is.  But they have not been tested. */
             VLOG_WARN("port %s: VLAN tags not supported on bonded ports",
                       port->name);
         }
diff --git a/vswitchd/flowtrack.c b/vswitchd/flowtrack.c
new file mode 100644 (file)
index 0000000..d475050
--- /dev/null
@@ -0,0 +1,145 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include <config.h>
+#include "flowtrack.h"
+#include <inttypes.h>
+#include <stdlib.h>
+#include "flow.h"
+#include "openflow/openflow.h"
+#include "util.h"
+
+void
+ftd_print(const struct ft_dst *dsts, size_t n)
+{
+    for (; n--; dsts++) {
+        printf(">p%"PRIu16, dsts->dp_ifidx);
+        if (dsts->vlan != OFP_VLAN_NONE) {
+            printf("v%"PRIu16, dsts->vlan);
+        }
+    }
+}
+
+struct ft_flow *
+ftf_create(const struct flow *flow,
+           const struct ft_dst dsts[], size_t n_dsts,
+           tag_type tags)
+{
+    struct ft_flow *f;
+
+    assert(!flow->reserved);
+    f = xmalloc(sizeof *f);
+    f->tags = tags;
+    f->flow = *flow;
+    f->node.hash = flow_hash(&f->flow, 0);
+    f->dsts = &f->one_dst;
+    ftf_set_dsts(f, dsts, n_dsts);
+    return f;
+}
+
+void
+ftf_destroy(struct ft_flow *f)
+{
+    if (f) {
+        ftf_set_dsts(f, NULL, 0);
+        free(f);
+    }
+}
+
+void
+ftf_set_dsts(struct ft_flow *f, const struct ft_dst dsts[], size_t n_dsts)
+{
+    if (f->dsts != &f->one_dst) {
+        free(f->dsts);
+    }
+
+    f->n_dsts = n_dsts;
+    if (n_dsts > 1) {
+        f->dsts = xmemdup(dsts, sizeof *f->dsts * n_dsts);
+    } else {
+        f->dsts = &f->one_dst;
+        if (n_dsts) {
+            f->one_dst = *dsts;
+        }
+    }
+}
+\f
+struct ft *
+ft_create(void)
+{
+    struct ft *ft = xcalloc(1, sizeof *ft);
+    hmap_init(&ft->flows);
+    return ft;
+}
+
+void
+ft_destroy(struct ft *ft)
+{
+    if (ft) {
+        struct ft_flow *f, *next;
+
+        HMAP_FOR_EACH_SAFE (f, next, struct ft_flow, node, &ft->flows) {
+            ftf_destroy(f);
+        }
+        free(ft);
+    }
+}
+
+struct ft_flow *
+ft_lookup(const struct ft *ft, const struct flow *target)
+{
+    struct ft_flow *f;
+    size_t hash;
+
+    assert(!target->reserved);
+    hash = flow_hash(target, 0);
+    HMAP_FOR_EACH_WITH_HASH (f, struct ft_flow, node, hash, &ft->flows) {
+        if (flow_equal(&f->flow, target)) {
+            return f;
+        }
+    }
+    return NULL;
+}
+
+void
+ft_remove(struct ft *ft, struct ft_flow *f)
+{
+    hmap_remove(&ft->flows, &f->node);
+    /* It's the caller's responsible to free 'f', if desired. */
+}
+
+void
+ft_insert(struct ft *ft, struct ft_flow *f)
+{
+    hmap_insert(&ft->flows, &f->node, f->node.hash);
+}
diff --git a/vswitchd/flowtrack.h b/vswitchd/flowtrack.h
new file mode 100644 (file)
index 0000000..4c12cb1
--- /dev/null
@@ -0,0 +1,86 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef VSWITCHD_FLOWTRACK_H
+#define VSWITCHD_FLOWTRACK_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "flow.h"
+#include "hmap.h"
+#include "tag.h"
+
+struct ft_dst {
+    uint16_t vlan;
+    uint16_t dp_ifidx;
+};
+
+static inline bool ftd_equal(const struct ft_dst *, size_t,
+                             const struct ft_dst *, size_t);
+void ftd_print(const struct ft_dst *, size_t);
+
+static inline bool
+ftd_equal(const struct ft_dst *a, size_t an,
+          const struct ft_dst *b, size_t bn)
+{
+    return an == bn && !memcmp(a, b, an * sizeof *a);
+}
+
+\f
+struct ft_flow {
+    tag_type tags;
+    struct hmap_node node;
+    struct flow flow;
+    struct ft_dst *dsts;
+    size_t n_dsts;
+    struct ft_dst one_dst;
+};
+
+struct ft_flow *ftf_create(const struct flow *,
+                           const struct ft_dst[], size_t n_dsts,
+                           tag_type tags);
+void ftf_destroy(struct ft_flow *);
+void ftf_set_dsts(struct ft_flow *, const struct ft_dst[], size_t n_dsts);
+\f
+struct ft {
+    struct hmap flows;
+};
+
+struct ft *ft_create(void);
+void ft_destroy(struct ft *);
+struct ft_flow *ft_lookup(const struct ft *, const struct flow *);
+void ft_remove(struct ft *, struct ft_flow *);
+void ft_insert(struct ft *, struct ft_flow *);
+
+#endif /* vswitchd/flowtrack.h */