/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "odp-util.h"
#include "ofp-util.h"
#include "ofpbuf.h"
+#include "ofp-parse.h"
#include "ofp-print.h"
#include "ofproto-dpif-governor.h"
#include "ofproto-dpif-sflow.h"
uint32_t realdev, ovs_be16 vlan_tci);
static uint16_t vsp_vlandev_to_realdev(const struct ofproto_dpif *,
uint16_t vlandev, int *vid);
+static bool vsp_adjust_flow(const struct ofproto_dpif *, struct flow *);
static void vsp_remove(struct ofport_dpif *);
static void vsp_add(struct ofport_dpif *, uint16_t realdev_ofp_port, int vid);
/* Utilities. */
static int send_packet(const struct ofport_dpif *, struct ofpbuf *packet);
-static size_t
-compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions,
- const struct flow *, uint32_t odp_port);
+static size_t compose_sflow_action(const struct ofproto_dpif *,
+ struct ofpbuf *odp_actions,
+ const struct flow *, uint32_t odp_port);
static void add_mirror_actions(struct action_xlate_ctx *ctx,
const struct flow *flow);
/* Global variables. */
struct ofpbuf *packet)
{
enum odp_key_fitness fitness;
- uint16_t realdev;
- int vid;
fitness = odp_flow_key_to_flow(key, key_len, flow);
if (fitness == ODP_FIT_ERROR) {
}
*initial_tci = flow->vlan_tci;
- realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid);
- if (realdev) {
- /* Cause the flow to be processed as if it came in on the real device
- * with the VLAN device's VLAN ID. */
- flow->in_port = realdev;
- flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+ if (vsp_adjust_flow(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
return;
}
- if (cookie.type == USER_ACTION_COOKIE_SFLOW) {
+ switch (cookie.type) {
+ case USER_ACTION_COOKIE_SFLOW:
if (ofproto->sflow) {
dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
&cookie);
}
- } else {
+ break;
+
+ case USER_ACTION_COOKIE_UNSPEC:
+ default:
VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata);
+ break;
}
}
return odp_put_userspace_action(pid, cookie, odp_actions);
}
+static void
+compose_sflow_cookie(const struct ofproto_dpif *ofproto,
+ ovs_be16 vlan_tci, uint32_t odp_port,
+ unsigned int n_outputs, struct user_action_cookie *cookie)
+{
+ int ifindex;
+
+ cookie->type = USER_ACTION_COOKIE_SFLOW;
+ cookie->vlan_tci = vlan_tci;
+
+ /* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output
+ * port information") for the interpretation of cookie->output. */
+ switch (n_outputs) {
+ case 0:
+ /* 0x40000000 | 256 means "packet dropped for unknown reason". */
+ cookie->output = 0x40000000 | 256;
+ break;
+
+ case 1:
+ ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
+ if (ifindex) {
+ cookie->output = ifindex;
+ break;
+ }
+ /* Fall through. */
+ default:
+ /* 0x80000000 means "multiple output ports. */
+ cookie->output = 0x80000000 | n_outputs;
+ break;
+ }
+}
+
/* Compose SAMPLE action for sFlow. */
static size_t
compose_sflow_action(const struct ofproto_dpif *ofproto,
const struct flow *flow,
uint32_t odp_port)
{
- uint32_t port_ifindex;
uint32_t probability;
struct user_action_cookie cookie;
size_t sample_offset, actions_offset;
- int cookie_offset, n_output;
+ int cookie_offset;
if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
return 0;
}
- if (odp_port == OVSP_NONE) {
- port_ifindex = 0;
- n_output = 0;
- } else {
- port_ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
- n_output = 1;
- }
-
sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
/* Number of packets out of UINT_MAX to 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.type = USER_ACTION_COOKIE_SFLOW;
- cookie.data = port_ifindex;
- cookie.n_output = n_output;
- cookie.vlan_tci = 0;
+ 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);
}
cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset,
- sizeof(*cookie));
- assert(cookie != NULL);
+ sizeof(*cookie));
assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
- if (ctx->sflow_n_outputs) {
- cookie->data = dpif_sflow_odp_port_to_ifindex(ctx->ofproto->sflow,
- ctx->sflow_odp_port);
- }
- if (ctx->sflow_n_outputs >= 255) {
- cookie->n_output = 255;
- } else {
- cookie->n_output = ctx->sflow_n_outputs;
- }
- cookie->vlan_tci = base->vlan_tci;
+ compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
+ ctx->sflow_odp_port, ctx->sflow_n_outputs, cookie);
}
static void
/* ofproto/trace dpname flow [-generate] */
const char *flow_s = argv[2];
const char *generate_s = argv[3];
- 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;
- }
+ /* 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;
+ }
- /* Convert odp_key to flow. */
- error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
- odp_key.size, &flow,
- &initial_tci, NULL);
- if (error == ODP_FIT_ERROR) {
- unixctl_command_reply_error(conn, "Invalid flow");
- goto exit;
+ /* Convert odp_key to flow. */
+ error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
+ odp_key.size, &flow,
+ &initial_tci, NULL);
+ if (error == ODP_FIT_ERROR) {
+ unixctl_command_reply_error(conn, "Invalid flow");
+ goto exit;
+ }
+ } 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);
+ goto exit;
+ }
+
+ initial_tci = flow.vlan_tci;
+ vsp_adjust_flow(ofproto, &flow);
}
/* Generate a packet, if requested. */
return 0;
}
+/* Given 'flow', a flow representing a packet received on 'ofproto', checks
+ * whether 'flow->in_port' represents a Linux VLAN device. If so, changes
+ * 'flow->in_port' to the "real" device backing the VLAN device, sets
+ * 'flow->vlan_tci' to the VLAN VID, and returns true. Otherwise (which is
+ * always the case unless VLAN splinters are enabled), returns false without
+ * making any changes. */
+static bool
+vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow)
+{
+ uint16_t realdev;
+ int vid;
+
+ realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid);
+ if (!realdev) {
+ return false;
+ }
+
+ /* Cause the flow to be processed as if it came in on the real device with
+ * the VLAN device's VLAN ID. */
+ flow->in_port = realdev;
+ flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+ return true;
+}
+
static void
vsp_remove(struct ofport_dpif *port)
{