+static void
+update_stats(struct ofproto *ofproto, struct rule *rule,
+ const struct odp_flow_stats *stats)
+{
+ if (stats->n_packets) {
+ update_time(ofproto, rule, stats);
+ rule->packet_count += stats->n_packets;
+ rule->byte_count += stats->n_bytes;
+ netflow_flow_update_flags(&rule->nf_flow, stats->tcp_flags);
+ }
+}
+
+/* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT
+ * in which no matching flow already exists in the flow table.
+ *
+ * Adds the flow specified by 'ofm', which is followed by 'n_actions'
+ * ofp_actions, to 'p''s flow table. Returns 0 on success or an OpenFlow error
+ * code as encoded by ofp_mkerr() on failure.
+ *
+ * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
+ * if any. */
+static int
+add_flow(struct ofproto *p, struct ofconn *ofconn,
+ const struct ofp_flow_mod *ofm, size_t n_actions)
+{
+ struct ofpbuf *packet;
+ struct rule *rule;
+ uint16_t in_port;
+ int error;
+
+ if (ofm->flags & htons(OFPFF_CHECK_OVERLAP)) {
+ flow_t flow;
+ uint32_t wildcards;
+
+ flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie,
+ &flow, &wildcards);
+ if (classifier_rule_overlaps(&p->cls, &flow, wildcards,
+ ntohs(ofm->priority))) {
+ return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
+ }
+ }
+
+ rule = rule_create(p, NULL, (const union ofp_action *) ofm->actions,
+ n_actions, ntohs(ofm->idle_timeout),
+ ntohs(ofm->hard_timeout), ofm->cookie,
+ ofm->flags & htons(OFPFF_SEND_FLOW_REM));
+ cls_rule_from_match(&ofm->match, ntohs(ofm->priority),
+ p->tun_id_from_cookie, ofm->cookie, &rule->cr);
+
+ error = 0;
+ if (ofm->buffer_id != htonl(UINT32_MAX)) {
+ error = pktbuf_retrieve(ofconn->pktbuf, ntohl(ofm->buffer_id),
+ &packet, &in_port);
+ } else {
+ packet = NULL;
+ in_port = UINT16_MAX;
+ }
+
+ rule_insert(p, rule, packet, in_port);
+ return error;
+}
+
+static struct rule *
+find_flow_strict(struct ofproto *p, const struct ofp_flow_mod *ofm)
+{
+ uint32_t wildcards;
+ flow_t flow;
+
+ flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie,
+ &flow, &wildcards);
+ return rule_from_cls_rule(classifier_find_rule_exactly(
+ &p->cls, &flow, wildcards,
+ ntohs(ofm->priority)));
+}
+
+static int
+send_buffered_packet(struct ofproto *ofproto, struct ofconn *ofconn,
+ struct rule *rule, const struct ofp_flow_mod *ofm)
+{
+ struct ofpbuf *packet;
+ uint16_t in_port;
+ flow_t flow;
+ int error;
+
+ if (ofm->buffer_id == htonl(UINT32_MAX)) {
+ return 0;
+ }
+
+ error = pktbuf_retrieve(ofconn->pktbuf, ntohl(ofm->buffer_id),
+ &packet, &in_port);
+ if (error) {
+ return error;
+ }
+
+ flow_extract(packet, 0, in_port, &flow);
+ rule_execute(ofproto, rule, packet, &flow);
+
+ return 0;
+}
+\f
+/* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */
+
+struct modify_flows_cbdata {
+ struct ofproto *ofproto;
+ const struct ofp_flow_mod *ofm;
+ size_t n_actions;
+ struct rule *match;
+};
+
+static int modify_flow(struct ofproto *, const struct ofp_flow_mod *,
+ size_t n_actions, struct rule *);
+static void modify_flows_cb(struct cls_rule *, void *cbdata_);
+
+/* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code as
+ * encoded by ofp_mkerr() on failure.
+ *
+ * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
+ * if any. */
+static int
+modify_flows_loose(struct ofproto *p, struct ofconn *ofconn,
+ const struct ofp_flow_mod *ofm, size_t n_actions)
+{
+ struct modify_flows_cbdata cbdata;
+ struct cls_rule target;
+
+ cbdata.ofproto = p;
+ cbdata.ofm = ofm;
+ cbdata.n_actions = n_actions;
+ cbdata.match = NULL;
+
+ cls_rule_from_match(&ofm->match, 0, p->tun_id_from_cookie, ofm->cookie,
+ &target);
+
+ classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
+ modify_flows_cb, &cbdata);
+ if (cbdata.match) {
+ /* This credits the packet to whichever flow happened to happened to
+ * match last. That's weird. Maybe we should do a lookup for the
+ * flow that actually matches the packet? Who knows. */
+ send_buffered_packet(p, ofconn, cbdata.match, ofm);
+ return 0;
+ } else {
+ return add_flow(p, ofconn, ofm, n_actions);
+ }
+}
+
+/* Implements OFPFC_MODIFY_STRICT. Returns 0 on success or an OpenFlow error
+ * code as encoded by ofp_mkerr() on failure.
+ *
+ * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
+ * if any. */
+static int
+modify_flow_strict(struct ofproto *p, struct ofconn *ofconn,
+ struct ofp_flow_mod *ofm, size_t n_actions)