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? */
struct list list;
/* OpenFlow actions.
+ *
+ * 'n_actions' is the number of elements in the 'actions' array. A single
+ * action may take up more more than one element's worth of space.
*
* A subrule has no actions (it uses the super-rule's actions). */
int n_actions;
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,
- bool send_flow_removed);
+ 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 *);
/* Settings. */
uint64_t datapath_id; /* Datapath ID. */
uint64_t fallback_dpid; /* Datapath ID if no better choice found. */
- char *manufacturer; /* Manufacturer. */
- char *hardware; /* Hardware. */
- char *software; /* Software version. */
- char *serial; /* Serial number. */
+ char *mfr_desc; /* Manufacturer. */
+ char *hw_desc; /* Hardware. */
+ char *sw_desc; /* Software version. */
+ char *serial_desc; /* Serial number. */
+ char *dp_desc; /* Datapath description. */
/* Datapath. */
struct dpif *dpif;
bool need_revalidate;
long long int next_expiration;
struct tag_set revalidate_set;
+ bool tun_id_from_cookie;
/* OpenFlow connections. */
struct list all_conns;
p = xzalloc(sizeof *p);
p->fallback_dpid = pick_fallback_dpid();
p->datapath_id = p->fallback_dpid;
- p->manufacturer = xstrdup("Nicira Networks, Inc.");
- p->hardware = xstrdup("Reference Implementation");
- p->software = xstrdup(VERSION BUILDNR);
- p->serial = xstrdup("None");
+ p->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
+ p->hw_desc = xstrdup(DEFAULT_HW_DESC);
+ p->sw_desc = xstrdup(DEFAULT_SW_DESC);
+ p->serial_desc = xstrdup(DEFAULT_SERIAL_DESC);
+ p->dp_desc = xstrdup(DEFAULT_DP_DESC);
/* Initialize datapath. */
p->dpif = dpif;
void
ofproto_set_desc(struct ofproto *p,
- const char *manufacturer, const char *hardware,
- const char *software, const char *serial)
+ const char *mfr_desc, const char *hw_desc,
+ const char *sw_desc, const char *serial_desc,
+ const char *dp_desc)
{
- if (manufacturer) {
- free(p->manufacturer);
- p->manufacturer = xstrdup(manufacturer);
+ struct ofp_desc_stats *ods;
+
+ if (mfr_desc) {
+ if (strlen(mfr_desc) >= sizeof ods->mfr_desc) {
+ VLOG_WARN("truncating mfr_desc, must be less than %zu characters",
+ sizeof ods->mfr_desc);
+ }
+ free(p->mfr_desc);
+ p->mfr_desc = xstrdup(mfr_desc);
}
- if (hardware) {
- free(p->hardware);
- p->hardware = xstrdup(hardware);
+ if (hw_desc) {
+ if (strlen(hw_desc) >= sizeof ods->hw_desc) {
+ VLOG_WARN("truncating hw_desc, must be less than %zu characters",
+ sizeof ods->hw_desc);
+ }
+ free(p->hw_desc);
+ p->hw_desc = xstrdup(hw_desc);
}
- if (software) {
- free(p->software);
- p->software = xstrdup(software);
+ if (sw_desc) {
+ if (strlen(sw_desc) >= sizeof ods->sw_desc) {
+ VLOG_WARN("truncating sw_desc, must be less than %zu characters",
+ sizeof ods->sw_desc);
+ }
+ free(p->sw_desc);
+ p->sw_desc = xstrdup(sw_desc);
+ }
+ if (serial_desc) {
+ if (strlen(serial_desc) >= sizeof ods->serial_num) {
+ VLOG_WARN("truncating serial_desc, must be less than %zu "
+ "characters",
+ sizeof ods->serial_num);
+ }
+ free(p->serial_desc);
+ p->serial_desc = xstrdup(serial_desc);
}
- if (serial) {
- free(p->serial);
- p->serial = xstrdup(serial);
+ if (dp_desc) {
+ if (strlen(dp_desc) >= sizeof ods->dp_desc) {
+ VLOG_WARN("truncating dp_desc, must be less than %zu characters",
+ sizeof ods->dp_desc);
+ }
+ free(p->dp_desc);
+ p->dp_desc = xstrdup(dp_desc);
}
}
return;
}
- /* Destroy fail-open early, because it touches the classifier. */
+ /* Destroy fail-open and in-band early, since they touch the classifier. */
ofproto_set_failure(p, false);
+ ofproto_set_in_band(p, false);
ofproto_flush_flows(p);
classifier_destroy(&p->cls);
shash_destroy(&p->port_by_name);
switch_status_destroy(p->switch_status);
- in_band_destroy(p->in_band);
discovery_destroy(p->discovery);
pinsched_destroy(p->miss_sched);
pinsched_destroy(p->action_sched);
mac_learning_destroy(p->ml);
+ free(p->mfr_desc);
+ free(p->hw_desc);
+ free(p->sw_desc);
+ free(p->serial_desc);
+ free(p->dp_desc);
+
+ port_array_destroy(&p->ports);
+
free(p);
}
/* Someone destroyed the datapath behind our back. The caller
* better destroy us and give up, because we're just going to
* spin from here on out. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_ERR_RL(&rl, "%s: datapath was destroyed externally",
+ static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally",
dpif_name(p->dpif));
return ENODEV;
}
struct rule *rule;
rule = rule_create(p, NULL, actions, n_actions,
idle_timeout >= 0 ? idle_timeout : 5 /* XXX */,
- 0, false);
- cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
+ 0, 0, false);
+ cls_rule_from_flow(flow, wildcards, priority, &rule->cr);
rule_insert(p, rule, NULL, 0);
}
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,
- bool send_flow_removed)
+ 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;
/* Send the packet and credit it to the rule. */
if (packet) {
flow_t flow;
- flow_extract(packet, in_port, &flow);
+ flow_extract(packet, 0, in_port, &flow);
rule_execute(p, rule, packet, &flow);
}
{
struct rule *subrule = rule_create(ofproto, rule, NULL, 0,
rule->idle_timeout, rule->hard_timeout,
- false);
+ 0, false);
COVERAGE_INC(ofproto_subrule_create);
- cls_rule_from_flow(&subrule->cr, flow, 0,
- (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
- : rule->cr.priority));
+ cls_rule_from_flow(flow, 0, (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
+ : rule->cr.priority), &subrule->cr);
classifier_insert_exact(&ofproto->cls, &subrule->cr);
return subrule;
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);
}
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);
}
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_ARP_MATCH_IP);
osf->actions = htonl((1u << OFPAT_OUTPUT) |
(1u << OFPAT_SET_VLAN_VID) |
(1u << OFPAT_SET_VLAN_PCP) |
struct action_xlate_ctx {
/* Input. */
- const flow_t *flow; /* Flow to which these actions correspond. */
+ flow_t flow; /* Flow to which these actions correspond. */
int recurse; /* Recursion level, via xlate_table_action. */
struct ofproto *ofproto;
const struct ofpbuf *packet; /* The packet corresponding to 'flow', or a
xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
{
if (!ctx->recurse) {
+ uint16_t old_in_port;
struct rule *rule;
- flow_t flow;
- flow = *ctx->flow;
- flow.in_port = in_port;
+ /* Look up a flow with 'in_port' as the input port. Then restore the
+ * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
+ * have surprising behavior). */
+ old_in_port = ctx->flow.in_port;
+ ctx->flow.in_port = in_port;
+ rule = lookup_valid_rule(ctx->ofproto, &ctx->flow);
+ ctx->flow.in_port = old_in_port;
- rule = lookup_valid_rule(ctx->ofproto, &flow);
if (rule) {
if (rule->super) {
rule = rule->super;
switch (ntohs(oao->port)) {
case OFPP_IN_PORT:
- add_output_action(ctx, ctx->flow->in_port);
+ add_output_action(ctx, ctx->flow.in_port);
break;
case OFPP_TABLE:
- xlate_table_action(ctx, ctx->flow->in_port);
+ xlate_table_action(ctx, ctx->flow.in_port);
break;
case OFPP_NORMAL:
- if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
+ if (!ctx->ofproto->ofhooks->normal_cb(&ctx->flow, ctx->packet,
ctx->out, ctx->tags,
&ctx->nf_output_iface,
ctx->ofproto->aux)) {
break;
default:
odp_port = ofp_port_to_odp_port(ntohs(oao->port));
- if (odp_port != ctx->flow->in_port) {
+ if (odp_port != ctx->flow.in_port) {
add_output_action(ctx, odp_port);
}
break;
const struct nx_action_header *nah)
{
const struct nx_action_resubmit *nar;
+ const struct nx_action_set_tunnel *nast;
+ union odp_action *oa;
int subtype = ntohs(nah->subtype);
assert(nah->vendor == htonl(NX_VENDOR_ID));
xlate_table_action(ctx, ofp_port_to_odp_port(ntohs(nar->in_port)));
break;
+ case NXAST_SET_TUNNEL:
+ nast = (const struct nx_action_set_tunnel *) nah;
+ oa = odp_actions_add(ctx->out, ODPAT_SET_TUNNEL);
+ ctx->flow.tun_id = oa->tunnel.tun_id = nast->tun_id;
+ break;
+
+ /* If you add a new action here that modifies flow data, don't forget to
+ * update the flow key in ctx->flow in the same key. */
+
default:
VLOG_DBG_RL(&rl, "unknown Nicira action type %"PRIu16, subtype);
break;
const union ofp_action *ia;
const struct ofport *port;
- port = port_array_get(&ctx->ofproto->ports, ctx->flow->in_port);
+ port = port_array_get(&ctx->ofproto->ports, ctx->flow.in_port);
if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
- port->opp.config & (eth_addr_equals(ctx->flow->dl_dst, stp_eth_addr)
+ port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, stp_eth_addr)
? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) {
/* Drop this flow. */
return;
case OFPAT_SET_VLAN_VID:
oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_VID);
- oa->vlan_vid.vlan_vid = ia->vlan_vid.vlan_vid;
+ ctx->flow.dl_vlan = oa->vlan_vid.vlan_vid = ia->vlan_vid.vlan_vid;
break;
case OFPAT_SET_VLAN_PCP:
oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_PCP);
- oa->vlan_pcp.vlan_pcp = ia->vlan_pcp.vlan_pcp;
+ ctx->flow.dl_vlan_pcp = oa->vlan_pcp.vlan_pcp = ia->vlan_pcp.vlan_pcp;
break;
case OFPAT_STRIP_VLAN:
odp_actions_add(ctx->out, ODPAT_STRIP_VLAN);
+ ctx->flow.dl_vlan = OFP_VLAN_NONE;
+ ctx->flow.dl_vlan_pcp = 0;
break;
case OFPAT_SET_DL_SRC:
oa = odp_actions_add(ctx->out, ODPAT_SET_DL_SRC);
memcpy(oa->dl_addr.dl_addr,
((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
+ memcpy(ctx->flow.dl_src,
+ ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
break;
case OFPAT_SET_DL_DST:
oa = odp_actions_add(ctx->out, ODPAT_SET_DL_DST);
memcpy(oa->dl_addr.dl_addr,
((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
+ memcpy(ctx->flow.dl_dst,
+ ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
break;
case OFPAT_SET_NW_SRC:
oa = odp_actions_add(ctx->out, ODPAT_SET_NW_SRC);
- oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
+ ctx->flow.nw_src = oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
break;
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;
+ ctx->flow.nw_dst = oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
+ break;
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;
+ ctx->flow.nw_tos = oa->nw_tos.nw_tos = ia->nw_tos.nw_tos;
break;
case OFPAT_SET_TP_SRC:
oa = odp_actions_add(ctx->out, ODPAT_SET_TP_SRC);
- oa->tp_port.tp_port = ia->tp_port.tp_port;
+ ctx->flow.tp_src = oa->tp_port.tp_port = ia->tp_port.tp_port;
break;
case OFPAT_SET_TP_DST:
oa = odp_actions_add(ctx->out, ODPAT_SET_TP_DST);
- oa->tp_port.tp_port = ia->tp_port.tp_port;
+ ctx->flow.tp_dst = oa->tp_port.tp_port = ia->tp_port.tp_port;
break;
case OFPAT_VENDOR:
struct action_xlate_ctx ctx;
COVERAGE_INC(ofproto_ofp2odp);
odp_actions_init(out);
- ctx.flow = flow;
+ ctx.flow = *flow;
ctx.recurse = 0;
ctx.ofproto = ofproto;
ctx.packet = packet;
buffer = NULL;
}
- flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
+ flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
&flow, p, &payload, &actions, NULL, NULL, NULL);
if (error) {
msg = start_stats_reply(request, sizeof *ods);
ods = append_stats_reply(sizeof *ods, ofconn, &msg);
- strncpy(ods->mfr_desc, p->manufacturer, sizeof ods->mfr_desc);
- 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);
+ memset(ods, 0, sizeof *ods);
+ ovs_strlcpy(ods->mfr_desc, p->mfr_desc, sizeof ods->mfr_desc);
+ ovs_strlcpy(ods->hw_desc, p->hw_desc, sizeof ods->hw_desc);
+ ovs_strlcpy(ods->sw_desc, p->sw_desc, sizeof ods->sw_desc);
+ ovs_strlcpy(ods->serial_num, p->serial_desc, sizeof ods->serial_num);
+ ovs_strlcpy(ods->dp_desc, p->dp_desc, sizeof ods->dp_desc);
queue_tx(msg, ofconn, ofconn->reply_counter);
return 0;
memset(ots, 0, sizeof *ots);
ots->table_id = TABLEID_CLASSIFIER;
strcpy(ots->name, "classifier");
- ots->wildcards = htonl(OFPFW_ALL);
+ ots->wildcards = p->tun_id_from_cookie ? htonl(OVSFW_ALL)
+ : htonl(OFPFW_ALL);
ots->max_entries = htonl(65536);
ots->active_count = htonl(n_wild);
ots->lookup_count = htonll(0); /* XXX */
return 0;
}
+static void
+append_port_stat(struct ofport *port, uint16_t port_no, struct ofconn *ofconn,
+ struct ofpbuf *msg)
+{
+ struct netdev_stats stats;
+ struct ofp_port_stats *ops;
+
+ /* Intentionally ignore return value, since errors will set
+ * 'stats' to all-1s, which is correct for OpenFlow, and
+ * netdev_get_stats() will log errors. */
+ netdev_get_stats(port->netdev, &stats);
+
+ ops = append_stats_reply(sizeof *ops, ofconn, &msg);
+ ops->port_no = htons(odp_port_to_ofp_port(port_no));
+ memset(ops->pad, 0, sizeof ops->pad);
+ ops->rx_packets = htonll(stats.rx_packets);
+ ops->tx_packets = htonll(stats.tx_packets);
+ ops->rx_bytes = htonll(stats.rx_bytes);
+ ops->tx_bytes = htonll(stats.tx_bytes);
+ ops->rx_dropped = htonll(stats.rx_dropped);
+ ops->tx_dropped = htonll(stats.tx_dropped);
+ ops->rx_errors = htonll(stats.rx_errors);
+ ops->tx_errors = htonll(stats.tx_errors);
+ ops->rx_frame_err = htonll(stats.rx_frame_errors);
+ ops->rx_over_err = htonll(stats.rx_over_errors);
+ ops->rx_crc_err = htonll(stats.rx_crc_errors);
+ ops->collisions = htonll(stats.collisions);
+}
+
static int
handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn,
- struct ofp_stats_request *request)
+ struct ofp_stats_request *osr,
+ size_t arg_size)
{
+ struct ofp_port_stats_request *psr;
struct ofp_port_stats *ops;
struct ofpbuf *msg;
struct ofport *port;
unsigned int port_no;
- msg = start_stats_reply(request, sizeof *ops * 16);
- PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
- struct netdev_stats stats;
-
- /* Intentionally ignore return value, since errors will set 'stats' to
- * all-1s, which is correct for OpenFlow, and netdev_get_stats() will
- * log errors. */
- netdev_get_stats(port->netdev, &stats);
-
- ops = append_stats_reply(sizeof *ops, ofconn, &msg);
- ops->port_no = htons(odp_port_to_ofp_port(port_no));
- memset(ops->pad, 0, sizeof ops->pad);
- ops->rx_packets = htonll(stats.rx_packets);
- ops->tx_packets = htonll(stats.tx_packets);
- ops->rx_bytes = htonll(stats.rx_bytes);
- ops->tx_bytes = htonll(stats.tx_bytes);
- ops->rx_dropped = htonll(stats.rx_dropped);
- ops->tx_dropped = htonll(stats.tx_dropped);
- ops->rx_errors = htonll(stats.rx_errors);
- ops->tx_errors = htonll(stats.tx_errors);
- ops->rx_frame_err = htonll(stats.rx_frame_errors);
- ops->rx_over_err = htonll(stats.rx_over_errors);
- ops->rx_crc_err = htonll(stats.rx_crc_errors);
- ops->collisions = htonll(stats.collisions);
+ if (arg_size != sizeof *psr) {
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+ }
+ psr = (struct ofp_port_stats_request *) osr->body;
+
+ msg = start_stats_reply(osr, sizeof *ops * 16);
+ if (psr->port_no != htons(OFPP_NONE)) {
+ port = port_array_get(&p->ports,
+ ofp_port_to_odp_port(ntohs(psr->port_no)));
+ if (port) {
+ append_port_stat(port, ntohs(psr->port_no), ofconn, msg);
+ }
+ } else {
+ PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
+ append_port_stat(port, port_no, ofconn, msg);
+ }
}
queue_tx(msg, ofconn, ofconn->reply_counter);
struct ofpbuf *msg;
};
+/* Obtains statistic counters for 'rule' within 'p' and stores them into
+ * '*packet_countp' and '*byte_countp'. If 'rule' is a wildcarded rule, the
+ * returned statistic include statistics for all of 'rule''s subrules. */
static void
query_stats(struct ofproto *p, struct rule *rule,
uint64_t *packet_countp, uint64_t *byte_countp)
struct odp_flow *odp_flows;
size_t n_odp_flows;
+ /* Start from historical data for 'rule' itself that are no longer tracked
+ * by the datapath. This counts, for example, subrules that have
+ * expired. */
packet_count = rule->packet_count;
byte_count = rule->byte_count;
+ /* Prepare to ask the datapath for statistics on 'rule', or if it is
+ * wildcarded then on all of its subrules.
+ *
+ * Also, add any statistics that are not tracked by the datapath for each
+ * subrule. This includes, for example, statistics for packets that were
+ * executed "by hand" by ofproto via dpif_execute() but must be accounted
+ * to a flow. */
n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1;
odp_flows = xzalloc(n_odp_flows * sizeof *odp_flows);
if (rule->cr.wc.wildcards) {
odp_flows[0].key = rule->cr.flow;
}
- packet_count = rule->packet_count;
- byte_count = rule->byte_count;
+ /* Fetch up-to-date statistics from the datapath and add them in. */
if (!dpif_flow_get_multiple(p->dpif, odp_flows, n_odp_flows)) {
size_t i;
for (i = 0; i < n_odp_flows; i++) {
}
free(odp_flows);
+ /* Return the stats to the caller. */
*packet_countp = packet_count;
*byte_countp = byte_count;
}
struct ofp_flow_stats *ofs;
uint64_t packet_count, byte_count;
size_t act_len, len;
+ long long int tdiff = time_msec() - rule->created;
+ uint32_t sec = tdiff / 1000;
+ uint32_t msec = tdiff - (sec * 1000);
if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
return;
ofs->length = htons(len);
ofs->table_id = rule->cr.wc.wildcards ? TABLEID_CLASSIFIER : TABLEID_HASH;
ofs->pad = 0;
- flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofs->match);
- ofs->duration = htonl((time_msec() - rule->created) / 1000);
+ flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards,
+ cbdata->ofproto->tun_id_from_cookie, &ofs->match);
+ ofs->duration_sec = htonl(sec);
+ ofs->duration_nsec = htonl(msec * 1000000);
+ 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);
- ofs->pad2 = 0;
+ memset(ofs->pad2, 0, sizeof ofs->pad2);
ofs->packet_count = htonll(packet_count);
ofs->byte_count = htonll(byte_count);
memcpy(ofs->actions, rule->actions, act_len);
cbdata.ofconn = ofconn;
cbdata.out_port = fsr->out_port;
cbdata.msg = start_stats_reply(osr, 1024);
- cls_rule_from_match(&target, &fsr->match, 0);
+ cls_rule_from_match(&fsr->match, 0, false, 0, &target);
classifier_for_each_match(&p->cls, &target,
table_id_to_include(fsr->table_id),
flow_stats_cb, &cbdata);
}
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,
+ cbdata->ofproto->tun_id_from_cookie, &match);
ds_put_format(results, "duration=%llds, ",
(time_msec() - rule->created) / 1000);
struct flow_stats_ds_cbdata cbdata;
memset(&match, 0, sizeof match);
- match.wildcards = htonl(OFPFW_ALL);
+ match.wildcards = htonl(OVSFW_ALL);
cbdata.ofproto = p;
cbdata.results = results;
- cls_rule_from_match(&target, &match, 0);
+ cls_rule_from_match(&match, 0, false, 0, &target);
classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
flow_stats_ds_cb, &cbdata);
}
cbdata.packet_count = 0;
cbdata.byte_count = 0;
cbdata.n_flows = 0;
- cls_rule_from_match(&target, &asr->match, 0);
+ cls_rule_from_match(&asr->match, 0, false, 0, &target);
classifier_for_each_match(&p->cls, &target,
table_id_to_include(asr->table_id),
aggregate_stats_cb, &cbdata);
return handle_table_stats_request(p, ofconn, osr);
case OFPST_PORT:
- return handle_port_stats_request(p, ofconn, osr);
+ return handle_port_stats_request(p, ofconn, osr, arg_size);
case OFPST_VENDOR:
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
}
}
+/* 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,
- struct ofp_flow_mod *ofm, size_t n_actions)
+ const struct ofp_flow_mod *ofm, size_t n_actions)
{
struct ofpbuf *packet;
struct rule *rule;
flow_t flow;
uint32_t wildcards;
- flow_from_match(&flow, &wildcards, &ofm->match);
+ 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),
+ ntohs(ofm->hard_timeout), ofm->cookie,
ofm->flags & htons(OFPFF_SEND_FLOW_REM));
- cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
+ 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)) {
return error;
}
-static int
-modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm,
- size_t n_actions, uint16_t command, struct rule *rule)
+static struct rule *
+find_flow_strict(struct ofproto *p, const struct ofp_flow_mod *ofm)
{
- if (rule_is_hidden(rule)) {
- return 0;
- }
-
- 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;
-
- if (n_actions == rule->n_actions
- && !memcmp(ofm->actions, rule->actions, actions_len))
- {
- return 0;
- }
-
- free(rule->actions);
- rule->actions = xmemdup(ofm->actions, actions_len);
- rule->n_actions = n_actions;
-
- if (rule->cr.wc.wildcards) {
- COVERAGE_INC(ofproto_mod_wc_flow);
- p->need_revalidate = true;
- } else {
- rule_update_actions(p, rule);
- }
- }
+ uint32_t wildcards;
+ flow_t flow;
- return 0;
+ 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
-modify_flows_strict(struct ofproto *p, const struct ofp_flow_mod *ofm,
- size_t n_actions, uint16_t command)
+send_buffered_packet(struct ofproto *ofproto, struct ofconn *ofconn,
+ struct rule *rule, const struct ofp_flow_mod *ofm)
{
- struct rule *rule;
- uint32_t wildcards;
+ struct ofpbuf *packet;
+ uint16_t in_port;
flow_t flow;
+ int error;
- flow_from_match(&flow, &wildcards, &ofm->match);
- rule = rule_from_cls_rule(classifier_find_rule_exactly(
- &p->cls, &flow, wildcards,
- ntohs(ofm->priority)));
-
- if (rule) {
- if (command == OFPFC_DELETE
- && ofm->out_port != htons(OFPP_NONE)
- && !rule_has_out_port(rule, ofm->out_port)) {
- return 0;
- }
+ if (ofm->buffer_id == htonl(UINT32_MAX)) {
+ return 0;
+ }
- modify_flow(p, ofm, n_actions, command, rule);
+ 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);
+ ofpbuf_delete(packet);
+
return 0;
}
+\f
+/* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */
struct modify_flows_cbdata {
struct ofproto *ofproto;
const struct ofp_flow_mod *ofm;
- uint16_t out_port;
size_t n_actions;
- uint16_t command;
+ 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)
+{
+ struct rule *rule = find_flow_strict(p, ofm);
+ if (rule && !rule_is_hidden(rule)) {
+ modify_flow(p, ofm, n_actions, rule);
+ return send_buffered_packet(p, ofconn, rule, ofm);
+ } else {
+ return add_flow(p, ofconn, ofm, n_actions);
+ }
+}
+
+/* Callback for modify_flows_loose(). */
static void
modify_flows_cb(struct cls_rule *rule_, void *cbdata_)
{
struct rule *rule = rule_from_cls_rule(rule_);
struct modify_flows_cbdata *cbdata = cbdata_;
- if (cbdata->out_port != htons(OFPP_NONE)
- && !rule_has_out_port(rule, cbdata->out_port)) {
- return;
+ if (!rule_is_hidden(rule)) {
+ cbdata->match = rule;
+ modify_flow(cbdata->ofproto, cbdata->ofm, cbdata->n_actions, rule);
}
-
- modify_flow(cbdata->ofproto, cbdata->ofm, cbdata->n_actions,
- cbdata->command, rule);
}
+/* Implements core of OFPFC_MODIFY and OFPFC_MODIFY_STRICT where 'rule' has
+ * been identified as a flow in 'p''s flow table to be modified, by changing
+ * the rule's actions to match those in 'ofm' (which is followed by 'n_actions'
+ * ofp_action[] structures). */
static int
-modify_flows_loose(struct ofproto *p, const struct ofp_flow_mod *ofm,
- size_t n_actions, uint16_t command)
+modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm,
+ size_t n_actions, struct rule *rule)
{
- struct modify_flows_cbdata cbdata;
+ size_t actions_len = n_actions * sizeof *rule->actions;
+
+ rule->flow_cookie = ofm->cookie;
+
+ /* If the actions are the same, do nothing. */
+ if (n_actions == rule->n_actions
+ && !memcmp(ofm->actions, rule->actions, actions_len))
+ {
+ return 0;
+ }
+
+ /* Replace actions. */
+ free(rule->actions);
+ rule->actions = xmemdup(ofm->actions, actions_len);
+ rule->n_actions = n_actions;
+
+ /* Make sure that the datapath gets updated properly. */
+ if (rule->cr.wc.wildcards) {
+ COVERAGE_INC(ofproto_mod_wc_flow);
+ p->need_revalidate = true;
+ } else {
+ rule_update_actions(p, rule);
+ }
+
+ return 0;
+}
+\f
+/* OFPFC_DELETE implementation. */
+
+struct delete_flows_cbdata {
+ struct ofproto *ofproto;
+ uint16_t out_port;
+};
+
+static void delete_flows_cb(struct cls_rule *, void *cbdata_);
+static void delete_flow(struct ofproto *, struct rule *, uint16_t out_port);
+
+/* Implements OFPFC_DELETE. */
+static void
+delete_flows_loose(struct ofproto *p, const struct ofp_flow_mod *ofm)
+{
+ struct delete_flows_cbdata cbdata;
struct cls_rule target;
cbdata.ofproto = p;
- cbdata.ofm = ofm;
- cbdata.out_port = (command == OFPFC_DELETE ? ofm->out_port
- : htons(OFPP_NONE));
- cbdata.n_actions = n_actions;
- cbdata.command = command;
+ cbdata.out_port = ofm->out_port;
- cls_rule_from_match(&target, &ofm->match, 0);
+ 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);
- return 0;
+ delete_flows_cb, &cbdata);
+}
+
+/* Implements OFPFC_DELETE_STRICT. */
+static void
+delete_flow_strict(struct ofproto *p, struct ofp_flow_mod *ofm)
+{
+ struct rule *rule = find_flow_strict(p, ofm);
+ if (rule) {
+ delete_flow(p, rule, ofm->out_port);
+ }
}
+/* Callback for delete_flows_loose(). */
+static void
+delete_flows_cb(struct cls_rule *rule_, void *cbdata_)
+{
+ struct rule *rule = rule_from_cls_rule(rule_);
+ struct delete_flows_cbdata *cbdata = cbdata_;
+
+ delete_flow(cbdata->ofproto, rule, cbdata->out_port);
+}
+
+/* Implements core of OFPFC_DELETE and OFPFC_DELETE_STRICT where 'rule' has
+ * been identified as a flow to delete from 'p''s flow table, by deleting the
+ * flow and sending out a OFPT_FLOW_REMOVED message to any interested
+ * controller.
+ *
+ * Will not delete 'rule' if it is hidden. Will delete 'rule' only if
+ * 'out_port' is htons(OFPP_NONE) or if 'rule' actually outputs to the
+ * specified 'out_port'. */
+static void
+delete_flow(struct ofproto *p, struct rule *rule, uint16_t out_port)
+{
+ if (rule_is_hidden(rule)) {
+ return;
+ }
+
+ if (out_port != htons(OFPP_NONE) && !rule_has_out_port(rule, out_port)) {
+ return;
+ }
+
+ send_flow_removed(p, rule, time_msec(), OFPRR_DELETE);
+ rule_remove(p, rule);
+}
+\f
static int
handle_flow_mod(struct ofproto *p, struct ofconn *ofconn,
struct ofp_flow_mod *ofm)
return add_flow(p, ofconn, ofm, n_actions);
case OFPFC_MODIFY:
- return modify_flows_loose(p, ofm, n_actions, OFPFC_MODIFY);
+ return modify_flows_loose(p, ofconn, ofm, n_actions);
case OFPFC_MODIFY_STRICT:
- return modify_flows_strict(p, ofm, n_actions, OFPFC_MODIFY);
+ return modify_flow_strict(p, ofconn, ofm, n_actions);
case OFPFC_DELETE:
- return modify_flows_loose(p, ofm, n_actions, OFPFC_DELETE);
+ delete_flows_loose(p, ofm);
+ return 0;
case OFPFC_DELETE_STRICT:
- return modify_flows_strict(p, ofm, n_actions, OFPFC_DELETE);
+ delete_flow_strict(p, ofm);
+ return 0;
default:
return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND);
}
}
+static int
+handle_tun_id_from_cookie(struct ofproto *p, struct nxt_tun_id_cookie *msg)
+{
+ int error;
+
+ error = check_ofp_message(&msg->header, OFPT_VENDOR, sizeof *msg);
+ if (error) {
+ return error;
+ }
+
+ p->tun_id_from_cookie = !!msg->set;
+ return 0;
+}
+
static int
handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
{
struct nicira_header *nh;
if (ntohs(ovh->header.length) < sizeof(struct ofp_vendor_header)) {
+ VLOG_WARN_RL(&rl, "received vendor message of length %zu "
+ "(expected at least %zu)",
+ ntohs(ovh->header.length), sizeof(struct ofp_vendor_header));
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)) {
+ VLOG_WARN_RL(&rl, "received Nicira vendor message of length %zu "
+ "(expected at least %zu)",
+ ntohs(ovh->header.length), sizeof(struct nicira_header));
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
}
case NXT_STATUS_REQUEST:
return switch_status_handle_request(p->switch_status, ofconn->rconn,
msg);
+
+ case NXT_TUN_ID_FROM_COOKIE:
+ return handle_tun_id_from_cookie(p, msg);
}
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
payload.data = msg + 1;
payload.size = msg->length - sizeof *msg;
- flow_extract(&payload, msg->port, &flow);
+ flow_extract(&payload, msg->arg, msg->port, &flow);
/* Check with in-band control to see if this packet should be sent
* to the local port regardless of the flow table. */
}
static struct ofpbuf *
-compose_flow_removed(const struct rule *rule, long long int now, uint8_t reason)
+compose_flow_removed(struct ofproto *p, const struct rule *rule,
+ long long int now, uint8_t reason)
{
struct ofp_flow_removed *ofr;
struct ofpbuf *buf;
+ long long int tdiff = now - rule->created;
+ uint32_t sec = tdiff / 1000;
+ uint32_t msec = tdiff - (sec * 1000);
ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf);
- flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofr->match);
+ flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, p->tun_id_from_cookie,
+ &ofr->match);
+ ofr->cookie = rule->flow_cookie;
ofr->priority = htons(rule->cr.priority);
ofr->reason = reason;
- ofr->duration = htonl((now - rule->created) / 1000);
+ ofr->duration_sec = htonl(sec);
+ ofr->duration_nsec = htonl(msec * 1000000);
ofr->idle_timeout = htons(rule->idle_timeout);
ofr->packet_count = htonll(rule->packet_count);
ofr->byte_count = htonll(rule->byte_count);
if (prev) {
queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
} else {
- buf = compose_flow_removed(rule, now, reason);
+ buf = compose_flow_removed(p, rule, now, reason);
}
prev = ofconn;
}
? pktbuf_get_null()
: pktbuf_save(pb, &payload, msg->port));
int send_len = (buffer_id != UINT32_MAX ? ofconn->miss_send_len
- : UINT32_MAX);
+ : INT_MAX);
do_send_packet_in(ofconn, buffer_id, packet, send_len);
}
}