/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
* Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
*
* Licensed under the Apache License, Version 2.0 (the "License");
#include "byte-order.h"
#include "classifier.h"
#include "coverage.h"
-#include "discovery.h"
#include "dpif.h"
#include "dynamic-string.h"
#include "fail-open.h"
#include "poll-loop.h"
#include "rconn.h"
#include "shash.h"
-#include "status.h"
#include "stream-ssl.h"
#include "svec.h"
#include "tag.h"
COVERAGE_DEFINE(ofproto_uninstallable);
COVERAGE_DEFINE(ofproto_update_port);
-#include "sflow_api.h"
-
/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
* flow translation. */
#define MAX_RESUBMIT_RECURSION 16
/* type == OFCONN_PRIMARY only. */
enum nx_role role; /* Role. */
struct hmap_node hmap_node; /* In struct ofproto's "controllers" map. */
- struct discovery *discovery; /* Controller discovery object, if enabled. */
- struct status_category *ss; /* Switch status category. */
enum ofproto_band band; /* In-band or out-of-band? */
};
uint32_t max_ports;
/* Configuration. */
- struct switch_status *switch_status;
struct fail_open *fail_open;
struct netflow *netflow;
struct ofproto_sflow *sflow;
p->max_ports = dpif_get_max_ports(dpif);
/* Initialize submodules. */
- p->switch_status = switch_status_create(p);
p->fail_open = NULL;
p->netflow = NULL;
p->sflow = NULL;
} else {
p->ofhooks = &default_ofhooks;
p->aux = p;
- p->ml = mac_learning_create();
+ p->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
}
/* Pick final datapath ID. */
}
}
-static bool
-is_discovery_controller(const struct ofproto_controller *c)
-{
- return !strcmp(c->target, "discover");
-}
-
-static bool
-is_in_band_controller(const struct ofproto_controller *c)
-{
- return is_discovery_controller(c) || c->band == OFPROTO_IN_BAND;
-}
-
/* Creates a new controller in 'ofproto'. Some of the settings are initially
* drawn from 'c', but update_controller() needs to be called later to finish
* the new ofconn's configuration. */
static void
add_controller(struct ofproto *ofproto, const struct ofproto_controller *c)
{
- struct discovery *discovery;
+ char *name = ofconn_make_name(ofproto, c->target);
struct ofconn *ofconn;
- if (is_discovery_controller(c)) {
- int error = discovery_create(c->accept_re, c->update_resolv_conf,
- ofproto->dpif, ofproto->switch_status,
- &discovery);
- if (error) {
- return;
- }
- } else {
- discovery = NULL;
- }
-
ofconn = ofconn_create(ofproto, rconn_create(5, 8), OFCONN_PRIMARY);
ofconn->pktbuf = pktbuf_create();
ofconn->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
- if (discovery) {
- ofconn->discovery = discovery;
- } else {
- char *name = ofconn_make_name(ofproto, c->target);
- rconn_connect(ofconn->rconn, c->target, name);
- free(name);
- }
+ rconn_connect(ofconn->rconn, c->target, name);
hmap_insert(&ofproto->controllers, &ofconn->hmap_node,
hash_string(c->target, 0));
+
+ free(name);
}
/* Reconfigures 'ofconn' to match 'c'. This function cannot update an ofconn's
- * target or turn discovery on or off (these are done by creating new ofconns
- * and deleting old ones), but it can update the rest of an ofconn's
- * settings. */
+ * target (this is done by creating new ofconns and deleting old ones), but it
+ * can update the rest of an ofconn's settings. */
static void
update_controller(struct ofconn *ofconn, const struct ofproto_controller *c)
{
int probe_interval;
- ofconn->band = (is_in_band_controller(c)
- ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND);
+ ofconn->band = c->band;
rconn_set_max_backoff(ofconn->rconn, c->max_backoff);
probe_interval = c->probe_interval ? MAX(c->probe_interval, 5) : 0;
rconn_set_probe_interval(ofconn->rconn, probe_interval);
- if (ofconn->discovery) {
- discovery_set_update_resolv_conf(ofconn->discovery,
- c->update_resolv_conf);
- discovery_set_accept_controller_re(ofconn->discovery, c->accept_re);
- }
-
ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit);
}
static const char *
ofconn_get_target(const struct ofconn *ofconn)
{
- return ofconn->discovery ? "discover" : rconn_get_target(ofconn->rconn);
+ return rconn_get_target(ofconn->rconn);
}
static struct ofconn *
const struct ofconn *ofconn;
struct sockaddr_in *addrs;
size_t max_addrs, n_addrs;
- bool discovery;
size_t i;
/* Allocate enough memory for as many remotes as we could possibly have. */
n_addrs = 0;
/* Add all the remotes. */
- discovery = false;
HMAP_FOR_EACH (ofconn, hmap_node, &ofproto->controllers) {
struct sockaddr_in *sin = &addrs[n_addrs];
sin->sin_port = rconn_get_remote_port(ofconn->rconn);
n_addrs++;
}
- if (ofconn->discovery) {
- discovery = true;
- }
}
for (i = 0; i < ofproto->n_extra_remotes; i++) {
addrs[n_addrs++] = ofproto->extra_in_band_remotes[i];
}
- /* Create or update or destroy in-band.
- *
- * Ordinarily we only enable in-band if there's at least one remote
- * address, but discovery needs the in-band rules for DHCP to be installed
- * even before we know any remote addresses. */
- if (n_addrs || discovery) {
+ /* Create or update or destroy in-band. */
+ if (n_addrs) {
if (!ofproto->in_band) {
- in_band_create(ofproto, ofproto->dpif, ofproto->switch_status,
- &ofproto->in_band);
+ in_band_create(ofproto, ofproto->dpif, &ofproto->in_band);
}
if (ofproto->in_band) {
in_band_set_remotes(ofproto->in_band, addrs, n_addrs);
size_t n;
if (!p->fail_open) {
- p->fail_open = fail_open_create(p, p->switch_status);
+ p->fail_open = fail_open_create(p);
}
n = 0;
struct shash new_controllers;
struct ofconn *ofconn, *next_ofconn;
struct ofservice *ofservice, *next_ofservice;
- bool ss_exists;
size_t i;
/* Create newly configured controllers and services.
for (i = 0; i < n_controllers; i++) {
const struct ofproto_controller *c = &controllers[i];
- if (!vconn_verify_name(c->target) || !strcmp(c->target, "discover")) {
+ if (!vconn_verify_name(c->target)) {
if (!find_controller_by_target(p, c->target)) {
add_controller(p, c);
}
/* Delete controllers that are no longer configured.
* Update configuration of all now-existing controllers. */
- ss_exists = false;
HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &p->controllers) {
struct ofproto_controller *c;
ofconn_destroy(ofconn);
} else {
update_controller(ofconn, c);
- if (ofconn->ss) {
- ss_exists = true;
- }
}
}
update_in_band_remotes(p);
update_fail_open(p);
-
- if (!hmap_is_empty(&p->controllers) && !ss_exists) {
- ofconn = CONTAINER_OF(hmap_first(&p->controllers),
- struct ofconn, hmap_node);
- ofconn->ss = switch_status_register(p->switch_status, "remote",
- rconn_status_cb, ofconn->rconn);
- }
}
void
}
shash_destroy(&p->port_by_name);
- switch_status_destroy(p->switch_status);
netflow_destroy(p->netflow);
ofproto_sflow_destroy(p->sflow);
}
void
-ofproto_get_ofproto_controller_info(const struct ofproto * ofproto,
+ofproto_get_ofproto_controller_info(const struct ofproto *ofproto,
struct shash *info)
{
const struct ofconn *ofconn;
HMAP_FOR_EACH (ofconn, hmap_node, &ofproto->controllers) {
const struct rconn *rconn = ofconn->rconn;
+ time_t now = time_now();
+ time_t last_connection = rconn_get_last_connection(rconn);
+ time_t last_disconnect = rconn_get_last_disconnect(rconn);
const int last_error = rconn_get_last_error(rconn);
struct ofproto_controller_info *cinfo = xmalloc(sizeof *cinfo);
cinfo->pairs.values[cinfo->pairs.n++] =
xstrdup(rconn_get_state(rconn));
- cinfo->pairs.keys[cinfo->pairs.n] = "time_in_state";
- cinfo->pairs.values[cinfo->pairs.n++] =
- xasprintf("%u", rconn_get_state_elapsed(rconn));
+ if (last_connection != TIME_MIN) {
+ cinfo->pairs.keys[cinfo->pairs.n] = "sec_since_connect";
+ cinfo->pairs.values[cinfo->pairs.n++]
+ = xasprintf("%ld", (long int) (now - last_connection));
+ }
+
+ if (last_disconnect != TIME_MIN) {
+ cinfo->pairs.keys[cinfo->pairs.n] = "sec_since_disconnect";
+ cinfo->pairs.values[cinfo->pairs.n++]
+ = xasprintf("%ld", (long int) (now - last_disconnect));
+ }
}
}
ofproto_port_del(struct ofproto *ofproto, uint16_t odp_port)
{
struct ofport *ofport = get_port(ofproto, odp_port);
- const char *name = ofport ? ofport->opp.name : "<unknown>";
+ const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
int error;
error = dpif_port_del(ofproto->dpif, odp_port);
VLOG_ERR("%s: failed to remove port %"PRIu16" (%s) interface (%s)",
dpif_name(ofproto->dpif), odp_port, name, strerror(error));
} else if (ofport) {
- /* 'name' is ofport->opp.name and update_port() is going to destroy
- * 'ofport'. Just in case update_port() refers to 'name' after it
+ /* '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);
shash_init(&devnames);
HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
- shash_add_once (&devnames, ofport->opp.name, NULL);
+ shash_add_once (&devnames, netdev_get_name(ofport->netdev), NULL);
}
DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
shash_add_once (&devnames, dpif_port.name, NULL);
shash_destroy(&devnames);
}
-static struct ofport *
-make_ofport(const struct dpif_port *dpif_port)
+/* Opens and returns a netdev for 'dpif_port', or a null pointer if the netdev
+ * cannot be opened. On success, also fills in 'opp', in *HOST* byte order. */
+static struct netdev *
+ofport_open(const struct dpif_port *dpif_port, struct ofp_phy_port *opp)
{
struct netdev_options netdev_options;
enum netdev_flags flags;
- struct ofport *ofport;
struct netdev *netdev;
int error;
return NULL;
}
- ofport = xzalloc(sizeof *ofport);
- ofport->netdev = netdev;
- ofport->odp_port = dpif_port->port_no;
- ofport->opp.port_no = odp_port_to_ofp_port(dpif_port->port_no);
- netdev_get_etheraddr(netdev, ofport->opp.hw_addr);
- ovs_strlcpy(ofport->opp.name, dpif_port->name, sizeof ofport->opp.name);
-
netdev_get_flags(netdev, &flags);
- ofport->opp.config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN;
-
- ofport->opp.state = netdev_get_carrier(netdev) ? 0 : OFPPS_LINK_DOWN;
- netdev_get_features(netdev,
- &ofport->opp.curr, &ofport->opp.advertised,
- &ofport->opp.supported, &ofport->opp.peer);
- return ofport;
+ opp->port_no = odp_port_to_ofp_port(dpif_port->port_no);
+ netdev_get_etheraddr(netdev, opp->hw_addr);
+ ovs_strzcpy(opp->name, dpif_port->name, sizeof opp->name);
+ opp->config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN;
+ opp->state = netdev_get_carrier(netdev) ? 0 : OFPPS_LINK_DOWN;
+ netdev_get_features(netdev, &opp->curr, &opp->advertised,
+ &opp->supported, &opp->peer);
+ return netdev;
}
static bool
}
}
-static int
-ofport_equal(const struct ofport *a_, const struct ofport *b_)
+/* Returns true if most fields of 'a' and 'b' are equal. Differences in name,
+ * port number, and 'config' bits other than OFPPC_PORT_DOWN are
+ * disregarded. */
+static bool
+ofport_equal(const struct ofp_phy_port *a, const struct ofp_phy_port *b)
{
- const struct ofp_phy_port *a = &a_->opp;
- const struct ofp_phy_port *b = &b_->opp;
-
BUILD_ASSERT_DECL(sizeof *a == 48); /* Detect ofp_phy_port changes. */
- return (a->port_no == b->port_no
- && !memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
- && !strcmp(a->name, b->name)
+ return (!memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
&& a->state == b->state
- && a->config == b->config
+ && !((a->config ^ b->config) & OFPPC_PORT_DOWN)
&& a->curr == b->curr
&& a->advertised == b->advertised
&& a->supported == b->supported
}
static void
-send_port_status(struct ofproto *p, const struct ofport *ofport,
+send_port_status(struct ofproto *p, const struct ofp_phy_port *opp,
uint8_t reason)
{
/* XXX Should limit the number of queued port status change messages. */
ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
ops->reason = reason;
- ops->desc = ofport->opp;
+ ops->desc = *opp;
hton_ofp_phy_port(&ops->desc);
queue_tx(b, ofconn, NULL);
}
}
+/* Adds an ofport to 'p' initialized based on the given 'netdev' and 'opp'.
+ * The caller must ensure that 'p' does not have a conflicting ofport (that is,
+ * one with the same name or port number). */
static void
-ofport_install(struct ofproto *p, struct ofport *ofport)
+ofport_install(struct ofproto *p,
+ struct netdev *netdev, const struct ofp_phy_port *opp)
{
- const char *netdev_name = ofport->opp.name;
+ const char *netdev_name = netdev_get_name(netdev);
+ struct ofport *ofport;
+
+ send_port_status(p, opp, OFPPR_ADD);
+
+ /* Create ofport. */
+ ofport = xmalloc(sizeof *ofport);
+ ofport->netdev = netdev;
+ ofport->opp = *opp;
+ ofport->odp_port = ofp_port_to_odp_port(opp->port_no);
+ /* Add port to 'p'. */
netdev_monitor_add(p->netdev_monitor, ofport->netdev);
hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->odp_port, 0));
shash_add(&p->port_by_name, netdev_name, ofport);
}
}
+/* Removes 'ofport' from 'p' and destroys it. */
static void
ofport_remove(struct ofproto *p, struct ofport *ofport)
{
+ send_port_status(p, &ofport->opp, OFPPR_DELETE);
+
netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
hmap_remove(&p->ports, &ofport->hmap_node);
shash_delete(&p->port_by_name,
- shash_find(&p->port_by_name, ofport->opp.name));
+ shash_find(&p->port_by_name,
+ netdev_get_name(ofport->netdev)));
if (p->sflow) {
ofproto_sflow_del_port(p->sflow, ofport->odp_port);
}
+
+ ofport_free(ofport);
+}
+
+/* If 'ofproto' contains an ofport named 'name', removes it from 'ofproto' and
+ * destroys it. */
+static void
+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(ofproto, port);
+ }
+}
+
+/* Updates 'port' within 'ofproto' with the new 'netdev' and 'opp'.
+ *
+ * Does not handle a name or port number change. The caller must implement
+ * such a change as a delete followed by an add. */
+static void
+ofport_modified(struct ofproto *ofproto, struct ofport *port,
+ struct netdev *netdev, struct ofp_phy_port *opp)
+{
+ memcpy(port->opp.hw_addr, opp->hw_addr, ETH_ADDR_LEN);
+ port->opp.config = ((port->opp.config & ~OFPPC_PORT_DOWN)
+ | (opp->config & OFPPC_PORT_DOWN));
+ port->opp.state = opp->state;
+ port->opp.curr = opp->curr;
+ port->opp.advertised = opp->advertised;
+ port->opp.supported = opp->supported;
+ port->opp.peer = opp->peer;
+
+ netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
+ netdev_monitor_add(ofproto->netdev_monitor, netdev);
+
+ netdev_close(port->netdev);
+ port->netdev = netdev;
+
+ send_port_status(ofproto, &port->opp, OFPPR_MODIFY);
}
static void
}
static void
-update_port(struct ofproto *p, const char *devname)
+update_port(struct ofproto *ofproto, const char *name)
{
struct dpif_port dpif_port;
- struct ofport *old_ofport;
- struct ofport *new_ofport;
- int error;
+ struct ofp_phy_port opp;
+ struct netdev *netdev;
+ struct ofport *port;
COVERAGE_INC(ofproto_update_port);
- /* Query the datapath for port information. */
- error = dpif_port_query_by_name(p->dpif, devname, &dpif_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, dpif_port.port_no);
- }
- } else if (error != ENOENT && error != ENODEV) {
- VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error "
- "%s", strerror(error));
- goto exit;
- }
-
- /* Create a new ofport. */
- new_ofport = !error ? make_ofport(&dpif_port) : NULL;
-
- /* Eliminate a few pathological cases. */
- if (!old_ofport && !new_ofport) {
- goto exit;
- } else if (old_ofport && new_ofport) {
- /* Most of the 'config' bits are OpenFlow soft state, but
- * OFPPC_PORT_DOWN is maintained by 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);
- goto exit;
+ /* Fetch 'name''s location and properties from the datapath. */
+ netdev = (!dpif_port_query_by_name(ofproto->dpif, name, &dpif_port)
+ ? ofport_open(&dpif_port, &opp)
+ : NULL);
+ if (netdev) {
+ port = get_port(ofproto, dpif_port.port_no);
+ if (port && !strcmp(netdev_get_name(port->netdev), name)) {
+ /* 'name' hasn't changed location. Any properties changed? */
+ if (!ofport_equal(&port->opp, &opp)) {
+ ofport_modified(ofproto, port, netdev, &opp);
+ } else {
+ netdev_close(netdev);
+ }
+ } else {
+ /* If 'port' is nonnull then its name differs from 'name' and thus
+ * we should delete it. If we think there's a port named 'name'
+ * then its port number must be wrong now so delete it too. */
+ if (port) {
+ ofport_remove(ofproto, port);
+ }
+ ofport_remove_with_name(ofproto, name);
+ ofport_install(ofproto, netdev, &opp);
}
+ } else {
+ /* Any port named 'name' is gone now. */
+ ofport_remove_with_name(ofproto, name);
}
-
- /* 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);
-
-exit:
dpif_port_destroy(&dpif_port);
}
DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
if (!ofport_conflicts(p, &dpif_port)) {
- struct ofport *ofport = make_ofport(&dpif_port);
- if (ofport) {
- ofport_install(p, ofport);
+ struct ofp_phy_port opp;
+ struct netdev *netdev;
+
+ netdev = ofport_open(&dpif_port, &opp);
+ if (netdev) {
+ ofport_install(p, netdev, &opp);
}
}
}
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);
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);
- free(controller_name);
- } else {
- rconn_disconnect(ofconn->rconn);
- }
- }
- }
-
for (i = 0; i < N_SCHEDULERS; i++) {
pinsched_run(ofconn->schedulers[i], do_send_packet_in, ofconn);
}
}
}
- if (!ofconn->discovery && !rconn_is_alive(ofconn->rconn)) {
+ if (!rconn_is_alive(ofconn->rconn)) {
ofconn_destroy(ofconn);
}
}
{
int i;
- if (ofconn->discovery) {
- discovery_wait(ofconn->discovery);
- }
for (i = 0; i < N_SCHEDULERS; i++) {
pinsched_wait(ofconn->schedulers[i]);
}
if (rate > 0) {
if (!*s) {
- *s = pinsched_create(rate, burst,
- ofconn->ofproto->switch_status);
+ *s = pinsched_create(rate, burst);
} else {
pinsched_set_limits(*s, rate, burst);
}
assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
flow_extract_stats(&facet->flow, packet, &stats);
+ stats.used = time_msec();
if (execute_odp_actions(ofproto, &facet->flow,
facet->actions, facet->actions_len, packet)) {
- facet->used = time_msec();
facet_update_stats(ofproto, facet, &stats);
- netflow_flow_update_time(ofproto->netflow,
- &facet->nf_flow, facet->used);
}
}
}
/* Adds a pretty-printed description of all flows to 'results', including
- * those marked hidden by secchan (e.g., by in-band control). */
+ * hidden flows (e.g., set up by in-band control). */
void
ofproto_get_all_flows(struct ofproto *p, struct ds *results)
{
facet_update_stats(struct ofproto *ofproto, struct facet *facet,
const struct dpif_flow_stats *stats)
{
- if (stats->n_packets) {
+ if (stats->n_packets || stats->used > facet->used) {
facet_update_time(ofproto, facet, stats->used);
facet->packet_count += stats->n_packets;
facet->byte_count += stats->n_bytes;
return 0;
/* Nicira extension requests. */
- case OFPUTIL_NXT_STATUS_REQUEST:
- return switch_status_handle_request(
- ofconn->ofproto->switch_status, ofconn->rconn, oh);
-
case OFPUTIL_NXT_TUN_ID_FROM_COOKIE:
return handle_tun_id_from_cookie(ofconn, oh);
case OFPUTIL_OFPST_PORT_REPLY:
case OFPUTIL_OFPST_TABLE_REPLY:
case OFPUTIL_OFPST_AGGREGATE_REPLY:
- case OFPUTIL_NXT_STATUS_REPLY:
case OFPUTIL_NXT_ROLE_REPLY:
case OFPUTIL_NXT_FLOW_REMOVED:
case OFPUTIL_NXST_FLOW_REPLY:
* OFPT_PACKET_IN message to each OpenFlow controller as necessary according to
* their individual configurations.
*
- * Takes ownership of 'packet'. */
+ * If 'clone' is true, the caller retains ownership of 'upcall->packet'.
+ * Otherwise, ownership is transferred to this function. */
static void
send_packet_in(struct ofproto *ofproto, struct dpif_upcall *upcall,
const struct flow *flow, bool clone)