#include <errno.h>
+#include "bfd.h"
#include "bond.h"
#include "bundle.h"
#include "byte-order.h"
#include "ofp-parse.h"
#include "ofp-print.h"
#include "ofproto-dpif-governor.h"
+#include "ofproto-dpif-ipfix.h"
#include "ofproto-dpif-sflow.h"
#include "poll-loop.h"
#include "simap.h"
* this flow when actions change header fields. */
struct flow flow;
+ /* Flow at the last commit. */
+ struct flow base_flow;
+
+ /* Tunnel IP destination address as received. This is stored separately
+ * as the base_flow.tunnel is cleared on init to reflect the datapath
+ * behavior. Used to make sure not to send tunneled output to ourselves,
+ * which might lead to an infinite loop. This could happen easily
+ * if a tunnel is marked as 'ip_remote=flow', and the flow does not
+ * actually set the tun_dst field. */
+ ovs_be32 orig_tunnel_ip_dst;
+
/* stack for the push and pop actions.
* Each stack element is of the type "union mf_subvalue". */
struct ofpbuf stack;
int recurse; /* Recursion level, via xlate_table_action. */
bool max_resubmit_trigger; /* Recursed too deeply during translation. */
- struct flow base_flow; /* Flow at the last commit. */
uint32_t orig_skb_priority; /* Priority when packet arrived. */
uint8_t table_id; /* OpenFlow table ID where flow was found. */
uint32_t sflow_n_outputs; /* Number of output ports. */
* This member should be removed when the VLAN splinters feature is no
* longer needed. */
ovs_be16 vlan_tci;
-
- /* If received on a tunnel, the IP TOS value of the tunnel. */
- uint8_t tunnel_ip_tos;
};
static void action_xlate_ctx_init(struct action_xlate_ctx *,
static size_t put_userspace_action(const struct ofproto_dpif *,
struct ofpbuf *odp_actions,
const struct flow *,
- const union user_action_cookie *);
+ const union user_action_cookie *,
+ const size_t);
static void compose_slow_path(const struct ofproto_dpif *, const struct flow *,
enum slow_path_reason,
static void subfacet_update_stats(struct subfacet *,
const struct dpif_flow_stats *);
static void subfacet_make_actions(struct subfacet *,
- const struct ofpbuf *packet,
- struct ofpbuf *odp_actions);
+ const struct ofpbuf *packet);
static int subfacet_install(struct subfacet *,
const struct nlattr *actions, size_t actions_len,
struct dpif_flow_stats *, enum slow_path_reason);
const struct flow *, uint32_t hash);
static struct facet *facet_lookup_valid(struct ofproto_dpif *,
const struct flow *, uint32_t hash);
-static void facet_revalidate(struct facet *);
+static bool facet_revalidate(struct facet *);
static bool facet_check_consistency(struct facet *);
static void facet_flush_stats(struct facet *);
struct ofbundle *bundle; /* Bundle that contains this port, if any. */
struct list bundle_node; /* In struct ofbundle's "ports" list. */
struct cfm *cfm; /* Connectivity Fault Management, if any. */
+ struct bfd *bfd; /* BFD, if any. */
tag_type tag; /* Tag associated with this port. */
bool may_enable; /* May be enabled in bonds. */
long long int carrier_seq; /* Carrier status changes. */
static struct ofport_dpif *
ofport_dpif_cast(const struct ofport *ofport)
{
- ovs_assert(ofport->ofproto->ofproto_class == &ofproto_dpif_class);
return ofport ? CONTAINER_OF(ofport, struct ofport_dpif, up) : NULL;
}
static void port_run(struct ofport_dpif *);
static void port_run_fast(struct ofport_dpif *);
static void port_wait(struct ofport_dpif *);
+static int set_bfd(struct ofport *, const struct smap *);
static int set_cfm(struct ofport *, const struct cfm_settings *);
static void ofport_clear_priorities(struct ofport_dpif *);
static void run_fast_rl(void);
/* Bridging. */
struct netflow *netflow;
struct dpif_sflow *sflow;
+ struct dpif_ipfix *ipfix;
struct hmap bundles; /* Contains "struct ofbundle"s. */
struct mac_learning *ml;
struct ofmirror *mirrors[MAX_MIRRORS];
static size_t compose_sflow_action(const struct ofproto_dpif *,
struct ofpbuf *odp_actions,
const struct flow *, uint32_t odp_port);
+static void compose_ipfix_action(const struct ofproto_dpif *,
+ struct ofpbuf *odp_actions,
+ const struct flow *);
static void add_mirror_actions(struct action_xlate_ctx *ctx,
const struct flow *flow);
/* Global variables. */
ofproto->netflow = NULL;
ofproto->sflow = NULL;
+ ofproto->ipfix = NULL;
ofproto->stp = NULL;
hmap_init(&ofproto->bundles);
ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
ofproto->backer->need_revalidate = REV_RECONFIGURE;
port->bundle = NULL;
port->cfm = NULL;
+ port->bfd = NULL;
port->tag = tag_create_random();
port->may_enable = true;
port->stp_port = NULL;
port->carrier_seq = netdev_get_carrier_resets(netdev);
if (netdev_vport_is_patch(netdev)) {
- /* XXX By bailing out here, we don't do required sFlow work. */
+ /* By bailing out here, we don't submit the port to the sFlow module
+ * to be considered for counter polling export. This is correct
+ * because the patch port represents an interface that sFlow considers
+ * to be "internal" to the switch as a whole, and therefore not an
+ * candidate for counter polling. */
port->odp_port = OVSP_NONE;
return 0;
}
ofproto->backer->need_revalidate = REV_RECONFIGURE;
bundle_remove(port_);
set_cfm(port_, NULL);
+ set_bfd(port_, NULL);
if (ofproto->sflow) {
dpif_sflow_del_port(ofproto->sflow, port->odp_port);
}
return 0;
}
+static int
+set_ipfix(
+ struct ofproto *ofproto_,
+ const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options,
+ const struct ofproto_ipfix_flow_exporter_options *flow_exporters_options,
+ size_t n_flow_exporters_options)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct dpif_ipfix *di = ofproto->ipfix;
+
+ if (bridge_exporter_options || flow_exporters_options) {
+ if (!di) {
+ di = ofproto->ipfix = dpif_ipfix_create();
+ }
+ dpif_ipfix_set_options(
+ di, bridge_exporter_options, flow_exporters_options,
+ n_flow_exporters_options);
+ } else {
+ if (di) {
+ dpif_ipfix_destroy(di);
+ ofproto->ipfix = NULL;
+ }
+ }
+ return 0;
+}
+
static int
set_cfm(struct ofport *ofport_, const struct cfm_settings *s)
{
ofproto = ofproto_dpif_cast(ofport->up.ofproto);
ofproto->backer->need_revalidate = REV_RECONFIGURE;
- ofport->cfm = cfm_create(netdev_get_name(ofport->up.netdev));
+ ofport->cfm = cfm_create(ofport->up.netdev);
}
if (cfm_configure(ofport->cfm, s)) {
return false;
}
}
+
+static int
+set_bfd(struct ofport *ofport_, const struct smap *cfg)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto);
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ struct bfd *old;
+
+ old = ofport->bfd;
+ ofport->bfd = bfd_configure(old, netdev_get_name(ofport->up.netdev), cfg);
+ if (ofport->bfd != old) {
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
+ }
+
+ return 0;
+}
+
+static int
+get_bfd_status(struct ofport *ofport_, struct smap *smap)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+ if (ofport->bfd) {
+ bfd_get_status(ofport->bfd, smap);
+ return 0;
+ } else {
+ return ENOENT;
+ }
+}
\f
/* Spanning Tree. */
}
static bool
-bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
+bundle_add_port(struct ofbundle *bundle, uint16_t ofp_port,
struct lacp_slave_settings *lacp)
{
struct ofport_dpif *port;
send_packet(ofport, &packet);
ofpbuf_uninit(&packet);
}
+
+ if (ofport->bfd && bfd_should_send_packet(ofport->bfd)) {
+ struct ofpbuf packet;
+
+ ofpbuf_init(&packet, 0);
+ bfd_put_packet(ofport->bfd, &packet, ofport->up.pp.hw_addr);
+ send_packet(ofport, &packet);
+ ofpbuf_uninit(&packet);
+ }
}
static void
}
}
+ if (ofport->bfd) {
+ bfd_run(ofport->bfd);
+ enable = enable && bfd_forwarding(ofport->bfd);
+ }
+
if (ofport->bundle) {
enable = enable && lacp_slave_may_enable(ofport->bundle->lacp, ofport);
if (carrier_changed) {
if (ofport->cfm) {
cfm_wait(ofport->cfm);
}
+
+ if (ofport->bfd) {
+ bfd_wait(ofport->bfd);
+ }
}
static int
return error;
}
-/* Account packets for LOCAL port. */
-static void
-ofproto_update_local_port_stats(const struct ofproto *ofproto_,
- size_t tx_size, size_t rx_size)
-{
- struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-
- if (rx_size) {
- ofproto->stats.rx_packets++;
- ofproto->stats.rx_bytes += rx_size;
- }
- if (tx_size) {
- ofproto->stats.tx_packets++;
- ofproto->stats.tx_bytes += tx_size;
- }
-}
-
struct port_dump_state {
uint32_t bucket;
uint32_t offset;
cfm_process_heartbeat(ofport->cfm, packet);
}
return SLOW_CFM;
+ } else if (ofport->bfd && bfd_should_process_flow(flow)) {
+ if (packet) {
+ bfd_process_packet(ofport->bfd, flow, packet);
+ }
+ return SLOW_BFD;
} else if (ofport->bundle && ofport->bundle->lacp
&& flow->dl_type == htons(ETH_TYPE_LACP)) {
if (packet) {
COVERAGE_INC(facet_suppress);
+ handle_flow_miss_common(rule, packet, &miss->flow);
+
ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
dpif_flow_stats_extract(&miss->flow, packet, now, &stats);
rule_credit_stats(rule, &stats);
- action_xlate_ctx_init(&ctx, ofproto, &miss->flow,
- &miss->initial_vals, rule, 0, packet);
+ action_xlate_ctx_init(&ctx, ofproto, &miss->flow, &miss->initial_vals,
+ rule, stats.tcp_flags, packet);
ctx.resubmit_stats = &stats;
xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
LIST_FOR_EACH (packet, list_node, &miss->packets) {
struct flow_miss_op *op = &ops[*n_ops];
struct dpif_flow_stats stats;
- struct ofpbuf odp_actions;
handle_flow_miss_common(facet->rule, packet, &miss->flow);
- ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
- if (!subfacet->actions || subfacet->slow) {
- subfacet_make_actions(subfacet, packet, &odp_actions);
+ if (!subfacet->actions) {
+ subfacet_make_actions(subfacet, packet);
+ } else if (subfacet->slow) {
+ struct action_xlate_ctx ctx;
+
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
+ &subfacet->initial_vals, facet->rule, 0,
+ packet);
+ xlate_actions_for_side_effects(&ctx, facet->rule->up.ofpacts,
+ facet->rule->up.ofpacts_len);
}
dpif_flow_stats_extract(&facet->flow, packet, now, &stats);
struct dpif_execute *execute = &op->dpif_op.u.execute;
init_flow_miss_execute_op(miss, packet, op);
- if (!subfacet->slow) {
- execute->actions = subfacet->actions;
- execute->actions_len = subfacet->actions_len;
- ofpbuf_uninit(&odp_actions);
- } else {
- execute->actions = odp_actions.data;
- execute->actions_len = odp_actions.size;
- op->garbage = ofpbuf_get_uninit_pointer(&odp_actions);
- }
+ execute->actions = subfacet->actions;
+ execute->actions_len = subfacet->actions_len;
(*n_ops)++;
- } else {
- ofpbuf_uninit(&odp_actions);
}
}
if (!facet) {
struct rule_dpif *rule = rule_dpif_lookup(ofproto, &miss->flow);
- if (!flow_miss_should_make_facet(ofproto, miss, hash)) {
+ /* There does not exist a bijection between 'struct flow' and datapath
+ * flow keys with fitness ODP_FIT_TO_LITTLE. This breaks a fundamental
+ * assumption used throughout the facet and subfacet handling code.
+ * Since we have to handle these misses in userspace anyway, we simply
+ * skip facet creation, avoiding the problem alltogether. */
+ if (miss->key_fitness == ODP_FIT_TOO_LITTLE
+ || !flow_miss_should_make_facet(ofproto, miss, hash)) {
handle_flow_miss_without_facet(miss, rule, ops, n_ops);
return;
}
* to the VLAN TCI with which the packet was really received, that is, the
* actual VLAN TCI extracted by odp_flow_key_to_flow(). (This differs from
* the value returned in flow->vlan_tci only for packets received on
- * VLAN splinters.) Also, if received on an IP tunnel, sets
- * 'initial_vals->tunnel_ip_tos' to the tunnel's IP TOS.
+ * VLAN splinters.)
*
* Similarly, this function also includes some logic to help with tunnels. It
* may modify 'flow' as necessary to make the tunneling implementation
if (initial_vals) {
initial_vals->vlan_tci = flow->vlan_tci;
- initial_vals->tunnel_ip_tos = flow->tunnel.ip_tos;
}
if (odp_in_port) {
*odp_in_port = flow->in_port;
}
- if (tnl_port_should_receive(flow)) {
- const struct ofport *ofport = tnl_port_receive(flow);
- if (!ofport) {
- flow->in_port = OFPP_NONE;
- goto exit;
- }
- port = ofport_dpif_cast(ofport);
-
- /* We can't reproduce 'key' from 'flow'. */
- fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
+ port = (tnl_port_should_receive(flow)
+ ? ofport_dpif_cast(tnl_port_receive(flow))
+ : odp_port_to_ofport(backer, flow->in_port));
+ flow->in_port = port ? port->up.ofp_port : OFPP_NONE;
+ if (!port) {
+ goto exit;
+ }
- /* XXX: Since the tunnel module is not scoped per backer, it's
- * theoretically possible that we'll receive an ofport belonging to an
- * entirely different datapath. In practice, this can't happen because
- * no platforms has two separate datapaths which each support
- * tunneling. */
- ovs_assert(ofproto_dpif_cast(port->up.ofproto)->backer == backer);
- } else {
- port = odp_port_to_ofport(backer, flow->in_port);
- if (!port) {
- flow->in_port = OFPP_NONE;
- goto exit;
- }
+ /* XXX: Since the tunnel module is not scoped per backer, for a tunnel port
+ * it's theoretically possible that we'll receive an ofport belonging to an
+ * entirely different datapath. In practice, this can't happen because no
+ * platforms has two separate datapaths which each support tunneling. */
+ ovs_assert(ofproto_dpif_cast(port->up.ofproto)->backer == backer);
- flow->in_port = port->up.ofp_port;
- if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
- if (packet) {
- /* Make the packet resemble the flow, so that it gets sent to
- * an OpenFlow controller properly, so that it looks correct
- * for sFlow, and so that flow_extract() will get the correct
- * vlan_tci if it is called on 'packet'.
- *
- * The allocated space inside 'packet' probably also contains
- * 'key', that is, both 'packet' and 'key' are probably part of
- * a struct dpif_upcall (see the large comment on that
- * structure definition), so pushing data on 'packet' is in
- * general not a good idea since it could overwrite 'key' or
- * free it as a side effect. However, it's OK in this special
- * case because we know that 'packet' is inside a Netlink
- * attribute: pushing 4 bytes will just overwrite the 4-byte
- * "struct nlattr", which is fine since we don't need that
- * header anymore. */
- eth_push_vlan(packet, flow->vlan_tci);
- }
- /* We can't reproduce 'key' from 'flow'. */
- fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
+ if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
+ if (packet) {
+ /* Make the packet resemble the flow, so that it gets sent to
+ * an OpenFlow controller properly, so that it looks correct
+ * for sFlow, and so that flow_extract() will get the correct
+ * vlan_tci if it is called on 'packet'.
+ *
+ * The allocated space inside 'packet' probably also contains
+ * 'key', that is, both 'packet' and 'key' are probably part of
+ * a struct dpif_upcall (see the large comment on that
+ * structure definition), so pushing data on 'packet' is in
+ * general not a good idea since it could overwrite 'key' or
+ * free it as a side effect. However, it's OK in this special
+ * case because we know that 'packet' is inside a Netlink
+ * attribute: pushing 4 bytes will just overwrite the 4-byte
+ * "struct nlattr", which is fine since we don't need that
+ * header anymore. */
+ eth_push_vlan(packet, flow->vlan_tci);
}
+ /* We can't reproduce 'key' from 'flow'. */
+ fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
}
error = 0;
hmap_destroy(&todo);
}
-static enum { SFLOW_UPCALL, MISS_UPCALL, BAD_UPCALL }
+static enum { SFLOW_UPCALL, MISS_UPCALL, BAD_UPCALL, FLOW_SAMPLE_UPCALL,
+ IPFIX_UPCALL }
classify_upcall(const struct dpif_upcall *upcall)
{
+ size_t userdata_len;
union user_action_cookie cookie;
/* First look at the upcall type. */
VLOG_WARN_RL(&rl, "action upcall missing cookie");
return BAD_UPCALL;
}
- if (nl_attr_get_size(upcall->userdata) != sizeof(cookie)) {
+ userdata_len = nl_attr_get_size(upcall->userdata);
+ if (userdata_len < sizeof cookie.type
+ || userdata_len > sizeof cookie) {
VLOG_WARN_RL(&rl, "action upcall cookie has unexpected size %zu",
- nl_attr_get_size(upcall->userdata));
+ userdata_len);
return BAD_UPCALL;
}
- memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof(cookie));
- switch (cookie.type) {
- case USER_ACTION_COOKIE_SFLOW:
+ memset(&cookie, 0, sizeof cookie);
+ memcpy(&cookie, nl_attr_get(upcall->userdata), userdata_len);
+ if (userdata_len == sizeof cookie.sflow
+ && cookie.type == USER_ACTION_COOKIE_SFLOW) {
return SFLOW_UPCALL;
-
- case USER_ACTION_COOKIE_SLOW_PATH:
+ } else if (userdata_len == sizeof cookie.slow_path
+ && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
return MISS_UPCALL;
-
- case USER_ACTION_COOKIE_UNSPEC:
- default:
- VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64,
- nl_attr_get_u64(upcall->userdata));
+ } else if (userdata_len == sizeof cookie.flow_sample
+ && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
+ return FLOW_SAMPLE_UPCALL;
+ } else if (userdata_len == sizeof cookie.ipfix
+ && cookie.type == USER_ACTION_COOKIE_IPFIX) {
+ return IPFIX_UPCALL;
+ } else {
+ VLOG_WARN_RL(&rl, "invalid user cookie of type %"PRIu16
+ " and size %zu", cookie.type, userdata_len);
return BAD_UPCALL;
}
}
return;
}
- memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof(cookie));
+ memset(&cookie, 0, sizeof cookie);
+ memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof cookie.sflow);
dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
odp_in_port, &cookie);
}
+static void
+handle_flow_sample_upcall(struct dpif_backer *backer,
+ const struct dpif_upcall *upcall)
+{
+ struct ofproto_dpif *ofproto;
+ union user_action_cookie cookie;
+ struct flow flow;
+
+ if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
+ &flow, NULL, &ofproto, NULL, NULL)
+ || !ofproto->ipfix) {
+ return;
+ }
+
+ memset(&cookie, 0, sizeof cookie);
+ memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof cookie.flow_sample);
+
+ /* The flow reflects exactly the contents of the packet. Sample
+ * the packet using it. */
+ dpif_ipfix_flow_sample(ofproto->ipfix, upcall->packet, &flow,
+ cookie.flow_sample.collector_set_id,
+ cookie.flow_sample.probability,
+ cookie.flow_sample.obs_domain_id,
+ cookie.flow_sample.obs_point_id);
+}
+
+static void
+handle_ipfix_upcall(struct dpif_backer *backer,
+ const struct dpif_upcall *upcall)
+{
+ struct ofproto_dpif *ofproto;
+ struct flow flow;
+
+ if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
+ &flow, NULL, &ofproto, NULL, NULL)
+ || !ofproto->ipfix) {
+ return;
+ }
+
+ /* The flow reflects exactly the contents of the packet. Sample
+ * the packet using it. */
+ dpif_ipfix_bridge_sample(ofproto->ipfix, upcall->packet, &flow);
+}
+
static int
handle_upcalls(struct dpif_backer *backer, unsigned int max_batch)
{
ofpbuf_uninit(buf);
break;
+ case FLOW_SAMPLE_UPCALL:
+ handle_flow_sample_upcall(backer, upcall);
+ ofpbuf_uninit(buf);
+ break;
+
+ case IPFIX_UPCALL:
+ handle_ipfix_upcall(backer, upcall);
+ ofpbuf_uninit(buf);
+ break;
+
case BAD_UPCALL:
ofpbuf_uninit(buf);
break;
&ofproto->subfacets) {
long long int cutoff;
- cutoff = (subfacet->slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)
+ cutoff = (subfacet->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)
? special_cutoff
: normal_cutoff);
if (subfacet->used < cutoff) {
netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
}
- facet->rule->packet_count += facet->packet_count;
- facet->rule->byte_count += facet->byte_count;
-
/* Reset counters to prevent double counting if 'facet' ever gets
* reinstalled. */
facet_reset_counters(facet);
if (facet
&& (ofproto->backer->need_revalidate
|| tag_set_intersects(&ofproto->backer->revalidate_set,
- facet->tags))) {
- facet_revalidate(facet);
-
- /* facet_revalidate() may have destroyed 'facet'. */
+ facet->tags))
+ && !facet_revalidate(facet)) {
facet = facet_find(ofproto, flow, hash);
}
}
want_path = subfacet_want_path(subfacet->slow);
- if (want_path == SF_SLOW_PATH && subfacet->path == SF_SLOW_PATH) {
- /* The actions for slow-path flows may legitimately vary from one
- * packet to the next. We're done. */
- continue;
- }
if (!subfacet_should_install(subfacet, subfacet->slow, &odp_actions)) {
continue;
* where it is and recompiles its actions anyway.
*
* - If any of 'facet''s subfacets correspond to a new flow according to
- * ofproto_receive(), 'facet' is removed. */
-static void
+ * ofproto_receive(), 'facet' is removed.
+ *
+ * Returns true if 'facet' is still valid. False if 'facet' was removed. */
+static bool
facet_revalidate(struct facet *facet)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
|| recv_ofproto != ofproto
|| memcmp(&recv_flow, &facet->flow, sizeof recv_flow)) {
facet_remove(facet);
- return;
+ return false;
}
}
memset(&ctx, 0, sizeof ctx);
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
- enum slow_path_reason slow;
-
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
&subfacet->initial_vals, new_rule, 0, NULL);
xlate_actions(&ctx, new_rule->up.ofpacts, new_rule->up.ofpacts_len,
&odp_actions);
- slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
- if (subfacet_should_install(subfacet, slow, &odp_actions)) {
+ if (subfacet_should_install(subfacet, ctx.slow, &odp_actions)) {
struct dpif_flow_stats stats;
- subfacet_install(subfacet,
- odp_actions.data, odp_actions.size, &stats, slow);
+ subfacet_install(subfacet, odp_actions.data, odp_actions.size,
+ &stats, ctx.slow);
subfacet_update_stats(subfacet, &stats);
if (!new_actions) {
i = 0;
LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
- subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
+ subfacet->slow = ctx.slow;
if (new_actions && new_actions[i].odp_actions) {
free(subfacet->actions);
facet->used = new_rule->up.created;
facet->prev_used = facet->used;
}
+
+ return true;
}
/* Updates 'facet''s used time. Caller is responsible for calling
facet->prev_byte_count = facet->byte_count;
facet->prev_used = facet->used;
+ rule_credit_stats(facet->rule, &stats);
flow_push_stats(facet, &stats);
update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto),
subfacet->dp_byte_count = 0;
subfacet->actions_len = 0;
subfacet->actions = NULL;
- subfacet->slow = (subfacet->key_fitness == ODP_FIT_TOO_LITTLE
- ? SLOW_MATCH
- : 0);
+ subfacet->slow = 0;
subfacet->path = SF_NOT_INSTALLED;
subfacet->initial_vals = miss->initial_vals;
subfacet->odp_in_port = miss->odp_in_port;
}
}
-/* Composes the datapath actions for 'subfacet' based on its rule's actions.
- * Translates the actions into 'odp_actions', which the caller must have
- * initialized and is responsible for uninitializing. */
+/* Composes the datapath actions for 'subfacet' based on its rule's actions. */
static void
-subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
- struct ofpbuf *odp_actions)
+subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet)
{
struct facet *facet = subfacet->facet;
struct rule_dpif *rule = facet->rule;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
struct action_xlate_ctx ctx;
+ struct ofpbuf odp_actions;
+ uint64_t stub[1024 / 8];
+ ofpbuf_use_stub(&odp_actions, stub, sizeof stub);
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
&subfacet->initial_vals, rule, 0, packet);
- xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, odp_actions);
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions);
facet->tags = ctx.tags;
facet->has_learn = ctx.has_learn;
facet->has_normal = ctx.has_normal;
facet->nf_flow.output_iface = ctx.nf_output_iface;
facet->mirrors = ctx.mirrors;
- subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
- if (subfacet->actions_len != odp_actions->size
- || memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) {
- free(subfacet->actions);
- subfacet->actions_len = odp_actions->size;
- subfacet->actions = xmemdup(odp_actions->data, odp_actions->size);
- }
+ subfacet->slow = ctx.slow;
+
+ ovs_assert(!subfacet->actions);
+ subfacet->actions_len = odp_actions.size;
+ subfacet->actions = ofpbuf_steal_data(&odp_actions);
}
/* Updates 'subfacet''s datapath flow, setting its actions to 'actions_len'
rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
{
struct rule_dpif *rule = rule_dpif_cast(rule_);
- struct facet *facet;
/* push_all_stats() can handle flow misses which, when using the learn
* action, can cause rules to be added and deleted. This can corrupt our
* in facets. This counts, for example, facets that have expired. */
*packets = rule->packet_count;
*bytes = rule->byte_count;
-
- /* Add any statistics that are tracked by facets. This includes
- * statistical data recently updated by ofproto_update_stats() as well as
- * stats for packets that were executed "by hand" via dpif_execute(). */
- LIST_FOR_EACH (facet, list_node, &rule->facets) {
- *packets += facet->packet_count;
- *bytes += facet->byte_count;
- }
}
static void
rule_credit_stats(rule, &stats);
initial_vals.vlan_tci = flow->vlan_tci;
- initial_vals.tunnel_ip_tos = flow->tunnel.ip_tos;
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
action_xlate_ctx_init(&ctx, ofproto, flow, &initial_vals,
rule, stats.tcp_flags, packet);
static int
send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
{
- const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf key, odp_actions;
+ struct dpif_flow_stats stats;
struct odputil_keybuf keybuf;
- uint32_t odp_port;
+ struct ofpact_output output;
+ struct action_xlate_ctx ctx;
struct flow flow;
int error;
- flow_extract(packet, 0, 0, NULL, OFPP_LOCAL, &flow);
- if (netdev_vport_is_patch(ofport->up.netdev)) {
- struct ofproto_dpif *peer_ofproto;
- struct dpif_flow_stats stats;
- struct ofport_dpif *peer;
- struct rule_dpif *rule;
-
- peer = ofport_get_peer(ofport);
- if (!peer) {
- return ENODEV;
- }
-
- dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
- netdev_vport_inc_tx(ofport->up.netdev, &stats);
- netdev_vport_inc_rx(peer->up.netdev, &stats);
-
- flow.in_port = peer->up.ofp_port;
- peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
- rule = rule_dpif_lookup(peer_ofproto, &flow);
- rule_dpif_execute(rule, &flow, packet);
-
- return 0;
- }
-
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
- if (ofport->tnl_port) {
- struct dpif_flow_stats stats;
-
- odp_port = tnl_port_send(ofport->tnl_port, &flow);
- if (odp_port == OVSP_NONE) {
- return ENODEV;
- }
+ /* Use OFPP_NONE as the in_port to avoid special packet processing. */
+ flow_extract(packet, 0, 0, NULL, OFPP_NONE, &flow);
+ odp_flow_key_from_flow(&key, &flow, ofp_port_to_odp_port(ofproto,
+ OFPP_LOCAL));
+ dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
- dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
- netdev_vport_inc_tx(ofport->up.netdev, &stats);
- odp_put_tunnel_action(&flow.tunnel, &odp_actions);
- odp_put_skb_mark_action(flow.skb_mark, &odp_actions);
- } else {
- odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
- flow.vlan_tci);
- if (odp_port != ofport->odp_port) {
- eth_pop_vlan(packet);
- flow.vlan_tci = htons(0);
- }
- }
+ ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output);
+ output.port = ofport->up.ofp_port;
+ output.max_len = 0;
- ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
- odp_flow_key_from_flow(&key, &flow,
- ofp_port_to_odp_port(ofproto, flow.in_port));
-
- compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
+ action_xlate_ctx_init(&ctx, ofproto, &flow, NULL, NULL, 0, packet);
+ ctx.resubmit_stats = &stats;
+ xlate_actions(&ctx, &output.ofpact, sizeof output, &odp_actions);
- nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
error = dpif_execute(ofproto->backer->dpif,
key.data, key.size,
odp_actions.data, odp_actions.size,
ofpbuf_uninit(&odp_actions);
if (error) {
- VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
- ofproto->up.name, odp_port, strerror(error));
+ VLOG_WARN_RL(&rl, "%s: failed to send packet on port %s (%s)",
+ ofproto->up.name, netdev_get_name(ofport->up.netdev),
+ strerror(error));
}
- ofproto_update_local_port_stats(ofport->up.ofproto, packet->size, 0);
+
+ ofproto->stats.tx_packets++;
+ ofproto->stats.tx_bytes += packet->size;
return error;
}
\f
cookie.slow_path.reason = slow;
ofpbuf_use_stack(&buf, stub, stub_size);
- if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
+ if (slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)) {
uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT32_MAX);
- odp_put_userspace_action(pid, &cookie, sizeof cookie, &buf);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, &buf);
} else {
- put_userspace_action(ofproto, &buf, flow, &cookie);
+ put_userspace_action(ofproto, &buf, flow, &cookie,
+ sizeof cookie.slow_path);
}
*actionsp = buf.data;
*actions_lenp = buf.size;
put_userspace_action(const struct ofproto_dpif *ofproto,
struct ofpbuf *odp_actions,
const struct flow *flow,
- const union user_action_cookie *cookie)
+ const union user_action_cookie *cookie,
+ const size_t cookie_size)
{
uint32_t pid;
pid = dpif_port_get_pid(ofproto->backer->dpif,
ofp_port_to_odp_port(ofproto, flow->in_port));
- return odp_put_userspace_action(pid, cookie, sizeof *cookie, odp_actions);
+ return odp_put_userspace_action(pid, cookie, cookie_size, odp_actions);
+}
+
+/* Compose SAMPLE action for sFlow or IPFIX. The given probability is
+ * the number of packets out of UINT32_MAX to sample. The given
+ * cookie is passed back in the callback for each sampled packet.
+ */
+static size_t
+compose_sample_action(const struct ofproto_dpif *ofproto,
+ struct ofpbuf *odp_actions,
+ const struct flow *flow,
+ const uint32_t probability,
+ const union user_action_cookie *cookie,
+ const size_t cookie_size)
+{
+ size_t sample_offset, actions_offset;
+ int cookie_offset;
+
+ sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
+
+ nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
+
+ actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
+ cookie_offset = put_userspace_action(ofproto, odp_actions, flow, cookie,
+ cookie_size);
+
+ nl_msg_end_nested(odp_actions, actions_offset);
+ nl_msg_end_nested(odp_actions, sample_offset);
+ return cookie_offset;
}
static void
}
}
-/* Compose SAMPLE action for sFlow. */
+/* Compose SAMPLE action for sFlow bridge sampling. */
static size_t
compose_sflow_action(const struct ofproto_dpif *ofproto,
struct ofpbuf *odp_actions,
{
uint32_t probability;
union user_action_cookie cookie;
- size_t sample_offset, actions_offset;
- int cookie_offset;
if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
return 0;
}
- sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
-
- /* Number of packets out of UINT_MAX to sample. */
probability = dpif_sflow_get_probability(ofproto->sflow);
- nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
-
- actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
compose_sflow_cookie(ofproto, htons(0), odp_port,
odp_port == OVSP_NONE ? 0 : 1, &cookie);
- cookie_offset = put_userspace_action(ofproto, odp_actions, flow, &cookie);
- nl_msg_end_nested(odp_actions, actions_offset);
- nl_msg_end_nested(odp_actions, sample_offset);
- return cookie_offset;
+ return compose_sample_action(ofproto, odp_actions, flow, probability,
+ &cookie, sizeof cookie.sflow);
+}
+
+static void
+compose_flow_sample_cookie(uint16_t probability, uint32_t collector_set_id,
+ uint32_t obs_domain_id, uint32_t obs_point_id,
+ union user_action_cookie *cookie)
+{
+ cookie->type = USER_ACTION_COOKIE_FLOW_SAMPLE;
+ cookie->flow_sample.probability = probability;
+ cookie->flow_sample.collector_set_id = collector_set_id;
+ cookie->flow_sample.obs_domain_id = obs_domain_id;
+ cookie->flow_sample.obs_point_id = obs_point_id;
}
-/* SAMPLE action must be first action in any given list of actions.
- * At this point we do not have all information required to build it. So try to
- * build sample action as complete as possible. */
+static void
+compose_ipfix_cookie(union user_action_cookie *cookie)
+{
+ cookie->type = USER_ACTION_COOKIE_IPFIX;
+}
+
+/* Compose SAMPLE action for IPFIX bridge sampling. */
+static void
+compose_ipfix_action(const struct ofproto_dpif *ofproto,
+ struct ofpbuf *odp_actions,
+ const struct flow *flow)
+{
+ uint32_t probability;
+ union user_action_cookie cookie;
+
+ if (!ofproto->ipfix || flow->in_port == OFPP_NONE) {
+ return;
+ }
+
+ probability = dpif_ipfix_get_bridge_exporter_probability(ofproto->ipfix);
+ compose_ipfix_cookie(&cookie);
+
+ compose_sample_action(ofproto, odp_actions, flow, probability,
+ &cookie, sizeof cookie.ipfix);
+}
+
+/* SAMPLE action for sFlow must be first action in any given list of
+ * actions. At this point we do not have all information required to
+ * build it. So try to build sample action as complete as possible. */
static void
add_sflow_action(struct action_xlate_ctx *ctx)
{
ctx->sflow_n_outputs = 0;
}
+/* SAMPLE action for IPFIX must be 1st or 2nd action in any given list
+ * of actions, eventually after the SAMPLE action for sFlow. */
+static void
+add_ipfix_action(struct action_xlate_ctx *ctx)
+{
+ compose_ipfix_action(ctx->ofproto, ctx->odp_actions, &ctx->flow);
+}
+
/* Fix SAMPLE action according to data collected while composing ODP actions.
* We need to fix SAMPLE actions OVS_SAMPLE_ATTR_ACTIONS attribute, i.e. nested
* USERSPACE action's user-cookie which is required for sflow. */
}
cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset,
- sizeof(*cookie));
+ sizeof cookie->sflow);
ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
bool check_stp)
{
const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
- ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
- ovs_be64 flow_tun_id = ctx->flow.tunnel.tun_id;
- uint8_t flow_nw_tos = ctx->flow.nw_tos;
+ ovs_be16 flow_vlan_tci;
+ uint32_t flow_skb_mark;
+ uint8_t flow_nw_tos;
struct priority_to_dscp *pdscp;
uint32_t out_port, odp_port;
return;
}
+ flow_vlan_tci = ctx->flow.vlan_tci;
+ flow_skb_mark = ctx->flow.skb_mark;
+ flow_nw_tos = ctx->flow.nw_tos;
+
pdscp = get_priority(ofport, ctx->flow.skb_priority);
if (pdscp) {
ctx->flow.nw_tos &= ~IP_DSCP_MASK;
}
if (ofport->tnl_port) {
+ /* Save tunnel metadata so that changes made due to
+ * the Logical (tunnel) Port are not visible for any further
+ * matches, while explicit set actions on tunnel metadata are.
+ */
+ struct flow_tnl flow_tnl = ctx->flow.tunnel;
odp_port = tnl_port_send(ofport->tnl_port, &ctx->flow);
if (odp_port == OVSP_NONE) {
xlate_report(ctx, "Tunneling decided against output");
- return;
+ goto out; /* restore flow_nw_tos */
+ }
+ if (ctx->flow.tunnel.ip_dst == ctx->orig_tunnel_ip_dst) {
+ xlate_report(ctx, "Not tunneling to our own address");
+ goto out; /* restore flow_nw_tos */
}
-
if (ctx->resubmit_stats) {
netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
}
out_port = odp_port;
commit_odp_tunnel_action(&ctx->flow, &ctx->base_flow,
ctx->odp_actions);
+ ctx->flow.tunnel = flow_tnl; /* Restore tunnel metadata */
} else {
odp_port = ofport->odp_port;
out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
ctx->sflow_odp_port = odp_port;
ctx->sflow_n_outputs++;
ctx->nf_output_iface = ofp_port;
- ctx->flow.tunnel.tun_id = flow_tun_id;
+
+ /* Restore flow */
ctx->flow.vlan_tci = flow_vlan_tci;
+ ctx->flow.skb_mark = flow_skb_mark;
+ out:
ctx->flow.nw_tos = flow_nw_tos;
}
}
}
-struct xlate_reg_state {
- ovs_be16 vlan_tci;
- ovs_be64 tun_id;
-};
-
static bool
slave_enabled_cb(uint16_t ofp_port, void *ofproto_)
{
}
}
+static void
+xlate_sample_action(struct action_xlate_ctx *ctx,
+ const struct ofpact_sample *os)
+{
+ union user_action_cookie cookie;
+ /* Scale the probability from 16-bit to 32-bit while representing
+ * the same percentage. */
+ uint32_t probability = (os->probability << 16) | os->probability;
+
+ commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
+
+ compose_flow_sample_cookie(os->probability, os->collector_set_id,
+ os->obs_domain_id, os->obs_point_id, &cookie);
+ compose_sample_action(ctx->ofproto, ctx->odp_actions, &ctx->flow,
+ probability, &cookie, sizeof cookie.flow_sample);
+}
+
static bool
may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
{
tunnel_ecn_ok(struct action_xlate_ctx *ctx)
{
if (is_ip_any(&ctx->base_flow)
- && (ctx->base_flow.tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
+ && (ctx->flow.tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
if ((ctx->base_flow.nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE"
" but is not ECN capable");
}
break;
}
+
+ case OFPACT_SAMPLE:
+ xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
+ break;
}
}
struct rule_dpif *rule,
uint8_t tcp_flags, const struct ofpbuf *packet)
{
- ovs_be64 initial_tun_id = flow->tunnel.tun_id;
-
/* Flow initialization rules:
* - 'base_flow' must match the kernel's view of the packet at the
* time that action processing starts. 'flow' represents any
* to another device without any modifications this will cause us to
* insert a new tag since the original one was stripped off by the
* VLAN device.
- * - Tunnel 'flow' is largely cleared when transitioning between
- * the input and output stages since it does not make sense to output
- * a packet with the exact headers that it was received with (i.e.
- * the destination IP is us). The one exception is the tun_id, which
- * is preserved to allow use in later resubmit lookups and loads into
- * registers.
+ * - Tunnel metadata as received is retained in 'flow'. This allows
+ * tunnel metadata matching also in later tables.
+ * Since a kernel action for setting the tunnel metadata will only be
+ * generated with actual tunnel output, changing the tunnel metadata
+ * values in 'flow' (such as tun_id) will only have effect with a later
+ * tunnel output action.
* - Tunnel 'base_flow' is completely cleared since that is what the
* kernel does. If we wish to maintain the original values an action
* needs to be generated. */
ctx->ofproto = ofproto;
ctx->flow = *flow;
- memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
ctx->base_flow = ctx->flow;
- ctx->base_flow.vlan_tci = initial_vals->vlan_tci;
- ctx->base_flow.tunnel.ip_tos = initial_vals->tunnel_ip_tos;
- ctx->flow.tunnel.tun_id = initial_tun_id;
+ memset(&ctx->base_flow.tunnel, 0, sizeof ctx->base_flow.tunnel);
+ ctx->orig_tunnel_ip_dst = flow->tunnel.ip_dst;
ctx->rule = rule;
ctx->packet = packet;
ctx->may_learn = packet != NULL;
ctx->resubmit_hook = NULL;
ctx->report_hook = NULL;
ctx->resubmit_stats = NULL;
+
+ if (initial_vals) {
+ ctx->base_flow.vlan_tci = initial_vals->vlan_tci;
+ }
}
/* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
} else {
static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
struct initial_vals initial_vals;
+ size_t sample_actions_len;
uint32_t local_odp_port;
initial_vals.vlan_tci = ctx->base_flow.vlan_tci;
- initial_vals.tunnel_ip_tos = ctx->base_flow.tunnel.ip_tos;
add_sflow_action(ctx);
+ add_ipfix_action(ctx);
+ sample_actions_len = ctx->odp_actions->size;
if (tunnel_ecn_ok(ctx) && (!in_port || may_receive(in_port, ctx))) {
do_xlate_actions(ofpacts, ofpacts_len, ctx);
/* We've let OFPP_NORMAL and the learning action look at the
* packet, so drop it now if forwarding is disabled. */
if (in_port && !stp_forward_in_state(in_port->stp_state)) {
- ofpbuf_clear(ctx->odp_actions);
- add_sflow_action(ctx);
+ ctx->odp_actions->size = sample_actions_len;
}
}
}
local_odp_port = ofp_port_to_odp_port(ctx->ofproto, OFPP_LOCAL);
- if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
- local_odp_port,
- ctx->odp_actions->data,
- ctx->odp_actions->size)) {
- ctx->slow |= SLOW_IN_BAND;
- if (ctx->packet
- && connmgr_msg_in_hook(ctx->ofproto->up.connmgr, &ctx->flow,
- ctx->packet)) {
- compose_output_action(ctx, OFPP_LOCAL);
- }
+ if (!connmgr_must_output_local(ctx->ofproto->up.connmgr, &ctx->flow,
+ local_odp_port,
+ ctx->odp_actions->data,
+ ctx->odp_actions->size)) {
+ compose_output_action(ctx, OFPP_LOCAL);
}
if (ctx->ofproto->has_mirrors) {
add_mirror_actions(ctx, &orig_flow);
dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
initial_vals.vlan_tci = flow->vlan_tci;
- initial_vals.tunnel_ip_tos = 0;
action_xlate_ctx_init(&ctx, ofproto, flow, &initial_vals, NULL,
packet_get_tcp_flags(packet, flow), packet);
ctx.resubmit_stats = &stats;
ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
void *aux OVS_UNUSED)
{
- const char *dpname = argv[1];
+ const struct dpif_backer *backer;
struct ofproto_dpif *ofproto;
struct ofpbuf odp_key;
struct ofpbuf *packet;
char *s;
packet = NULL;
- ofpbuf_init(&odp_key, 0);
+ backer = NULL;
ds_init(&result);
+ ofpbuf_init(&odp_key, 0);
- ofproto = ofproto_dpif_lookup(dpname);
- if (!ofproto) {
- unixctl_command_reply_error(conn, "Unknown ofproto (use ofproto/list "
- "for help)");
- goto exit;
+ /* Handle "-generate" or a hex string as the last argument. */
+ if (!strcmp(argv[argc - 1], "-generate")) {
+ packet = ofpbuf_new(0);
+ argc--;
+ } else {
+ const char *error = eth_from_hex(argv[argc - 1], &packet);
+ if (!error) {
+ argc--;
+ } else if (argc == 4) {
+ /* The 3-argument form must end in "-generate' or a hex string. */
+ unixctl_command_reply_error(conn, error);
+ goto exit;
+ }
}
- if (argc == 3 || (argc == 4 && !strcmp(argv[3], "-generate"))) {
- /* ofproto/trace dpname flow [-generate] */
- const char *flow_s = argv[2];
- const char *generate_s = argv[3];
- /* Allow 'flow_s' to be either a datapath flow or an OpenFlow-like
- * flow. We guess which type it is based on whether 'flow_s' contains
- * an '(', since a datapath flow always contains '(') but an
- * OpenFlow-like flow should not (in fact it's allowed but I believe
- * that's not documented anywhere).
- *
- * An alternative would be to try to parse 'flow_s' both ways, but then
- * it would be tricky giving a sensible error message. After all, do
- * you just say "syntax error" or do you present both error messages?
- * Both choices seem lousy. */
- if (strchr(flow_s, '(')) {
- int error;
-
- /* Convert string to datapath key. */
- ofpbuf_init(&odp_key, 0);
- error = odp_flow_key_from_string(flow_s, NULL, &odp_key);
- if (error) {
- unixctl_command_reply_error(conn, "Bad flow syntax");
- goto exit;
+ /* Parse the flow and determine whether a datapath or
+ * bridge is specified. If function odp_flow_key_from_string()
+ * returns 0, the flow is a odp_flow. If function
+ * parse_ofp_exact_flow() returns 0, the flow is a br_flow. */
+ if (!odp_flow_key_from_string(argv[argc - 1], NULL, &odp_key)) {
+ /* If the odp_flow is the second argument,
+ * the datapath name is the first argument. */
+ if (argc == 3) {
+ const char *dp_type;
+ if (!strncmp(argv[1], "ovs-", 4)) {
+ dp_type = argv[1] + 4;
+ } else {
+ dp_type = argv[1];
}
-
- /* The user might have specified the wrong ofproto but within the
- * same backer. That's OK, ofproto_receive() can find the right
- * one for us. */
- if (ofproto_receive(ofproto->backer, NULL, odp_key.data,
- odp_key.size, &flow, NULL, &ofproto, NULL,
- &initial_vals)) {
- unixctl_command_reply_error(conn, "Invalid flow");
+ backer = shash_find_data(&all_dpif_backers, dp_type);
+ if (!backer) {
+ unixctl_command_reply_error(conn, "Cannot find datapath "
+ "of this name");
goto exit;
}
- ds_put_format(&result, "Bridge: %s\n", ofproto->up.name);
} else {
- char *error_s;
-
- error_s = parse_ofp_exact_flow(&flow, argv[2]);
- if (error_s) {
- unixctl_command_reply_error(conn, error_s);
- free(error_s);
+ /* No datapath name specified, so there should be only one
+ * datapath. */
+ struct shash_node *node;
+ if (shash_count(&all_dpif_backers) != 1) {
+ unixctl_command_reply_error(conn, "Must specify datapath "
+ "name, there is more than one type of datapath");
goto exit;
}
-
- initial_vals.vlan_tci = flow.vlan_tci;
- initial_vals.tunnel_ip_tos = flow.tunnel.ip_tos;
+ node = shash_first(&all_dpif_backers);
+ backer = node->data;
}
- /* Generate a packet, if requested. */
- if (generate_s) {
- packet = ofpbuf_new(0);
- flow_compose(packet, &flow);
+ /* Extract the ofproto_dpif object from the ofproto_receive()
+ * function. */
+ if (ofproto_receive(backer, NULL, odp_key.data,
+ odp_key.size, &flow, NULL, &ofproto, NULL,
+ &initial_vals)) {
+ unixctl_command_reply_error(conn, "Invalid datapath flow");
+ goto exit;
}
- } else if (argc == 7) {
- /* ofproto/trace dpname priority tun_id in_port mark packet */
- const char *priority_s = argv[2];
- const char *tun_id_s = argv[3];
- const char *in_port_s = argv[4];
- const char *mark_s = argv[5];
- const char *packet_s = argv[6];
- uint32_t in_port = atoi(in_port_s);
- ovs_be64 tun_id = htonll(strtoull(tun_id_s, NULL, 0));
- uint32_t priority = atoi(priority_s);
- uint32_t mark = atoi(mark_s);
- const char *msg;
-
- msg = eth_from_hex(packet_s, &packet);
- if (msg) {
- unixctl_command_reply_error(conn, msg);
+ ds_put_format(&result, "Bridge: %s\n", ofproto->up.name);
+ } else if (!parse_ofp_exact_flow(&flow, argv[argc - 1])) {
+ if (argc != 3) {
+ unixctl_command_reply_error(conn, "Must specify bridge name");
goto exit;
}
- ds_put_cstr(&result, "Packet: ");
- s = ofp_packet_to_string(packet->data, packet->size);
- ds_put_cstr(&result, s);
- free(s);
-
- flow_extract(packet, priority, mark, NULL, in_port, &flow);
- flow.tunnel.tun_id = tun_id;
+ ofproto = ofproto_dpif_lookup(argv[1]);
+ if (!ofproto) {
+ unixctl_command_reply_error(conn, "Unknown bridge name");
+ goto exit;
+ }
initial_vals.vlan_tci = flow.vlan_tci;
- initial_vals.tunnel_ip_tos = flow.tunnel.ip_tos;
} else {
- unixctl_command_reply_error(conn, "Bad command syntax");
+ unixctl_command_reply_error(conn, "Bad flow syntax");
goto exit;
}
+ /* Generate a packet, if requested. */
+ if (packet) {
+ if (!packet->size) {
+ flow_compose(packet, &flow);
+ } else {
+ ds_put_cstr(&result, "Packet: ");
+ s = ofp_packet_to_string(packet->data, packet->size);
+ ds_put_cstr(&result, s);
+ free(s);
+
+ /* Use the metadata from the flow and the packet argument
+ * to reconstruct the flow. */
+ flow_extract(packet, flow.skb_priority, flow.skb_mark, NULL,
+ flow.in_port, &flow);
+ initial_vals.vlan_tci = flow.vlan_tci;
+ }
+ }
+
ofproto_trace(ofproto, &flow, packet, &initial_vals, &result);
unixctl_command_reply(conn, ds_cstr(&result));
case SLOW_STP:
ds_put_cstr(ds, "\n\t- Consists of STP packets.");
break;
- case SLOW_IN_BAND:
- ds_put_cstr(ds, "\n\t- Needs in-band special case "
- "processing.");
- if (!packet) {
- ds_put_cstr(ds, "\n\t (The datapath actions are "
- "incomplete--for complete actions, "
- "please supply a packet.)");
- }
+ case SLOW_BFD:
+ ds_put_cstr(ds, "\n\t- Consists of BFD packets.");
break;
case SLOW_CONTROLLER:
ds_put_cstr(ds, "\n\t- Sends \"packet-in\" messages "
"to the OpenFlow controller.");
break;
- case SLOW_MATCH:
- ds_put_cstr(ds, "\n\t- Needs more specific matching "
- "than the datapath supports.");
- break;
}
slow &= ~bit;
}
-
- if (slow & ~SLOW_MATCH) {
- ds_put_cstr(ds, "\nThe datapath actions above do not reflect "
- "the special slow-path processing.");
- }
}
}
}
unixctl_command_register(
"ofproto/trace",
- "bridge {priority tun_id in_port mark packet | odp_flow [-generate]}",
- 2, 6, ofproto_unixctl_trace, NULL);
+ "[dp_name]|bridge odp_flow|br_flow [-generate|packet]",
+ 1, 3, ofproto_unixctl_trace, NULL);
unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
ofproto_unixctl_fdb_flush, NULL);
unixctl_command_register("fdb/show", "bridge", 1, 1,
set_netflow,
get_netflow_ids,
set_sflow,
+ set_ipfix,
set_cfm,
get_cfm_status,
+ set_bfd,
+ get_bfd_status,
set_stp,
get_stp_status,
set_stp_port,