#include "netflow.h"
#include "odp-util.h"
#include "ofp-print.h"
+#include "ofproto-sflow.h"
#include "ofpbuf.h"
#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
#define THIS_MODULE VLM_ofproto
#include "vlog.h"
-enum {
- DP_GROUP_FLOOD = 0,
- DP_GROUP_ALL = 1
-};
+#include "sflow_api.h"
enum {
TABLEID_HASH = 0,
struct rule {
struct cls_rule cr;
+ uint64_t flow_cookie; /* Controller-issued identifier.
+ (Kept in network-byte order.) */
uint16_t idle_timeout; /* In seconds from time of last use. */
uint16_t hard_timeout; /* In seconds from time of creation. */
+ bool send_flow_removed; /* Send a flow removed message? */
long long int used; /* Last-used time (0 if never used). */
long long int created; /* Creation time. */
uint64_t packet_count; /* Number of packets received. */
static struct rule *rule_create(struct ofproto *, struct rule *super,
const union ofp_action *, size_t n_actions,
- uint16_t idle_timeout, uint16_t hard_timeout);
+ uint16_t idle_timeout, uint16_t hard_timeout,
+ uint64_t flow_cookie, bool send_flow_removed);
static void rule_free(struct rule *);
static void rule_destroy(struct ofproto *, struct rule *);
static struct rule *rule_from_cls_rule(const struct cls_rule *);
struct rule *displaced_rule);
static void rule_uninstall(struct ofproto *, struct rule *);
static void rule_post_uninstall(struct ofproto *, struct rule *);
+static void send_flow_removed(struct ofproto *p, struct rule *rule,
+ long long int now, uint8_t reason);
struct ofconn {
struct list node;
struct rconn *rconn;
struct pktbuf *pktbuf;
- bool send_flow_exp;
int miss_send_len;
struct rconn_packet_counter *packet_in_counter;
char *hardware; /* Hardware. */
char *software; /* Software version. */
char *serial; /* Serial number. */
+ char *dp_desc; /* Datapath description. */
/* Datapath. */
struct dpif *dpif;
struct fail_open *fail_open;
struct pinsched *miss_sched, *action_sched;
struct netflow *netflow;
+ struct ofproto_sflow *sflow;
/* Flow table. */
struct classifier cls;
static void handle_openflow(struct ofconn *, struct ofproto *,
struct ofpbuf *);
-static void refresh_port_group(struct ofproto *, unsigned int group);
+static void refresh_port_groups(struct ofproto *);
+
static void update_port(struct ofproto *, const char *devname);
static int init_ports(struct ofproto *);
static void reinit_ports(struct ofproto *);
dpif_close(dpif);
return error;
}
- error = dpif_recv_set_mask(dpif, ODPL_MISS | ODPL_ACTION);
+ error = dpif_recv_set_mask(dpif, ODPL_MISS | ODPL_ACTION | ODPL_SFLOW);
if (error) {
VLOG_ERR("failed to listen on datapath %s: %s",
datapath, strerror(error));
p->hardware = xstrdup("Reference Implementation");
p->software = xstrdup(VERSION BUILDNR);
p->serial = xstrdup("None");
+ p->dp_desc = xstrdup("None");
/* Initialize datapath. */
p->dpif = dpif;
p->fail_open = NULL;
p->miss_sched = p->action_sched = NULL;
p->netflow = NULL;
+ p->sflow = NULL;
/* Initialize flow table. */
classifier_init(&p->cls);
/* Pick final datapath ID. */
p->datapath_id = pick_datapath_id(p);
- VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
+ VLOG_INFO("using datapath ID %016"PRIx64, p->datapath_id);
*ofprotop = p;
return 0;
uint64_t old_dpid = p->datapath_id;
p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
if (p->datapath_id != old_dpid) {
- VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
+ VLOG_INFO("datapath ID changed to %016"PRIx64, p->datapath_id);
rconn_reconnect(p->controller->rconn);
}
}
void
ofproto_set_desc(struct ofproto *p,
const char *manufacturer, const char *hardware,
- const char *software, const char *serial)
+ const char *software, const char *serial,
+ const char *dp_desc)
{
if (manufacturer) {
free(p->manufacturer);
free(p->serial);
p->serial = xstrdup(serial);
}
+ if (dp_desc) {
+ free(p->dp_desc);
+ p->dp_desc = xstrdup(dp_desc);
+ }
}
int
}
}
+void
+ofproto_set_sflow(struct ofproto *ofproto,
+ const struct ofproto_sflow_options *oso)
+{
+ struct ofproto_sflow *os = ofproto->sflow;
+ if (oso) {
+ if (!os) {
+ struct ofport *ofport;
+ unsigned int odp_port;
+
+ os = ofproto->sflow = ofproto_sflow_create(ofproto->dpif);
+ refresh_port_groups(ofproto);
+ PORT_ARRAY_FOR_EACH (ofport, &ofproto->ports, odp_port) {
+ ofproto_sflow_add_port(os, odp_port,
+ netdev_get_name(ofport->netdev));
+ }
+ }
+ ofproto_sflow_set_options(os, oso);
+ } else {
+ ofproto_sflow_destroy(os);
+ ofproto->sflow = NULL;
+ }
+}
+
void
ofproto_set_failure(struct ofproto *ofproto, bool fail_open)
{
}
int
-ofproto_set_stp(struct ofproto *ofproto UNUSED, bool enable_stp)
+ofproto_set_stp(struct ofproto *ofproto OVS_UNUSED, bool enable_stp)
{
/* XXX */
if (enable_stp) {
return;
}
+ /* Destroy fail-open early, because it touches the classifier. */
+ ofproto_set_failure(p, false);
+
ofproto_flush_flows(p);
classifier_destroy(&p->cls);
switch_status_destroy(p->switch_status);
in_band_destroy(p->in_band);
discovery_destroy(p->discovery);
- fail_open_destroy(p->fail_open);
pinsched_destroy(p->miss_sched);
pinsched_destroy(p->action_sched);
netflow_destroy(p->netflow);
+ ofproto_sflow_destroy(p->sflow);
switch_status_unregister(p->ss_cat);
if (p->netflow) {
netflow_run(p->netflow);
}
+ if (p->sflow) {
+ ofproto_sflow_run(p->sflow);
+ }
return 0;
}
}
pinsched_wait(p->miss_sched);
pinsched_wait(p->action_sched);
+ if (p->sflow) {
+ ofproto_sflow_wait(p->sflow);
+ }
if (!tag_set_is_empty(&p->revalidate_set)) {
poll_immediate_wake();
}
{
struct rule *rule;
rule = rule_create(p, NULL, actions, n_actions,
- idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 0);
+ idle_timeout >= 0 ? idle_timeout : 5 /* XXX */,
+ 0, 0, false);
cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
rule_insert(p, rule, NULL, 0);
}
svec_destroy(&devnames);
}
-static void
+static size_t
refresh_port_group(struct ofproto *p, unsigned int group)
{
uint16_t *ports;
}
dpif_port_group_set(p->dpif, group, ports, n_ports);
free(ports);
+
+ return n_ports;
}
static void
refresh_port_groups(struct ofproto *p)
{
- refresh_port_group(p, DP_GROUP_FLOOD);
- refresh_port_group(p, DP_GROUP_ALL);
+ size_t n_flood = refresh_port_group(p, DP_GROUP_FLOOD);
+ size_t n_all = refresh_port_group(p, DP_GROUP_ALL);
+ if (p->sflow) {
+ ofproto_sflow_set_group_sizes(p->sflow, n_flood, n_all);
+ }
}
static struct ofport *
static void
ofport_install(struct ofproto *p, struct ofport *ofport)
{
+ uint16_t odp_port = ofp_port_to_odp_port(ofport->opp.port_no);
+ const char *netdev_name = (const char *) ofport->opp.name;
+
netdev_monitor_add(p->netdev_monitor, ofport->netdev);
- port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no),
- ofport);
- shash_add(&p->port_by_name, (char *) ofport->opp.name, ofport);
+ port_array_set(&p->ports, odp_port, ofport);
+ shash_add(&p->port_by_name, netdev_name, ofport);
+ if (p->sflow) {
+ ofproto_sflow_add_port(p->sflow, odp_port, netdev_name);
+ }
}
static void
ofport_remove(struct ofproto *p, struct ofport *ofport)
{
+ uint16_t odp_port = ofp_port_to_odp_port(ofport->opp.port_no);
+
netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
- port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no), NULL);
+ port_array_set(&p->ports, odp_port, NULL);
shash_delete(&p->port_by_name,
shash_find(&p->port_by_name, (char *) ofport->opp.name));
+ if (p->sflow) {
+ ofproto_sflow_del_port(p->sflow, odp_port);
+ }
}
static void
list_push_back(&p->all_conns, &ofconn->node);
ofconn->rconn = rconn;
ofconn->pktbuf = NULL;
- ofconn->send_flow_exp = false;
ofconn->miss_send_len = 0;
ofconn->packet_in_counter = rconn_packet_counter_create ();
ofconn->reply_counter = rconn_packet_counter_create ();
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)
+ uint16_t idle_timeout, uint16_t hard_timeout,
+ uint64_t 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);
const flow_t *flow)
{
struct rule *subrule = rule_create(ofproto, rule, NULL, 0,
- rule->idle_timeout, rule->hard_timeout);
+ rule->idle_timeout, rule->hard_timeout,
+ 0, false);
COVERAGE_INC(ofproto_subrule_create);
cls_rule_from_flow(&subrule->cr, flow, 0,
(rule->cr.priority <= UINT16_MAX ? UINT16_MAX
osf->n_buffers = htonl(pktbuf_capacity());
osf->n_tables = 2;
osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
- OFPC_PORT_STATS | OFPC_MULTI_PHY_TX);
+ OFPC_PORT_STATS | OFPC_MULTI_PHY_TX |
+ OFPC_ARP_MATCH_IP);
osf->actions = htonl((1u << OFPAT_OUTPUT) |
(1u << OFPAT_SET_VLAN_VID) |
(1u << OFPAT_SET_VLAN_PCP) |
(1u << OFPAT_SET_DL_DST) |
(1u << OFPAT_SET_NW_SRC) |
(1u << OFPAT_SET_NW_DST) |
+ (1u << OFPAT_SET_NW_TOS) |
(1u << OFPAT_SET_TP_SRC) |
(1u << OFPAT_SET_TP_DST));
/* Figure out flags. */
dpif_get_drop_frags(p->dpif, &drop_frags);
flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
- if (ofconn->send_flow_exp) {
- flags |= OFPC_SEND_FLOW_EXP;
- }
/* Send reply. */
osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
}
flags = ntohs(osc->flags);
- ofconn->send_flow_exp = (flags & OFPC_SEND_FLOW_EXP) != 0;
-
if (ofconn == p->controller) {
switch (flags & OFPC_FRAG_MASK) {
case OFPC_FRAG_NORMAL:
case OFPAT_SET_NW_DST:
oa = odp_actions_add(ctx->out, ODPAT_SET_NW_DST);
oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
+
+ case OFPAT_SET_NW_TOS:
+ oa = odp_actions_add(ctx->out, ODPAT_SET_NW_TOS);
+ oa->nw_tos.nw_tos = ia->nw_tos.nw_tos;
break;
case OFPAT_SET_TP_SRC:
#undef REVALIDATE_BITS
if (mask & OFPPC_NO_FLOOD) {
port->opp.config ^= OFPPC_NO_FLOOD;
- refresh_port_group(p, DP_GROUP_FLOOD);
+ refresh_port_groups(p);
}
if (mask & OFPPC_NO_PACKET_IN) {
port->opp.config ^= OFPPC_NO_PACKET_IN;
strncpy(ods->hw_desc, p->hardware, sizeof ods->hw_desc);
strncpy(ods->sw_desc, p->software, sizeof ods->sw_desc);
strncpy(ods->serial_num, p->serial, sizeof ods->serial_num);
+ strncpy(ods->dp_desc, p->dp_desc, sizeof ods->dp_desc);
queue_tx(msg, ofconn, ofconn->reply_counter);
return 0;
ofs->pad = 0;
flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofs->match);
ofs->duration = htonl((time_msec() - rule->created) / 1000);
+ ofs->cookie = rule->flow_cookie;
ofs->priority = htons(rule->cr.priority);
ofs->idle_timeout = htons(rule->idle_timeout);
ofs->hard_timeout = htons(rule->hard_timeout);
struct cls_rule target;
if (arg_size != sizeof *fsr) {
- return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
}
fsr = (struct ofp_flow_stats_request *) osr->body;
}
query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
- flow_to_ovs_match(&rule->cr.flow, rule->cr.wc.wildcards, &match);
+ flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &match);
ds_put_format(results, "duration=%llds, ",
(time_msec() - rule->created) / 1000);
struct ofpbuf *msg;
if (arg_size != sizeof *asr) {
- return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
}
asr = (struct ofp_aggregate_stats_request *) osr->body;
uint16_t in_port;
int error;
+ if (ofm->flags & htons(OFPFF_CHECK_OVERLAP)) {
+ flow_t flow;
+ uint32_t wildcards;
+
+ flow_from_match(&flow, &wildcards, &ofm->match);
+ 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));
+ ntohs(ofm->hard_timeout), ofm->cookie,
+ ofm->flags & htons(OFPFF_SEND_FLOW_REM));
cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
- packet = NULL;
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);
}
if (command == OFPFC_DELETE) {
+ long long int now = time_msec();
+ send_flow_removed(p, rule, now, OFPRR_DELETE);
rule_remove(p, rule);
} else {
size_t actions_len = n_actions * sizeof *rule->actions;
free(rule->actions);
rule->actions = xmemdup(ofm->actions, actions_len);
rule->n_actions = n_actions;
+ rule->flow_cookie = ofm->cookie;
if (rule->cr.wc.wildcards) {
COVERAGE_INC(ofproto_mod_wc_flow);
return error;
}
+ /* We do not support the emergency flow cache. It will hopefully
+ * get dropped from OpenFlow in the near future. */
+ if (ofm->flags & htons(OFPFF_EMERG)) {
+ /* There isn't a good fit for an error code, so just state that the
+ * flow table is full. */
+ return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL);
+ }
+
normalize_match(&ofm->match);
if (!ofm->match.wildcards) {
ofm->priority = htons(UINT16_MAX);
struct nicira_header *nh;
if (ntohs(ovh->header.length) < sizeof(struct ofp_vendor_header)) {
- return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
}
if (ovh->vendor != htonl(NX_VENDOR_ID)) {
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
}
if (ntohs(ovh->header.length) < sizeof(struct nicira_header)) {
- return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
}
nh = msg;
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
}
+static int
+handle_barrier_request(struct ofconn *ofconn, struct ofp_header *oh)
+{
+ struct ofp_header *ob;
+ struct ofpbuf *buf;
+
+ /* Currently, everything executes synchronously, so we can just
+ * immediately send the barrier reply. */
+ ob = make_openflow_xid(sizeof *ob, OFPT_BARRIER_REPLY, oh->xid, &buf);
+ queue_tx(buf, ofconn, ofconn->reply_counter);
+ return 0;
+}
+
static void
handle_openflow(struct ofconn *ofconn, struct ofproto *p,
struct ofpbuf *ofp_msg)
error = handle_vendor(p, ofconn, ofp_msg->data);
break;
+ case OFPT_BARRIER_REQUEST:
+ error = handle_barrier_request(ofconn, oh);
+ break;
+
default:
if (VLOG_IS_WARN_ENABLED()) {
char *s = ofp_to_string(oh, ntohs(oh->length), 2);
}
\f
static void
-handle_odp_msg(struct ofproto *p, struct ofpbuf *packet)
+handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet)
{
struct odp_msg *msg = packet->data;
uint16_t in_port = odp_port_to_ofp_port(msg->port);
struct ofpbuf payload;
flow_t flow;
- /* Handle controller actions. */
- if (msg->type == _ODPL_ACTION_NR) {
- COVERAGE_INC(ofproto_ctlr_action);
- pinsched_send(p->action_sched, in_port, packet,
- send_packet_in_action, p);
- return;
- }
-
payload.data = msg + 1;
payload.size = msg->length - sizeof *msg;
flow_extract(&payload, msg->port, &flow);
ofpbuf_delete(packet);
}
}
+
+static void
+handle_odp_msg(struct ofproto *p, struct ofpbuf *packet)
+{
+ struct odp_msg *msg = packet->data;
+
+ switch (msg->type) {
+ case _ODPL_ACTION_NR:
+ COVERAGE_INC(ofproto_ctlr_action);
+ pinsched_send(p->action_sched, odp_port_to_ofp_port(msg->port), packet,
+ send_packet_in_action, p);
+ break;
+
+ case _ODPL_SFLOW_NR:
+ if (p->sflow) {
+ ofproto_sflow_received(p->sflow, msg);
+ }
+ ofpbuf_delete(packet);
+ break;
+
+ case _ODPL_MISS_NR:
+ handle_odp_miss_msg(p, packet);
+ break;
+
+ default:
+ VLOG_WARN_RL(&rl, "received ODP message of unexpected type %"PRIu32,
+ msg->type);
+ break;
+ }
+}
\f
static void
revalidate_cb(struct cls_rule *sub_, void *cbdata_)
}
static struct ofpbuf *
-compose_flow_exp(const struct rule *rule, long long int now, uint8_t reason)
+compose_flow_removed(const struct rule *rule, long long int now, uint8_t reason)
{
- struct ofp_flow_expired *ofe;
+ struct ofp_flow_removed *ofr;
struct ofpbuf *buf;
- ofe = make_openflow(sizeof *ofe, OFPT_FLOW_EXPIRED, &buf);
- flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofe->match);
- ofe->priority = htons(rule->cr.priority);
- ofe->reason = reason;
- ofe->duration = htonl((now - rule->created) / 1000);
- ofe->packet_count = htonll(rule->packet_count);
- ofe->byte_count = htonll(rule->byte_count);
+ ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf);
+ flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofr->match);
+ ofr->cookie = rule->flow_cookie;
+ ofr->priority = htons(rule->cr.priority);
+ ofr->reason = reason;
+ ofr->duration = htonl((now - rule->created) / 1000);
+ ofr->idle_timeout = htons(rule->idle_timeout);
+ ofr->packet_count = htonll(rule->packet_count);
+ ofr->byte_count = htonll(rule->byte_count);
return buf;
}
static void
-send_flow_exp(struct ofproto *p, struct rule *rule,
- long long int now, uint8_t reason)
+uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule)
+{
+ assert(rule->installed);
+ assert(!rule->cr.wc.wildcards);
+
+ if (rule->super) {
+ rule_remove(ofproto, rule);
+ } else {
+ rule_uninstall(ofproto, rule);
+ }
+}
+static void
+send_flow_removed(struct ofproto *p, struct rule *rule,
+ long long int now, uint8_t reason)
{
struct ofconn *ofconn;
struct ofconn *prev;
prev = NULL;
LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
- if (ofconn->send_flow_exp && rconn_is_connected(ofconn->rconn)) {
+ if (rule->send_flow_removed && rconn_is_connected(ofconn->rconn)) {
if (prev) {
queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
} else {
- buf = compose_flow_exp(rule, now, reason);
+ buf = compose_flow_removed(rule, now, reason);
}
prev = ofconn;
}
}
}
-static void
-uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule)
-{
- assert(rule->installed);
- assert(!rule->cr.wc.wildcards);
-
- if (rule->super) {
- rule_remove(ofproto, rule);
- } else {
- rule_uninstall(ofproto, rule);
- }
-}
static void
expire_rule(struct cls_rule *cls_rule, void *p_)
}
if (!rule_is_hidden(rule)) {
- send_flow_exp(p, rule, now,
- (now >= hard_expire
- ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+ send_flow_removed(p, rule, now,
+ (now >= hard_expire
+ ? OFPRR_HARD_TIMEOUT : OFPRR_IDLE_TIMEOUT));
}
rule_remove(p, rule);
}
pick_fallback_dpid(void)
{
uint8_t ea[ETH_ADDR_LEN];
- eth_addr_random(ea);
- ea[0] = 0x00; /* Set Nicira OUI. */
- ea[1] = 0x23;
- ea[2] = 0x20;
+ eth_addr_nicira_random(ea);
return eth_addr_to_uint64(ea);
}
\f