Commit message: ofproto: Fix OVS crash when reverting old flows in bundle commit

During bundle commit flows which are added in bundle are applied
to ofproto in-order. In case if a flow cannot be added (e.g. flow
action is go-to group id which does not exist), OVS tries to
revert back all previous flows which were successfully applied
from the same bundle. This is possible since OVS maintains list
of old flows which were replaced by flows from the bundle.

While reinserting old flows ovs asserts due to check on rule
state != RULE_INITIALIZED.  This will work only for new flows, but
for old flow the rule state will be RULE_REMOVED. This is causing
an assert and OVS crash.

The ovs assert check should be modified to != RULE_INSERTED to prevent
any existing rule being re-inserted and allow new rules and old rules
(in case of revert) to get inserted.

Here is an example to trigger the assert:

$ ovs-vsctl add-br br-test -- set Bridge br-test datapath_type=netdev

$ cat flows.txt
flow add table=1,priority=0,in_port=2,actions=NORMAL
flow add table=1,priority=0,in_port=3,actions=NORMAL

$ ovs-ofctl dump-flows -OOpenflow13 br-test
 cookie=0x0, duration=2.465s, table=1, n_packets=0, n_bytes=0, priority=0,in_port=2 actions=NORMAL
 cookie=0x0, duration=2.465s, table=1, n_packets=0, n_bytes=0, priority=0,in_port=3 actions=NORMAL

$ cat flow-modify.txt
flow modify table=1,priority=0,in_port=2,actions=drop
flow modify table=1,priority=0,in_port=3,actions=group:10

$ ovs-ofctl bundle br-test flow-modify.txt -OOpenflow13

First flow rule will be modified since it is a valid rule. However second
rule is invalid since no group with id 10 exists. Bundle commit tries to
revert (insert) the first rule to old flow which results in ovs_assert at
ofproto_rule_insert__() since old rule->state = RULE_REMOVED.

Signed-off-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
CVE: CVE-2018-17205
CWE: CWE-617
Vulnerability description: An issue was discovered in Open vSwitch (OvS) 2.7.x through 2.7.6, affecting ofproto_rule_insert__ in ofproto/ofproto.c. During bundle commit, flows that are added in a bundle are applied to ofproto in order. If a flow cannot be added (e.g., the flow action is a go-to for a group id that does not exist), OvS tries to revert back all previous flows that were successfully applied from the same bundle. This is possible since OvS maintains list of old flows that were replaced by flows from the bundle. While reinserting old flows, OvS has an assertion failure due to a check on rule state != RULE_INITIALIZED. This would work for new flows, but for an old flow the rule state is RULE_REMOVED. The assertion failure causes an OvS crash.

Project: ovs
File: ofproto.c

    OVS_EXCLUDED(ofproto_mutex)
{
    /* This skips the ofmonitor and flow-removed notifications because the
     * switch is being deleted and any OpenFlow channels have been or soon will
     * be killed. */
    ovs_mutex_lock(&ofproto_mutex);

    if (rule->state == RULE_INSERTED) {
        /* Make sure there is no postponed removal of the rule. */
        ovs_assert(cls_rule_visible_in_version(&rule->cr, OVS_VERSION_MAX));

        if (!classifier_remove(&rule->ofproto->tables[rule->table_id].cls,
                               &rule->cr)) {
            OVS_NOT_REACHED();
        }
        ofproto_rule_remove__(rule->ofproto, rule);
        if (ofproto->ofproto_class->rule_delete) {
            ofproto->ofproto_class->rule_delete(rule);
        }

        /* This may not be the last reference to the rule. */
        ofproto_rule_unref(rule);
    }
    ovs_mutex_unlock(&ofproto_mutex);
}

Project: ovs
File: ofproto.c

    OVS_EXCLUDED(ofproto_mutex)
{
    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
    struct ofputil_flow_stats_request request;
    struct ofputil_aggregate_stats stats;
    bool unknown_packets, unknown_bytes;
    struct rule_criteria criteria;
    struct rule_collection rules;
    struct ofpbuf *reply;
    enum ofperr error;

    error = ofputil_decode_flow_stats_request(&request, oh,
                                              ofproto_get_tun_tab(ofproto),
                                              &ofproto->vl_mff_map);
    if (error) {
        return error;
    }

    rule_criteria_init(&criteria, request.table_id, &request.match, 0,
                       OVS_VERSION_MAX, request.cookie, request.cookie_mask,
                       request.out_port, request.out_group);

    ovs_mutex_lock(&ofproto_mutex);
    error = collect_rules_loose(ofproto, &criteria, &rules);
    rule_criteria_destroy(&criteria);
    if (!error) {
        rule_collection_ref(&rules);
    }
    ovs_mutex_unlock(&ofproto_mutex);

    if (error) {
        return error;
    }

    memset(&stats, 0, sizeof stats);
    unknown_packets = unknown_bytes = false;

    struct rule *rule;
    RULE_COLLECTION_FOR_EACH (rule, &rules) {
        uint64_t packet_count;
        uint64_t byte_count;
        long long int used;

        ofproto->ofproto_class->rule_get_stats(rule, &packet_count,
                                               &byte_count, &used);

        if (packet_count == UINT64_MAX) {
            unknown_packets = true;
        } else {
            stats.packet_count += packet_count;
        }

        if (byte_count == UINT64_MAX) {
            unknown_bytes = true;
        } else {
            stats.byte_count += byte_count;
        }

        stats.flow_count++;
    }
    if (unknown_packets) {
        stats.packet_count = UINT64_MAX;
    }
    if (unknown_bytes) {
        stats.byte_count = UINT64_MAX;
    }

    rule_collection_unref(&rules);
    rule_collection_destroy(&rules);

    reply = ofputil_encode_aggregate_stats_reply(&stats, oh);
    ofconn_send_reply(ofconn, reply);

    return 0;
}

Project: ovs
File: ofproto.c

    OVS_EXCLUDED(ofproto_mutex)
{
    enum ofperr error = ofproto_flow_mod_learn_refresh(ofm);
    struct rule *rule = ofm->temp_rule;

    /* Do we need to insert the rule? */
    if (!error && rule->state == RULE_INITIALIZED) {
        ovs_mutex_lock(&ofproto_mutex);
        ofm->version = rule->ofproto->tables_version + 1;
        error = ofproto_flow_mod_learn_start(ofm);
        if (!error) {
            ofproto_flow_mod_learn_finish(ofm, NULL);
        }
        ovs_mutex_unlock(&ofproto_mutex);
    }

    if (!keep_ref) {
        ofproto_rule_unref(rule);
        ofm->temp_rule = NULL;
    }
    return error;
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    struct ofproto *ofproto = rule->ofproto;
    struct oftable *table = &ofproto->tables[rule->table_id];

    ovs_assert(!cls_rule_visible_in_version(&rule->cr, OVS_VERSION_MAX));
    if (!classifier_remove(&table->cls, &rule->cr)) {
        OVS_NOT_REACHED();
    }
    if (ofproto->ofproto_class->rule_delete) {
        ofproto->ofproto_class->rule_delete(rule);
    }
    ofproto_rule_unref(rule);
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    struct oftable *table;
    size_t n_readonly = 0;
    enum ofperr error = 0;

    rule_collection_init(rules);

    if (!check_table_id(ofproto, criteria->table_id)) {
        error = OFPERR_OFPBRC_BAD_TABLE_ID;
        goto exit;
    }

    if (criteria->cookie_mask == OVS_BE64_MAX) {
        struct rule *rule;

        HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node,
                                   hash_cookie(criteria->cookie),
                                   &ofproto->cookies) {
            if (cls_rule_equal(&rule->cr, &criteria->cr)) {
                collect_rule(rule, criteria, rules, &n_readonly);
            }
        }
    } else {
        FOR_EACH_MATCHING_TABLE (table, criteria->table_id, ofproto) {
            struct rule *rule;

            rule = rule_from_cls_rule(classifier_find_rule_exactly(
                                          &table->cls, &criteria->cr,
                                          criteria->version));
            if (rule) {
                collect_rule(rule, criteria, rules, &n_readonly);
            }
        }
    }

exit:
    if (!error && !rule_collection_n(rules) && n_readonly) {
        /* We didn't find any rules to modify.  We did find some read-only
         * rules that we're not allowed to modify, so report that. */
        error = OFPERR_OFPBRC_EPERM;
    }
    if (error) {
        rule_collection_destroy(rules);
    }
    return error;
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    enum ofperr error = 0;
    struct rule_collection rules;
    unsigned int count = table->n_flows;
    unsigned int max_flows = table->max_flows;

    rule_collection_init(&rules);

    while (count-- > max_flows) {
        struct rule *rule;

        if (!choose_rule_to_evict(table, &rule)) {
            error = OFPERR_OFPFMFC_TABLE_FULL;
            break;
        } else {
            eviction_group_remove_rule(rule);
            rule_collection_add(&rules, rule);
        }
    }
    delete_flows__(&rules, OFPRR_EVICTION, NULL);

    return error;
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    struct rule *rule = rule_collection_rules(&ofm->new_rules)[0];
    ofproto_flow_mod_revert(rule->ofproto, ofm);
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    struct rule *rule;

    RULE_COLLECTION_FOR_EACH (rule, rules) {
        enum nx_flow_monitor_flags flags = rule->monitor_flags;
        rule->monitor_flags = 0;

        ofproto_compose_flow_refresh_update(rule, flags, msgs,
                ofproto_get_tun_tab(rule->ofproto));
    }
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    if (m->flags & NXFMF_INITIAL) {
        ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
    }
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    ofproto_collect_ofmonitor_refresh_rules(m, seqno, rules);
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    struct oftable *table = &rule->ofproto->tables[rule->table_id];
    const struct mf_subfield *sf;
    struct flow flow;
    uint32_t hash;

    hash = table->eviction_group_id_basis;
    miniflow_expand(rule->cr.match.flow, &flow);
    for (sf = table->eviction_fields;
         sf < &table->eviction_fields[table->n_eviction_fields];
         sf++)
    {
        if (mf_are_prereqs_ok(sf->field, &flow, NULL)) {
            union mf_value value;

            mf_get_value(sf->field, &flow, &value);
            if (sf->ofs) {
                bitwise_zero(&value, sf->field->n_bytes, 0, sf->ofs);
            }
            if (sf->ofs + sf->n_bits < sf->field->n_bytes * 8) {
                unsigned int start = sf->ofs + sf->n_bits;
                bitwise_zero(&value, sf->field->n_bytes, start,
                             sf->field->n_bytes * 8 - start);
            }
            hash = hash_bytes(&value, sf->field->n_bytes, hash);
        } else {
            hash = hash_int(hash, 0);
        }
    }

    return hash;
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    struct ofproto *ofproto = rule->ofproto;
    struct oftable *table = &ofproto->tables[rule->table_id];
    bool has_timeout;

    /* Timeouts may be modified only when holding 'ofproto_mutex'.  We have it
     * so no additional protection is needed. */
    has_timeout = rule->hard_timeout || rule->idle_timeout;

    if (table->eviction && has_timeout) {
        struct eviction_group *evg;

        evg = eviction_group_find(table, eviction_group_hash_rule(rule));

        rule->eviction_group = evg;
        heap_insert(&evg->rules, &rule->evg_node,
                    rule_eviction_priority(ofproto, rule));
        eviction_group_resized(table, evg);
    }
}

Project: ovs
File: ofproto.c

    OVS_REQUIRES(ofproto_mutex)
{
    ovs_assert(rule->state == RULE_INSERTED);

    cookies_remove(ofproto, rule);

    eviction_group_remove_rule(rule);
    if (!ovs_list_is_empty(&rule->expirable)) {
        ovs_list_remove(&rule->expirable);
    }
    if (!ovs_list_is_empty(&rule->meter_list_node)) {
        ovs_list_remove(&rule->meter_list_node);
        ovs_list_init(&rule->meter_list_node);
    }

    /* Remove the rule from any groups, except from the group that is being
     * deleted, if any. */
    const struct rule_actions *actions = rule_get_actions(rule);

    if (actions->has_groups) {
        const struct ofpact_group *a;

        OFPACT_FOR_EACH_TYPE_FLATTENED(a, GROUP, actions->ofpacts,
                                        actions->ofpacts_len) {
            struct ofgroup *group;

            group = ofproto_group_lookup(ofproto, a->group_id, OVS_VERSION_MAX,
                                         false);
            ovs_assert(group);

            /* Leave the rule for the group that is being deleted, if any,
             * as we still need the list of rules for clean-up. */
            if (!group->being_deleted) {
                group_remove_rule(group, rule);
            }
        }
    }

    rule->state = RULE_REMOVED;
}

Project: ovs
File: ofproto.c

check_table_id(const struct ofproto *ofproto, uint8_t table_id)
{
    return table_id == OFPTT_ALL || table_id < ofproto->n_tables;
}

Project: ovs
File: ofproto.c

group_remove_rule(struct ofgroup *group, struct rule *rule)
{
    rule_collection_remove(&group->rules, rule);
}

Project: ovs
File: ofproto.c

handle_bundle_control(struct ofconn *ofconn, const struct ofp_header *oh)
{
    struct ofputil_bundle_ctrl_msg bctrl;
    struct ofputil_bundle_ctrl_msg reply;
    struct ofpbuf *buf;
    enum ofperr error;

    error = reject_slave_controller(ofconn);
    if (error) {
        return error;
    }

    error = ofputil_decode_bundle_ctrl(oh, &bctrl);
    if (error) {
        return error;
    }
    reply.flags = 0;
    reply.bundle_id = bctrl.bundle_id;

    switch (bctrl.type) {
    case OFPBCT_OPEN_REQUEST:
        error = ofp_bundle_open(ofconn, bctrl.bundle_id, bctrl.flags, oh);
        reply.type = OFPBCT_OPEN_REPLY;
        break;
    case OFPBCT_CLOSE_REQUEST:
        error = ofp_bundle_close(ofconn, bctrl.bundle_id, bctrl.flags);
        reply.type = OFPBCT_CLOSE_REPLY;
        break;
    case OFPBCT_COMMIT_REQUEST:
        error = do_bundle_commit(ofconn, bctrl.bundle_id, bctrl.flags);
        reply.type = OFPBCT_COMMIT_REPLY;
        break;
    case OFPBCT_DISCARD_REQUEST:
        error = ofp_bundle_discard(ofconn, bctrl.bundle_id);
        reply.type = OFPBCT_DISCARD_REPLY;
        break;

    case OFPBCT_OPEN_REPLY:
    case OFPBCT_CLOSE_REPLY:
    case OFPBCT_COMMIT_REPLY:
    case OFPBCT_DISCARD_REPLY:
        return OFPERR_OFPBFC_BAD_TYPE;
        break;
    }

    if (!error) {
        buf = ofputil_encode_bundle_ctrl_reply(oh, &reply);
        ofconn_send_reply(ofconn, buf);
    }
    return error;
}

Project: ovs
File: ofproto.c

handle_group_desc_stats_request(struct ofconn *ofconn,
                                const struct ofp_header *request)
{
    handle_group_request(ofconn, request,
                         ofputil_decode_group_desc_request(request),
                         append_group_desc);
    return 0;
}

Project: ovs
File: ofproto.c

init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm,
           ovs_version_t version, struct ofgroup **ofgroup)
{
    enum ofperr error;
    const long long int now = time_msec();

    if (gm->group_id > OFPG_MAX) {
        return OFPERR_OFPGMFC_INVALID_GROUP;
    }
    if (gm->type > OFPGT11_FF) {
        return OFPERR_OFPGMFC_BAD_TYPE;
    }

    *ofgroup = ofproto->ofproto_class->group_alloc();
    if (!*ofgroup) {
        VLOG_WARN_RL(&rl, "%s: failed to allocate group", ofproto->name);
        return OFPERR_OFPGMFC_OUT_OF_GROUPS;
    }

    *CONST_CAST(struct ofproto **, &(*ofgroup)->ofproto) = ofproto;
    *CONST_CAST(uint32_t *, &((*ofgroup)->group_id)) = gm->group_id;
    *CONST_CAST(enum ofp11_group_type *, &(*ofgroup)->type) = gm->type;
    *CONST_CAST(long long int *, &((*ofgroup)->created)) = now;
    *CONST_CAST(long long int *, &((*ofgroup)->modified)) = now;
    ovs_refcount_init(&(*ofgroup)->ref_count);
    (*ofgroup)->being_deleted = false;

    ovs_list_init(CONST_CAST(struct ovs_list *, &(*ofgroup)->buckets));
    ofputil_bucket_clone_list(CONST_CAST(struct ovs_list *,
                                         &(*ofgroup)->buckets),
                              &gm->buckets, NULL);

    *CONST_CAST(uint32_t *, &(*ofgroup)->n_buckets) =
        ovs_list_size(&(*ofgroup)->buckets);

    ofputil_group_properties_copy(CONST_CAST(struct ofputil_group_props *,
                                             &(*ofgroup)->props),
                                  &gm->props);
    rule_collection_init(&(*ofgroup)->rules);

    /* Make group visible from 'version'. */
    (*ofgroup)->versions = VERSIONS_INITIALIZER(version,
                                                OVS_VERSION_NOT_REMOVED);

    /* Construct called BEFORE any locks are held. */
    error = ofproto->ofproto_class->group_construct(*ofgroup);
    if (error) {
        ofputil_group_properties_destroy(CONST_CAST(struct ofputil_group_props *,
                                                    &(*ofgroup)->props));
        ofputil_bucket_list_destroy(CONST_CAST(struct ovs_list *,
                                               &(*ofgroup)->buckets));
        ofproto->ofproto_class->group_dealloc(*ofgroup);
    }
    return error;
}

Project: ovs
File: ofproto.c

ofport_remove_with_name(struct ofproto *ofproto, const char *name)
{
    struct ofport *port = shash_find_data(&ofproto->port_by_name, name);
    if (port) {
        ofport_remove(port);
    }
}

Project: ovs
File: ofproto.c

ofproto_enumerate_types(struct sset *types)
{
    size_t i;

    sset_clear(types);
    for (i = 0; i < n_ofproto_classes; i++) {
        ofproto_classes[i]->enumerate_types(types);
    }
}

Project: ovs
File: ofproto.c

ofproto_normalize_type(const char *type)
{
    return type && type[0] ? type : "system";
}

Project: ovs
File: ofproto.c

ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
                 ofp_port_t *ofp_portp)
{
    ofp_port_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE;
    int error;

    error = ofproto->ofproto_class->port_add(ofproto, netdev);
    if (!error) {
        const char *netdev_name = netdev_get_name(netdev);

        simap_put(&ofproto->ofp_requests, netdev_name,
                  ofp_to_u16(ofp_port));
        error = update_port(ofproto, netdev_name);
    }
    if (ofp_portp) {
        *ofp_portp = OFPP_NONE;
        if (!error) {
            struct ofproto_port ofproto_port;

            error = ofproto_port_query_by_name(ofproto,
                                               netdev_get_name(netdev),
                                               &ofproto_port);
            if (!error) {
                *ofp_portp = ofproto_port.ofp_port;
                ofproto_port_destroy(&ofproto_port);
            }
        }
    }
    return error;
}

Project: ovs
File: ofproto.c

ofproto_port_cfm_status_changed(struct ofproto *ofproto, ofp_port_t ofp_port)
{
    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
    return (ofport && ofproto->ofproto_class->cfm_status_changed
            ? ofproto->ofproto_class->cfm_status_changed(ofport)
            : true);
}

Project: ovs
File: ofproto.c

ofproto_port_clear_cfm(struct ofproto *ofproto, ofp_port_t ofp_port)
{
    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
    if (ofport && ofproto->ofproto_class->set_cfm) {
        ofproto->ofproto_class->set_cfm(ofport, NULL);
    }
}

Project: ovs
File: ofproto.c

ofproto_port_del(struct ofproto *ofproto, ofp_port_t ofp_port)
{
    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
    const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
    struct simap_node *ofp_request_node;
    int error;

    ofp_request_node = simap_find(&ofproto->ofp_requests, name);
    if (ofp_request_node) {
        simap_delete(&ofproto->ofp_requests, ofp_request_node);
    }

    error = ofproto->ofproto_class->port_del(ofproto, ofp_port);
    if (!error && ofport) {
        /* 'name' is the netdev's name and update_port() is going to close the
         * netdev.  Just in case update_port() refers to 'name' after it
         * destroys 'ofport', make a copy of it around the update_port()
         * call. */
        char *devname = xstrdup(name);
        update_port(ofproto, devname);
        free(devname);
    }
    return error;
}

Project: ovs
File: ofproto.c

ofproto_port_set_rstp(struct ofproto *ofproto, ofp_port_t ofp_port,
                      const struct ofproto_port_rstp_settings *s)
{
    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
    if (!ofport) {
        VLOG_WARN("%s: cannot configure RSTP on nonexistent port %"PRIu32,
                ofproto->name, ofp_port);
        return ENODEV;
    }

    if (!ofproto->ofproto_class->set_rstp_port) {
        return  EOPNOTSUPP;
    }
    ofproto->ofproto_class->set_rstp_port(ofport, s);
    return 0;
}

Project: ovs
File: ofproto.c

ofproto_rule_ref(struct rule *rule)
{
    if (rule) {
        ovs_refcount_ref(&rule->ref_count);
    }
}

Project: ovs
File: ofproto.c

ofproto_set_cpu_mask(const char *cmask)
{
    free(pmd_cpu_mask);
    pmd_cpu_mask = nullable_xstrdup(cmask);
}

Project: ovs
File: ofproto.c

ofproto_set_dp_desc(struct ofproto *p, const char *dp_desc)
{
    free(p->dp_desc);
    p->dp_desc = nullable_xstrdup(dp_desc);
}

Project: ovs
File: ofproto.c

ofproto_set_mac_table_config(struct ofproto *ofproto, unsigned idle_time,
                             size_t max_entries)
{
    if (ofproto->ofproto_class->set_mac_table_config) {
        ofproto->ofproto_class->set_mac_table_config(ofproto, idle_time,
                                                     max_entries);
    }
}

Project: ovs
File: ofproto.c

ofproto_type_wait(const char *datapath_type)
{
    const struct ofproto_class *class;

    datapath_type = ofproto_normalize_type(datapath_type);
    class = ofproto_class_find__(datapath_type);

    if (class->type_wait) {
        class->type_wait(datapath_type);
    }
}

Project: ovs
File: ofproto.c

pick_datapath_id(const struct ofproto *ofproto)
{
    const struct ofport *port;

    port = ofproto_get_port(ofproto, OFPP_LOCAL);
    if (port) {
        struct eth_addr ea;
        int error;

        error = netdev_get_etheraddr(port->netdev, &ea);
        if (!error) {
            return eth_addr_to_uint64(ea);
        }
        VLOG_WARN("%s: could not get MAC address for %s (%s)",
                  ofproto->name, netdev_get_name(port->netdev),
                  ovs_strerror(error));
    }
    return ofproto->fallback_dpid;
}

Project: ovs
File: ofproto.c

query_table_desc__(struct ofputil_table_desc *td,
                   struct ofproto *ofproto, uint8_t table_id)
{
    const struct oftable *t = &ofproto->tables[table_id];

    td->table_id = table_id;
    td->eviction = (t->eviction & EVICTION_OPENFLOW
                    ? OFPUTIL_TABLE_EVICTION_ON
                    : OFPUTIL_TABLE_EVICTION_OFF);
    td->eviction_flags = OFPROTO_EVICTION_FLAGS;
    td->vacancy = (t->vacancy_event
                   ? OFPUTIL_TABLE_VACANCY_ON
                   : OFPUTIL_TABLE_VACANCY_OFF);
    td->table_vacancy.vacancy_down = t->vacancy_down;
    td->table_vacancy.vacancy_up = t->vacancy_up;
    td->table_vacancy.vacancy = oftable_vacancy(t);
}

Project: ovs
File: ofproto.c

replace_rule_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
                   struct rule *old_rule, struct rule *new_rule)
{
    struct oftable *table = &ofproto->tables[new_rule->table_id];

    /* 'old_rule' may be either an evicted rule or replaced rule. */
    if (old_rule) {
        /* Copy values from old rule for modify semantics. */
        if (old_rule->removed_reason != OFPRR_EVICTION) {
            bool change_cookie = (ofm->modify_cookie
                                  && new_rule->flow_cookie != OVS_BE64_MAX
                                  && new_rule->flow_cookie != old_rule->flow_cookie);

            ovs_mutex_lock(&new_rule->mutex);
            ovs_mutex_lock(&old_rule->mutex);
            if (ofm->command != OFPFC_ADD) {
                new_rule->idle_timeout = old_rule->idle_timeout;
                new_rule->hard_timeout = old_rule->hard_timeout;
                *CONST_CAST(uint16_t *, &new_rule->importance) = old_rule->importance;
                new_rule->flags = old_rule->flags;
                new_rule->created = old_rule->created;
            }
            if (!change_cookie) {
                *CONST_CAST(ovs_be64 *, &new_rule->flow_cookie)
                    = old_rule->flow_cookie;
            }
            ovs_mutex_unlock(&old_rule->mutex);
            ovs_mutex_unlock(&new_rule->mutex);
        }

        /* Mark the old rule for removal in the next version. */
        cls_rule_make_invisible_in_version(&old_rule->cr, ofm->version);

        /* Remove the old rule from data structures. */
        ofproto_rule_remove__(ofproto, old_rule);
    } else {
        table->n_flows++;
    }
    /* Insert flow to ofproto data structures, so that later flow_mods may
     * relate to it.  This is reversible, in case later errors require this to
     * be reverted. */
    ofproto_rule_insert__(ofproto, new_rule);
    /* Make the new rule visible for classifier lookups only from the next
     * version. */
    classifier_insert(&table->cls, &new_rule->cr, ofm->version, ofm->conjs,
                      ofm->n_conjs);
}