- struct odp_port odp_port;
- struct ofport *old_ofport;
- struct ofport *new_ofport;
- int error;
-
- COVERAGE_INC(ofproto_update_port);
-
- /* Query the datapath for port information. */
- error = dpif_port_query_by_name(p->dpif, devname, &odp_port);
-
- /* Find the old ofport. */
- old_ofport = shash_find_data(&p->port_by_name, devname);
- if (!error) {
- if (!old_ofport) {
- /* There's no port named 'devname' but there might be a port with
- * the same port number. This could happen if a port is deleted
- * and then a new one added in its place very quickly, or if a port
- * is renamed. In the former case we want to send an OFPPR_DELETE
- * and an OFPPR_ADD, and in the latter case we want to send a
- * single OFPPR_MODIFY. We can distinguish the cases by comparing
- * the old port's ifindex against the new port, or perhaps less
- * reliably but more portably by comparing the old port's MAC
- * against the new port's MAC. However, this code isn't that smart
- * and always sends an OFPPR_MODIFY (XXX). */
- old_ofport = get_port(p, odp_port.port);
- }
- } else if (error != ENOENT && error != ENODEV) {
- VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error "
- "%s", strerror(error));
- return;
- }
-
- /* Create a new ofport. */
- new_ofport = !error ? make_ofport(&odp_port) : NULL;
-
- /* Eliminate a few pathological cases. */
- if (!old_ofport && !new_ofport) {
- return;
- } else if (old_ofport && new_ofport) {
- /* Most of the 'config' bits are OpenFlow soft state, but
- * OFPPC_PORT_DOWN is maintained the kernel. So transfer the OpenFlow
- * bits from old_ofport. (make_ofport() only sets OFPPC_PORT_DOWN and
- * leaves the other bits 0.) */
- new_ofport->opp.config |= old_ofport->opp.config & ~OFPPC_PORT_DOWN;
-
- if (ofport_equal(old_ofport, new_ofport)) {
- /* False alarm--no change. */
- ofport_free(new_ofport);
- return;
- }
- }
-
- /* Now deal with the normal cases. */
- if (old_ofport) {
- ofport_remove(p, old_ofport);
- }
- if (new_ofport) {
- ofport_install(p, new_ofport);
- }
- send_port_status(p, new_ofport ? new_ofport : old_ofport,
- (!old_ofport ? OFPPR_ADD
- : !new_ofport ? OFPPR_DELETE
- : OFPPR_MODIFY));
- ofport_free(old_ofport);
-}
-
-static int
-init_ports(struct ofproto *p)
-{
- struct odp_port *ports;
- size_t n_ports;
- size_t i;
- int error;
-
- error = dpif_port_list(p->dpif, &ports, &n_ports);
- if (error) {
- return error;
- }
-
- for (i = 0; i < n_ports; i++) {
- const struct odp_port *odp_port = &ports[i];
- if (!ofport_conflicts(p, odp_port)) {
- struct ofport *ofport = make_ofport(odp_port);
- if (ofport) {
- ofport_install(p, ofport);
- }
- }
- }
- free(ports);
- return 0;
-}
-\f
-static struct ofconn *
-ofconn_create(struct ofproto *p, struct rconn *rconn, enum ofconn_type type)
-{
- struct ofconn *ofconn = xzalloc(sizeof *ofconn);
- ofconn->ofproto = p;
- list_push_back(&p->all_conns, &ofconn->node);
- ofconn->rconn = rconn;
- ofconn->type = type;
- ofconn->flow_format = NXFF_OPENFLOW10;
- ofconn->role = NX_ROLE_OTHER;
- ofconn->packet_in_counter = rconn_packet_counter_create ();
- ofconn->pktbuf = NULL;
- ofconn->miss_send_len = 0;
- ofconn->reply_counter = rconn_packet_counter_create ();
- return ofconn;
-}
-
-static void
-ofconn_destroy(struct ofconn *ofconn)
-{
- if (ofconn->type == OFCONN_PRIMARY) {
- hmap_remove(&ofconn->ofproto->controllers, &ofconn->hmap_node);
- }
- discovery_destroy(ofconn->discovery);
-
- list_remove(&ofconn->node);
- switch_status_unregister(ofconn->ss);
- rconn_destroy(ofconn->rconn);
- rconn_packet_counter_destroy(ofconn->packet_in_counter);
- rconn_packet_counter_destroy(ofconn->reply_counter);
- pktbuf_destroy(ofconn->pktbuf);
- free(ofconn);
-}
-
-static void
-ofconn_run(struct ofconn *ofconn)
-{
- struct ofproto *p = ofconn->ofproto;
- int iteration;
- size_t i;
-
- if (ofconn->discovery) {
- char *controller_name;
- if (rconn_is_connectivity_questionable(ofconn->rconn)) {
- discovery_question_connectivity(ofconn->discovery);
- }
- if (discovery_run(ofconn->discovery, &controller_name)) {
- if (controller_name) {
- char *ofconn_name = ofconn_make_name(p, controller_name);
- rconn_connect(ofconn->rconn, controller_name, ofconn_name);
- free(ofconn_name);
- } else {
- rconn_disconnect(ofconn->rconn);
- }
- }
- }
-
- for (i = 0; i < N_SCHEDULERS; i++) {
- pinsched_run(ofconn->schedulers[i], do_send_packet_in, ofconn);
- }
-
- rconn_run(ofconn->rconn);
-
- if (rconn_packet_counter_read (ofconn->reply_counter) < OFCONN_REPLY_MAX) {
- /* Limit the number of iterations to prevent other tasks from
- * starving. */
- for (iteration = 0; iteration < 50; iteration++) {
- struct ofpbuf *of_msg = rconn_recv(ofconn->rconn);
- if (!of_msg) {
- break;
- }
- if (p->fail_open) {
- fail_open_maybe_recover(p->fail_open);
- }
- handle_openflow(ofconn, of_msg);
- ofpbuf_delete(of_msg);
- }
- }
-
- if (!ofconn->discovery && !rconn_is_alive(ofconn->rconn)) {
- ofconn_destroy(ofconn);
- }
-}
-
-static void
-ofconn_wait(struct ofconn *ofconn)
-{
- int i;
-
- if (ofconn->discovery) {
- discovery_wait(ofconn->discovery);
- }
- for (i = 0; i < N_SCHEDULERS; i++) {
- pinsched_wait(ofconn->schedulers[i]);
- }
- rconn_run_wait(ofconn->rconn);
- if (rconn_packet_counter_read (ofconn->reply_counter) < OFCONN_REPLY_MAX) {
- rconn_recv_wait(ofconn->rconn);
- } else {
- COVERAGE_INC(ofproto_ofconn_stuck);
- }
-}
-
-/* Returns true if 'ofconn' should receive asynchronous messages. */
-static bool
-ofconn_receives_async_msgs(const struct ofconn *ofconn)
-{
- if (ofconn->type == OFCONN_PRIMARY) {
- /* Primary controllers always get asynchronous messages unless they
- * have configured themselves as "slaves". */
- return ofconn->role != NX_ROLE_SLAVE;
- } else {
- /* Service connections don't get asynchronous messages unless they have
- * explicitly asked for them by setting a nonzero miss send length. */
- return ofconn->miss_send_len > 0;
- }
-}
-
-/* Returns a human-readable name for an OpenFlow connection between 'ofproto'
- * and 'target', suitable for use in log messages for identifying the
- * connection.
- *
- * The name is dynamically allocated. The caller should free it (with free())
- * when it is no longer needed. */
-static char *
-ofconn_make_name(const struct ofproto *ofproto, const char *target)
-{
- return xasprintf("%s<->%s", dpif_base_name(ofproto->dpif), target);
-}
-
-static void
-ofconn_set_rate_limit(struct ofconn *ofconn, int rate, int burst)
-{
- int i;
-
- for (i = 0; i < N_SCHEDULERS; i++) {
- struct pinsched **s = &ofconn->schedulers[i];
-
- if (rate > 0) {
- if (!*s) {
- *s = pinsched_create(rate, burst,
- ofconn->ofproto->switch_status);
- } else {
- pinsched_set_limits(*s, rate, burst);
- }
- } else {
- pinsched_destroy(*s);
- *s = NULL;
- }
- }
-}
-\f
-static void
-ofservice_reconfigure(struct ofservice *ofservice,
- const struct ofproto_controller *c)
-{
- ofservice->probe_interval = c->probe_interval;
- ofservice->rate_limit = c->rate_limit;
- ofservice->burst_limit = c->burst_limit;
-}
-
-/* Creates a new ofservice in 'ofproto'. Returns 0 if successful, otherwise a
- * positive errno value. */
-static int
-ofservice_create(struct ofproto *ofproto, const struct ofproto_controller *c)
-{
- struct ofservice *ofservice;
- struct pvconn *pvconn;
- int error;
-
- error = pvconn_open(c->target, &pvconn);
- if (error) {
- return error;
- }
-
- ofservice = xzalloc(sizeof *ofservice);
- hmap_insert(&ofproto->services, &ofservice->node,
- hash_string(c->target, 0));
- ofservice->pvconn = pvconn;
-
- ofservice_reconfigure(ofservice, c);
-
- return 0;
-}
-
-static void
-ofservice_destroy(struct ofproto *ofproto, struct ofservice *ofservice)
-{
- hmap_remove(&ofproto->services, &ofservice->node);
- pvconn_close(ofservice->pvconn);
- free(ofservice);
-}
-
-/* Finds and returns the ofservice within 'ofproto' that has the given
- * 'target', or a null pointer if none exists. */
-static struct ofservice *
-ofservice_lookup(struct ofproto *ofproto, const char *target)
-{
- struct ofservice *ofservice;
-
- HMAP_FOR_EACH_WITH_HASH (ofservice, node, hash_string(target, 0),
- &ofproto->services) {
- if (!strcmp(pvconn_get_name(ofservice->pvconn), target)) {
- return ofservice;
- }
- }
- return NULL;
-}
-\f
-/* Caller is responsible for initializing the 'cr' member of the returned
- * rule. */
-static struct rule *
-rule_create(struct ofproto *ofproto, struct rule *super,
- const union ofp_action *actions, size_t n_actions,
- uint16_t idle_timeout, uint16_t hard_timeout,
- ovs_be64 flow_cookie, bool send_flow_removed)
-{
- struct rule *rule = xzalloc(sizeof *rule);
- rule->idle_timeout = idle_timeout;
- rule->hard_timeout = hard_timeout;
- rule->flow_cookie = flow_cookie;
- rule->used = rule->created = time_msec();
- rule->send_flow_removed = send_flow_removed;
- rule->super = super;
- if (super) {
- list_push_back(&super->list, &rule->list);
- } else {
- list_init(&rule->list);
- }
- if (n_actions > 0) {
- rule->n_actions = n_actions;
- rule->actions = xmemdup(actions, n_actions * sizeof *actions);
- }
- netflow_flow_init(&rule->nf_flow);
- netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, rule->created);
-
- return rule;
-}
-
-static struct rule *
-rule_from_cls_rule(const struct cls_rule *cls_rule)
-{
- return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
-}
-
-static void
-rule_free(struct rule *rule)
-{
- free(rule->actions);
- free(rule->odp_actions);
- free(rule);
-}
-
-/* Destroys 'rule'. If 'rule' is a subrule, also removes it from its
- * super-rule's list of subrules. If 'rule' is a super-rule, also iterates
- * through all of its subrules and revalidates them, destroying any that no
- * longer has a super-rule (which is probably all of them).
- *
- * Before calling this function, the caller must make have removed 'rule' from
- * the classifier. If 'rule' is an exact-match rule, the caller is also
- * responsible for ensuring that it has been uninstalled from the datapath. */
-static void
-rule_destroy(struct ofproto *ofproto, struct rule *rule)
-{
- if (!rule->super) {
- struct rule *subrule, *next;
- LIST_FOR_EACH_SAFE (subrule, next, list, &rule->list) {
- revalidate_rule(ofproto, subrule);
- }
- } else {
- list_remove(&rule->list);
- }
- rule_free(rule);
-}
-
-static bool
-rule_has_out_port(const struct rule *rule, ovs_be16 out_port)
-{
- const union ofp_action *oa;
- struct actions_iterator i;
-
- if (out_port == htons(OFPP_NONE)) {
- return true;
- }
- for (oa = actions_first(&i, rule->actions, rule->n_actions); oa;
- oa = actions_next(&i)) {
- if (action_outputs_to_port(oa, out_port)) {
- return true;
- }
- }
- return false;
-}
-
-/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
- * 'packet', which arrived on 'in_port'.
- *
- * Takes ownership of 'packet'. */
-static bool
-execute_odp_actions(struct ofproto *ofproto, uint16_t in_port,
- const union odp_action *actions, size_t n_actions,
- struct ofpbuf *packet)
-{
- if (n_actions == 1 && actions[0].type == ODPAT_CONTROLLER) {
- /* As an optimization, avoid a round-trip from userspace to kernel to
- * userspace. This also avoids possibly filling up kernel packet
- * buffers along the way. */
- struct odp_msg *msg;
-
- msg = ofpbuf_push_uninit(packet, sizeof *msg);
- msg->type = _ODPL_ACTION_NR;
- msg->length = sizeof(struct odp_msg) + packet->size;
- msg->port = in_port;
- msg->reserved = 0;
- msg->arg = actions[0].controller.arg;
-
- send_packet_in(ofproto, packet);
-
- return true;
- } else {
- int error;
-
- error = dpif_execute(ofproto->dpif, actions, n_actions, packet);
- ofpbuf_delete(packet);
- return !error;
- }
-}
-
-/* Executes the actions indicated by 'rule' on 'packet', which is in flow
- * 'flow' and is considered to have arrived on ODP port 'in_port'. 'packet'
- * must have at least sizeof(struct ofp_packet_in) bytes of headroom.
- *
- * The flow that 'packet' actually contains does not need to actually match
- * 'rule'; the actions in 'rule' will be applied to it either way. Likewise,
- * the packet and byte counters for 'rule' will be credited for the packet sent
- * out whether or not the packet actually matches 'rule'.
- *
- * If 'rule' is an exact-match rule and 'flow' actually equals the rule's flow,
- * the caller must already have accurately composed ODP actions for it given
- * 'packet' using rule_make_actions(). If 'rule' is a wildcard rule, or if
- * 'rule' is an exact-match rule but 'flow' is not the rule's flow, then this
- * function will compose a set of ODP actions based on 'rule''s OpenFlow
- * actions and apply them to 'packet'.
- *
- * Takes ownership of 'packet'. */
-static void
-rule_execute(struct ofproto *ofproto, struct rule *rule,
- struct ofpbuf *packet, const struct flow *flow)
-{
- const union odp_action *actions;
- struct odp_flow_stats stats;
- size_t n_actions;
- struct odp_actions a;
-
- assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
-
- /* Grab or compose the ODP actions.
- *
- * The special case for an exact-match 'rule' where 'flow' is not the
- * rule's flow is important to avoid, e.g., sending a packet out its input
- * port simply because the ODP actions were composed for the wrong
- * scenario. */
- if (rule->cr.wc.wildcards || !flow_equal(flow, &rule->cr.flow)) {
- struct rule *super = rule->super ? rule->super : rule;
- if (xlate_actions(super->actions, super->n_actions, flow, ofproto,
- packet, &a, NULL, 0, NULL)) {
- ofpbuf_delete(packet);
- return;
- }
- actions = a.actions;
- n_actions = a.n_actions;
- } else {
- actions = rule->odp_actions;
- n_actions = rule->n_odp_actions;
- }
-
- /* Execute the ODP actions. */
- flow_extract_stats(flow, packet, &stats);
- if (execute_odp_actions(ofproto, flow->in_port,
- actions, n_actions, packet)) {
- update_stats(ofproto, rule, &stats);
- rule->used = time_msec();
- netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, rule->used);
- }
-}
-
-/* Inserts 'rule' into 'p''s flow table.
- *
- * If 'packet' is nonnull, takes ownership of 'packet', executes 'rule''s
- * actions on it and credits the statistics for sending the packet to 'rule'.
- * 'packet' must have at least sizeof(struct ofp_packet_in) bytes of
- * headroom. */
-static void
-rule_insert(struct ofproto *p, struct rule *rule, struct ofpbuf *packet,
- uint16_t in_port)
-{
- struct rule *displaced_rule;
-
- /* Insert the rule in the classifier. */
- displaced_rule = rule_from_cls_rule(classifier_insert(&p->cls, &rule->cr));
- if (!rule->cr.wc.wildcards) {
- rule_make_actions(p, rule, packet);
- }
-
- /* Send the packet and credit it to the rule. */
- if (packet) {
- struct flow flow;
- flow_extract(packet, 0, in_port, &flow);
- rule_execute(p, rule, packet, &flow);
- }
-
- /* Install the rule in the datapath only after sending the packet, to
- * avoid packet reordering. */
- if (rule->cr.wc.wildcards) {
- COVERAGE_INC(ofproto_add_wc_flow);
- p->need_revalidate = true;
- } else {
- rule_install(p, rule, displaced_rule);
- }
-
- /* Free the rule that was displaced, if any. */
- if (displaced_rule) {
- rule_destroy(p, displaced_rule);
- }
-}
-
-static struct rule *
-rule_create_subrule(struct ofproto *ofproto, struct rule *rule,
- const struct flow *flow)
-{
- struct rule *subrule = rule_create(ofproto, rule, NULL, 0,
- rule->idle_timeout, rule->hard_timeout,
- 0, false);
- COVERAGE_INC(ofproto_subrule_create);
- cls_rule_init_exact(flow, (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
- : rule->cr.priority),
- &subrule->cr);
-
- if (classifier_insert(&ofproto->cls, &subrule->cr)) {
- /* Can't happen, */
- NOT_REACHED();
- }
-
- return subrule;
-}
-
-/* Remove 'rule' from 'ofproto' and free up the associated memory:
- *
- * - If 'rule' was installed in the datapath, uninstalls it and updates
- * 'rule''s statistics (or its super-rule's statistics, if it is a
- * subrule), via rule_uninstall().
- *
- * - Removes 'rule' from the classifier.
- *
- * - If 'rule' is a super-rule that has subrules, revalidates (and possibly
- * uninstalls and destroys) its subrules, via rule_destroy().
- */
-static void
-rule_remove(struct ofproto *ofproto, struct rule *rule)
-{
- if (rule->cr.wc.wildcards) {
- COVERAGE_INC(ofproto_del_wc_flow);
- ofproto->need_revalidate = true;
- } else {
- rule_uninstall(ofproto, rule);
- }
- classifier_remove(&ofproto->cls, &rule->cr);
- rule_destroy(ofproto, rule);
-}
-
-/* Returns true if the actions changed, false otherwise. */
-static bool
-rule_make_actions(struct ofproto *p, struct rule *rule,
- const struct ofpbuf *packet)
-{
- const struct rule *super;
- struct odp_actions a;
- size_t actions_len;
-
- assert(!rule->cr.wc.wildcards);
-
- super = rule->super ? rule->super : rule;
- xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
- packet, &a, &rule->tags, &rule->may_install,
- &rule->nf_flow.output_iface);
-
- actions_len = a.n_actions * sizeof *a.actions;
- if (rule->n_odp_actions != a.n_actions
- || memcmp(rule->odp_actions, a.actions, actions_len)) {
- COVERAGE_INC(ofproto_odp_unchanged);
- free(rule->odp_actions);
- rule->n_odp_actions = a.n_actions;
- rule->odp_actions = xmemdup(a.actions, actions_len);
- return true;
- } else {
- return false;
- }
-}
-
-static int
-do_put_flow(struct ofproto *ofproto, struct rule *rule, int flags,
- struct odp_flow_put *put)
-{
- memset(&put->flow.stats, 0, sizeof put->flow.stats);
- odp_flow_key_from_flow(&put->flow.key, &rule->cr.flow);
- put->flow.actions = rule->odp_actions;
- put->flow.n_actions = rule->n_odp_actions;
- put->flow.flags = 0;
- put->flags = flags;
- return dpif_flow_put(ofproto->dpif, put);
-}
-
-static void
-rule_install(struct ofproto *p, struct rule *rule, struct rule *displaced_rule)
-{
- assert(!rule->cr.wc.wildcards);
-
- if (rule->may_install) {
- struct odp_flow_put put;
- if (!do_put_flow(p, rule,
- ODPPF_CREATE | ODPPF_MODIFY | ODPPF_ZERO_STATS,
- &put)) {
- rule->installed = true;
- if (displaced_rule) {
- update_stats(p, displaced_rule, &put.flow.stats);
- rule_post_uninstall(p, displaced_rule);
- }
- }
- } else if (displaced_rule) {
- rule_uninstall(p, displaced_rule);
- }
-}
-
-static void
-rule_reinstall(struct ofproto *ofproto, struct rule *rule)
-{
- if (rule->installed) {
- struct odp_flow_put put;
- COVERAGE_INC(ofproto_dp_missed);
- do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY, &put);
- } else {
- rule_install(ofproto, rule, NULL);
- }
-}
-
-static void
-rule_update_actions(struct ofproto *ofproto, struct rule *rule)
-{
- bool actions_changed;
- uint16_t new_out_iface, old_out_iface;
-
- old_out_iface = rule->nf_flow.output_iface;
- actions_changed = rule_make_actions(ofproto, rule, NULL);
-
- if (rule->may_install) {
- if (rule->installed) {
- if (actions_changed) {
- struct odp_flow_put put;
- do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY
- | ODPPF_ZERO_STATS, &put);
- update_stats(ofproto, rule, &put.flow.stats);
-
- /* Temporarily set the old output iface so that NetFlow
- * messages have the correct output interface for the old
- * stats. */
- new_out_iface = rule->nf_flow.output_iface;
- rule->nf_flow.output_iface = old_out_iface;
- rule_post_uninstall(ofproto, rule);
- rule->nf_flow.output_iface = new_out_iface;
- }
- } else {
- rule_install(ofproto, rule, NULL);
- }
- } else {
- rule_uninstall(ofproto, rule);
- }
-}
-
-static void
-rule_account(struct ofproto *ofproto, struct rule *rule, uint64_t extra_bytes)
-{
- uint64_t total_bytes = rule->byte_count + extra_bytes;
-
- if (ofproto->ofhooks->account_flow_cb
- && total_bytes > rule->accounted_bytes)
- {
- ofproto->ofhooks->account_flow_cb(
- &rule->cr.flow, rule->tags, rule->odp_actions, rule->n_odp_actions,
- total_bytes - rule->accounted_bytes, ofproto->aux);
- rule->accounted_bytes = total_bytes;
- }
-}
-
-/* 'rule' must be an exact-match rule in 'p'.
- *
- * If 'rule' is installed in the datapath, uninstalls it and updates's
- * statistics. If 'rule' is a subrule, the statistics that are updated are
- * actually its super-rule's statistics; otherwise 'rule''s own statistics are
- * updated.
- *
- * If 'rule' is not installed, this function has no effect. */
-static void
-rule_uninstall(struct ofproto *p, struct rule *rule)
-{
- assert(!rule->cr.wc.wildcards);
- if (rule->installed) {
- struct odp_flow odp_flow;
-
- odp_flow_key_from_flow(&odp_flow.key, &rule->cr.flow);
- odp_flow.actions = NULL;
- odp_flow.n_actions = 0;
- odp_flow.flags = 0;
- if (!dpif_flow_del(p->dpif, &odp_flow)) {
- update_stats(p, rule, &odp_flow.stats);
- }
- rule->installed = false;
-
- rule_post_uninstall(p, rule);
- }
-}
-
-static bool
-is_controller_rule(struct rule *rule)
-{
- /* If the only action is send to the controller then don't report
- * NetFlow expiration messages since it is just part of the control
- * logic for the network and not real traffic. */
-
- return (rule
- && rule->super
- && rule->super->n_actions == 1
- && action_outputs_to_port(&rule->super->actions[0],
- htons(OFPP_CONTROLLER)));
-}
-
-static void
-rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
-{
- struct rule *super = rule->super;
-
- rule_account(ofproto, rule, 0);
-
- if (ofproto->netflow && !is_controller_rule(rule)) {
- struct ofexpired expired;
- expired.flow = rule->cr.flow;
- expired.packet_count = rule->packet_count;
- expired.byte_count = rule->byte_count;
- expired.used = rule->used;
- netflow_expire(ofproto->netflow, &rule->nf_flow, &expired);
- }
- if (super) {
- super->packet_count += rule->packet_count;
- super->byte_count += rule->byte_count;
-
- /* Reset counters to prevent double counting if the rule ever gets
- * reinstalled. */
- rule->packet_count = 0;
- rule->byte_count = 0;
- rule->accounted_bytes = 0;
-
- netflow_flow_clear(&rule->nf_flow);
- }
-}
-\f
-static void
-queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
- struct rconn_packet_counter *counter)
-{
- update_openflow_length(msg);
- if (rconn_send(ofconn->rconn, msg, counter)) {
- ofpbuf_delete(msg);
- }
-}
-
-static void
-send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
- int error)
-{
- struct ofpbuf *buf = make_ofp_error_msg(error, oh);
- if (buf) {
- COVERAGE_INC(ofproto_error);
- queue_tx(buf, ofconn, ofconn->reply_counter);
- }
-}
-
-static void
-hton_ofp_phy_port(struct ofp_phy_port *opp)
-{
- opp->port_no = htons(opp->port_no);
- opp->config = htonl(opp->config);
- opp->state = htonl(opp->state);
- opp->curr = htonl(opp->curr);
- opp->advertised = htonl(opp->advertised);
- opp->supported = htonl(opp->supported);
- opp->peer = htonl(opp->peer);
-}
-
-static int
-handle_echo_request(struct ofconn *ofconn, struct ofp_header *oh)
-{
- struct ofp_header *rq = oh;
- queue_tx(make_echo_reply(rq), ofconn, ofconn->reply_counter);
- return 0;
-}
-
-static int
-handle_features_request(struct ofconn *ofconn, struct ofp_header *oh)
-{
- struct ofp_switch_features *osf;
- struct ofpbuf *buf;