2010-01-04 13:08:37 -08:00
|
|
|
/*
|
2014-02-06 16:04:05 -08:00
|
|
|
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
|
2010-10-08 16:36:13 -07:00
|
|
|
* Copyright (c) 2009 InMon Corp.
|
2010-01-04 13:08:37 -08:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
2011-06-15 11:28:51 -07:00
|
|
|
#include "ofproto-dpif-sflow.h"
|
2010-01-04 13:08:37 -08:00
|
|
|
#include <inttypes.h>
|
2012-07-25 22:51:05 +02:00
|
|
|
#include <sys/socket.h>
|
2011-12-06 13:01:25 -08:00
|
|
|
#include <net/if.h>
|
2010-01-04 13:08:37 -08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include "collectors.h"
|
|
|
|
#include "compiler.h"
|
2011-06-15 11:28:51 -07:00
|
|
|
#include "dpif.h"
|
2010-07-19 11:43:05 -07:00
|
|
|
#include "hash.h"
|
|
|
|
#include "hmap.h"
|
2010-01-04 13:08:37 -08:00
|
|
|
#include "netdev.h"
|
2010-12-10 10:40:58 -08:00
|
|
|
#include "netlink.h"
|
2010-01-04 13:08:37 -08:00
|
|
|
#include "ofpbuf.h"
|
|
|
|
#include "ofproto.h"
|
2010-10-08 16:26:21 -07:00
|
|
|
#include "packets.h"
|
2010-01-04 13:08:37 -08:00
|
|
|
#include "poll-loop.h"
|
2014-10-16 11:38:12 -07:00
|
|
|
#include "ovs-router.h"
|
2011-12-06 13:01:25 -08:00
|
|
|
#include "route-table.h"
|
2010-01-04 13:08:37 -08:00
|
|
|
#include "sflow_api.h"
|
|
|
|
#include "socket-util.h"
|
|
|
|
#include "timeval.h"
|
|
|
|
#include "vlog.h"
|
2011-09-19 14:55:31 -07:00
|
|
|
#include "lib/odp-util.h"
|
2011-12-08 13:26:05 -08:00
|
|
|
#include "ofproto-provider.h"
|
2014-06-27 11:19:59 -07:00
|
|
|
#include "lacp.h"
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2010-10-19 14:47:01 -07:00
|
|
|
VLOG_DEFINE_THIS_MODULE(sflow);
|
2010-07-16 11:02:49 -07:00
|
|
|
|
2013-07-22 12:32:19 -07:00
|
|
|
static struct ovs_mutex mutex;
|
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow_port {
|
|
|
|
struct hmap_node hmap_node; /* In struct dpif_sflow's "ports" hmap. */
|
2010-01-04 13:08:37 -08:00
|
|
|
SFLDataSource_instance dsi; /* sFlow library's notion of port number. */
|
2011-12-08 13:26:05 -08:00
|
|
|
struct ofport *ofport; /* To retrive port stats. */
|
2013-06-19 16:58:44 -07:00
|
|
|
odp_port_t odp_port;
|
2010-01-04 13:08:37 -08:00
|
|
|
};
|
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow {
|
2010-01-04 13:08:37 -08:00
|
|
|
struct collectors *collectors;
|
|
|
|
SFLAgent *sflow_agent;
|
|
|
|
struct ofproto_sflow_options *options;
|
|
|
|
time_t next_tick;
|
|
|
|
size_t n_flood, n_all;
|
2011-06-15 11:28:51 -07:00
|
|
|
struct hmap ports; /* Contains "struct dpif_sflow_port"s. */
|
2011-09-28 10:43:07 -07:00
|
|
|
uint32_t probability;
|
2013-12-27 19:39:24 -08:00
|
|
|
struct ovs_refcount ref_cnt;
|
2010-01-04 13:08:37 -08:00
|
|
|
};
|
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
static void dpif_sflow_del_port__(struct dpif_sflow *,
|
|
|
|
struct dpif_sflow_port *);
|
2010-07-19 11:43:05 -07:00
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
#define RECEIVER_INDEX 1
|
|
|
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
2010-01-11 11:07:59 -08:00
|
|
|
static bool
|
|
|
|
nullable_string_is_equal(const char *a, const char *b)
|
|
|
|
{
|
|
|
|
return a ? b && !strcmp(a, b) : !b;
|
|
|
|
}
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
static bool
|
|
|
|
ofproto_sflow_options_equal(const struct ofproto_sflow_options *a,
|
2011-06-15 11:28:51 -07:00
|
|
|
const struct ofproto_sflow_options *b)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2011-03-25 15:04:12 -07:00
|
|
|
return (sset_equals(&a->targets, &b->targets)
|
2010-01-04 13:08:37 -08:00
|
|
|
&& a->sampling_rate == b->sampling_rate
|
|
|
|
&& a->polling_interval == b->polling_interval
|
|
|
|
&& a->header_len == b->header_len
|
|
|
|
&& a->sub_id == b->sub_id
|
2010-01-11 11:07:59 -08:00
|
|
|
&& nullable_string_is_equal(a->agent_device, b->agent_device)
|
|
|
|
&& nullable_string_is_equal(a->control_ip, b->control_ip));
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct ofproto_sflow_options *
|
|
|
|
ofproto_sflow_options_clone(const struct ofproto_sflow_options *old)
|
|
|
|
{
|
|
|
|
struct ofproto_sflow_options *new = xmemdup(old, sizeof *old);
|
2011-03-25 15:04:12 -07:00
|
|
|
sset_clone(&new->targets, &old->targets);
|
2010-01-04 13:08:37 -08:00
|
|
|
new->agent_device = old->agent_device ? xstrdup(old->agent_device) : NULL;
|
|
|
|
new->control_ip = old->control_ip ? xstrdup(old->control_ip) : NULL;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ofproto_sflow_options_destroy(struct ofproto_sflow_options *options)
|
|
|
|
{
|
|
|
|
if (options) {
|
2011-03-25 15:04:12 -07:00
|
|
|
sset_destroy(&options->targets);
|
2010-01-04 13:08:37 -08:00
|
|
|
free(options->agent_device);
|
|
|
|
free(options->control_ip);
|
|
|
|
free(options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sFlow library callback to allocate memory. */
|
|
|
|
static void *
|
2010-02-11 10:59:47 -08:00
|
|
|
sflow_agent_alloc_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED,
|
|
|
|
size_t bytes)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
|
|
|
return calloc(1, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sFlow library callback to free memory. */
|
|
|
|
static int
|
2010-02-11 10:59:47 -08:00
|
|
|
sflow_agent_free_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED,
|
|
|
|
void *obj)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
|
|
|
free(obj);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sFlow library callback to report error. */
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
sflow_agent_error_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED,
|
|
|
|
char *msg)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
|
|
|
VLOG_WARN("sFlow agent error: %s", msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sFlow library callback to send datagram. */
|
|
|
|
static void
|
2011-06-15 11:28:51 -07:00
|
|
|
sflow_agent_send_packet_cb(void *ds_, SFLAgent *agent OVS_UNUSED,
|
2010-02-11 10:59:47 -08:00
|
|
|
SFLReceiver *receiver OVS_UNUSED, u_char *pkt,
|
2010-01-04 13:08:37 -08:00
|
|
|
uint32_t pktLen)
|
|
|
|
{
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow *ds = ds_;
|
|
|
|
collectors_send(ds->collectors, pkt, pktLen);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
static struct dpif_sflow_port *
|
2013-06-19 16:58:44 -07:00
|
|
|
dpif_sflow_find_port(const struct dpif_sflow *ds, odp_port_t odp_port)
|
2013-08-08 15:14:20 -07:00
|
|
|
OVS_REQUIRES(mutex)
|
2010-07-19 11:43:05 -07:00
|
|
|
{
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow_port *dsp;
|
2010-07-19 11:43:05 -07:00
|
|
|
|
2013-06-22 10:33:27 -07:00
|
|
|
HMAP_FOR_EACH_IN_BUCKET (dsp, hmap_node, hash_odp_port(odp_port),
|
|
|
|
&ds->ports) {
|
2012-09-28 17:56:07 -07:00
|
|
|
if (dsp->odp_port == odp_port) {
|
2011-06-15 11:28:51 -07:00
|
|
|
return dsp;
|
2010-07-19 11:43:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
static void
|
2011-06-15 11:28:51 -07:00
|
|
|
sflow_agent_get_counters(void *ds_, SFLPoller *poller,
|
2010-01-04 13:08:37 -08:00
|
|
|
SFL_COUNTERS_SAMPLE_TYPE *cs)
|
2013-08-08 15:14:20 -07:00
|
|
|
OVS_REQUIRES(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow *ds = ds_;
|
2014-06-27 11:19:59 -07:00
|
|
|
SFLCounters_sample_element elem, lacp_elem, of_elem, name_elem;
|
2012-02-15 14:23:38 -08:00
|
|
|
enum netdev_features current;
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow_port *dsp;
|
2010-01-04 13:08:37 -08:00
|
|
|
SFLIf_counters *counters;
|
|
|
|
struct netdev_stats stats;
|
|
|
|
enum netdev_flags flags;
|
2014-06-27 11:19:59 -07:00
|
|
|
struct lacp_slave_stats lacp_stats;
|
|
|
|
const char *ifName;
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2013-06-19 16:58:44 -07:00
|
|
|
dsp = dpif_sflow_find_port(ds, u32_to_odp(poller->bridgePort));
|
2011-06-15 11:28:51 -07:00
|
|
|
if (!dsp) {
|
2010-01-04 13:08:37 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
elem.tag = SFLCOUNTERS_GENERIC;
|
|
|
|
counters = &elem.counterBlock.generic;
|
|
|
|
counters->ifIndex = SFL_DS_INDEX(poller->dsi);
|
|
|
|
counters->ifType = 6;
|
2011-12-08 13:26:05 -08:00
|
|
|
if (!netdev_get_features(dsp->ofport->netdev, ¤t, NULL, NULL, NULL)) {
|
2011-06-15 11:28:51 -07:00
|
|
|
/* The values of ifDirection come from MAU MIB (RFC 2668): 0 = unknown,
|
|
|
|
1 = full-duplex, 2 = half-duplex, 3 = in, 4=out */
|
2012-11-03 18:00:39 -07:00
|
|
|
counters->ifSpeed = netdev_features_to_bps(current, 0);
|
2010-01-04 13:08:37 -08:00
|
|
|
counters->ifDirection = (netdev_features_is_full_duplex(current)
|
|
|
|
? 1 : 2);
|
|
|
|
} else {
|
|
|
|
counters->ifSpeed = 100000000;
|
2010-01-11 11:08:29 -08:00
|
|
|
counters->ifDirection = 0;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2011-12-08 13:26:05 -08:00
|
|
|
if (!netdev_get_flags(dsp->ofport->netdev, &flags) && flags & NETDEV_UP) {
|
2010-01-04 13:08:37 -08:00
|
|
|
counters->ifStatus = 1; /* ifAdminStatus up. */
|
2011-12-08 13:26:05 -08:00
|
|
|
if (netdev_get_carrier(dsp->ofport->netdev)) {
|
2010-01-04 13:08:37 -08:00
|
|
|
counters->ifStatus |= 2; /* ifOperStatus us. */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
counters->ifStatus = 0; /* Down. */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX
|
|
|
|
1. Is the multicast counter filled in?
|
|
|
|
2. Does the multicast counter include broadcasts?
|
|
|
|
3. Does the rx_packets counter include multicasts/broadcasts?
|
|
|
|
*/
|
2011-12-08 13:26:05 -08:00
|
|
|
ofproto_port_get_stats(dsp->ofport, &stats);
|
2010-01-04 13:08:37 -08:00
|
|
|
counters->ifInOctets = stats.rx_bytes;
|
|
|
|
counters->ifInUcastPkts = stats.rx_packets;
|
|
|
|
counters->ifInMulticastPkts = stats.multicast;
|
|
|
|
counters->ifInBroadcastPkts = -1;
|
|
|
|
counters->ifInDiscards = stats.rx_dropped;
|
|
|
|
counters->ifInErrors = stats.rx_errors;
|
|
|
|
counters->ifInUnknownProtos = -1;
|
|
|
|
counters->ifOutOctets = stats.tx_bytes;
|
|
|
|
counters->ifOutUcastPkts = stats.tx_packets;
|
|
|
|
counters->ifOutMulticastPkts = -1;
|
|
|
|
counters->ifOutBroadcastPkts = -1;
|
|
|
|
counters->ifOutDiscards = stats.tx_dropped;
|
|
|
|
counters->ifOutErrors = stats.tx_errors;
|
|
|
|
counters->ifPromiscuousMode = 0;
|
|
|
|
|
|
|
|
SFLADD_ELEMENT(cs, &elem);
|
2014-06-27 11:19:59 -07:00
|
|
|
|
|
|
|
/* Include LACP counters and identifiers if this port is part of a LAG. */
|
|
|
|
if (ofproto_port_get_lacp_stats(dsp->ofport, &lacp_stats) == 0) {
|
|
|
|
memset(&lacp_elem, 0, sizeof lacp_elem);
|
|
|
|
lacp_elem.tag = SFLCOUNTERS_LACP;
|
|
|
|
memcpy(&lacp_elem.counterBlock.lacp.actorSystemID,
|
|
|
|
lacp_stats.dot3adAggPortActorSystemID,
|
|
|
|
ETH_ADDR_LEN);
|
|
|
|
memcpy(&lacp_elem.counterBlock.lacp.partnerSystemID,
|
|
|
|
lacp_stats.dot3adAggPortPartnerOperSystemID,
|
|
|
|
ETH_ADDR_LEN);
|
|
|
|
lacp_elem.counterBlock.lacp.attachedAggID =
|
|
|
|
lacp_stats.dot3adAggPortAttachedAggID;
|
|
|
|
lacp_elem.counterBlock.lacp.portState.v.actorAdmin =
|
|
|
|
lacp_stats.dot3adAggPortActorAdminState;
|
|
|
|
lacp_elem.counterBlock.lacp.portState.v.actorOper =
|
|
|
|
lacp_stats.dot3adAggPortActorOperState;
|
|
|
|
lacp_elem.counterBlock.lacp.portState.v.partnerAdmin =
|
|
|
|
lacp_stats.dot3adAggPortPartnerAdminState;
|
|
|
|
lacp_elem.counterBlock.lacp.portState.v.partnerOper =
|
|
|
|
lacp_stats.dot3adAggPortPartnerOperState;
|
|
|
|
lacp_elem.counterBlock.lacp.LACPDUsRx =
|
|
|
|
lacp_stats.dot3adAggPortStatsLACPDUsRx;
|
|
|
|
SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerPDUsRx);
|
|
|
|
SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerResponsePDUsRx);
|
|
|
|
SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.unknownRx);
|
|
|
|
lacp_elem.counterBlock.lacp.illegalRx =
|
|
|
|
lacp_stats.dot3adAggPortStatsIllegalRx;
|
|
|
|
lacp_elem.counterBlock.lacp.LACPDUsTx =
|
|
|
|
lacp_stats.dot3adAggPortStatsLACPDUsTx;
|
|
|
|
SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerPDUsTx);
|
|
|
|
SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerResponsePDUsTx);
|
|
|
|
SFLADD_ELEMENT(cs, &lacp_elem);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Include Port name. */
|
|
|
|
if ((ifName = netdev_get_name(dsp->ofport->netdev)) != NULL) {
|
|
|
|
memset(&name_elem, 0, sizeof name_elem);
|
|
|
|
name_elem.tag = SFLCOUNTERS_PORTNAME;
|
|
|
|
name_elem.counterBlock.portName.portName.str = (char *)ifName;
|
|
|
|
name_elem.counterBlock.portName.portName.len = strlen(ifName);
|
|
|
|
SFLADD_ELEMENT(cs, &name_elem);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Include OpenFlow DPID and openflow port number. */
|
|
|
|
memset(&of_elem, 0, sizeof of_elem);
|
|
|
|
of_elem.tag = SFLCOUNTERS_OPENFLOWPORT;
|
|
|
|
of_elem.counterBlock.ofPort.datapath_id =
|
|
|
|
ofproto_get_datapath_id(dsp->ofport->ofproto);
|
|
|
|
of_elem.counterBlock.ofPort.port_no =
|
|
|
|
(OVS_FORCE uint32_t)dsp->ofport->ofp_port;
|
|
|
|
SFLADD_ELEMENT(cs, &of_elem);
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
sfl_poller_writeCountersSample(poller, cs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Obtains an address to use for the local sFlow agent and stores it into
|
|
|
|
* '*agent_addr'. Returns true if successful, false on failure.
|
|
|
|
*
|
|
|
|
* The sFlow agent address should be a local IP address that is persistent and
|
|
|
|
* reachable over the network, if possible. The IP address associated with
|
|
|
|
* 'agent_device' is used if it has one, and otherwise 'control_ip', the IP
|
2011-12-06 13:01:25 -08:00
|
|
|
* address used to talk to the controller. If the agent device is not
|
|
|
|
* specified then it is figured out by taking a look at the routing table based
|
|
|
|
* on 'targets'. */
|
2010-01-04 13:08:37 -08:00
|
|
|
static bool
|
2011-12-06 13:01:25 -08:00
|
|
|
sflow_choose_agent_address(const char *agent_device,
|
|
|
|
const struct sset *targets,
|
|
|
|
const char *control_ip,
|
2010-01-04 13:08:37 -08:00
|
|
|
SFLAddress *agent_addr)
|
|
|
|
{
|
2011-12-06 13:01:25 -08:00
|
|
|
const char *target;
|
2010-01-04 13:08:37 -08:00
|
|
|
struct in_addr in4;
|
|
|
|
|
|
|
|
memset(agent_addr, 0, sizeof *agent_addr);
|
|
|
|
agent_addr->type = SFLADDRESSTYPE_IP_V4;
|
|
|
|
|
|
|
|
if (agent_device) {
|
2011-12-06 13:01:25 -08:00
|
|
|
if (!netdev_get_in4_by_name(agent_device, &in4)) {
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SSET_FOR_EACH (target, targets) {
|
2014-08-05 13:51:19 -07:00
|
|
|
union {
|
|
|
|
struct sockaddr_storage ss;
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
} sa;
|
2011-12-06 13:01:25 -08:00
|
|
|
char name[IFNAMSIZ];
|
|
|
|
|
2014-08-05 13:51:19 -07:00
|
|
|
if (inet_parse_active(target, SFL_DEFAULT_COLLECTOR_PORT, &sa.ss)
|
|
|
|
&& sa.ss.ss_family == AF_INET) {
|
2014-10-16 11:38:12 -07:00
|
|
|
ovs_be32 gw;
|
|
|
|
|
|
|
|
if (ovs_router_lookup(sa.sin.sin_addr.s_addr, name, &gw)
|
2014-02-06 16:04:05 -08:00
|
|
|
&& !netdev_get_in4_by_name(name, &in4)) {
|
|
|
|
goto success;
|
|
|
|
}
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (control_ip && !lookup_ip(control_ip, &in4)) {
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
|
|
|
|
VLOG_ERR("could not determine IP address for sFlow agent");
|
|
|
|
return false;
|
|
|
|
|
|
|
|
success:
|
2011-05-06 12:59:51 -07:00
|
|
|
agent_addr->address.ip_v4.addr = (OVS_FORCE uint32_t) in4.s_addr;
|
2010-01-04 13:08:37 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-22 12:32:19 -07:00
|
|
|
static void
|
2013-08-08 15:14:19 -07:00
|
|
|
dpif_sflow_clear__(struct dpif_sflow *ds) OVS_REQUIRES(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2011-06-15 11:28:51 -07:00
|
|
|
if (ds->sflow_agent) {
|
|
|
|
sfl_agent_release(ds->sflow_agent);
|
2013-08-20 11:27:32 -07:00
|
|
|
free(ds->sflow_agent);
|
2011-06-15 11:28:51 -07:00
|
|
|
ds->sflow_agent = NULL;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2011-06-15 11:28:51 -07:00
|
|
|
collectors_destroy(ds->collectors);
|
|
|
|
ds->collectors = NULL;
|
|
|
|
ofproto_sflow_options_destroy(ds->options);
|
|
|
|
ds->options = NULL;
|
2010-01-04 13:08:37 -08:00
|
|
|
|
|
|
|
/* Turn off sampling to save CPU cycles. */
|
2011-09-28 10:43:07 -07:00
|
|
|
ds->probability = 0;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
2013-07-22 12:32:19 -07:00
|
|
|
void
|
|
|
|
dpif_sflow_clear(struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
|
|
|
|
{
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
dpif_sflow_clear__(ds);
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
}
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
bool
|
2013-07-22 12:32:19 -07:00
|
|
|
dpif_sflow_is_enabled(const struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2013-07-22 12:32:19 -07:00
|
|
|
bool enabled;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
enabled = ds->collectors != NULL;
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
return enabled;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow *
|
2012-11-01 16:16:16 -07:00
|
|
|
dpif_sflow_create(void)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2013-07-22 12:32:19 -07:00
|
|
|
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow *ds;
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2013-07-22 12:32:19 -07:00
|
|
|
if (ovsthread_once_start(&once)) {
|
Use "error-checking" mutexes in place of other kinds wherever possible.
We've seen a number of deadlocks in the tree since thread safety was
introduced. So far, all of these are self-deadlocks, that is, a single
thread acquiring a lock and then attempting to re-acquire the same lock
recursively. When this has happened, the process simply hung, and it was
somewhat difficult to find the cause.
POSIX "error-checking" mutexes check for this specific problem (and
others). This commit switches from other types of mutexes to
error-checking mutexes everywhere that we can, that is, everywhere that
we're not using recursive mutexes. This ought to help find problems more
quickly in the future.
There might be performance advantages to other kinds of mutexes in some
cases. However, the existing mutex type choices were just guesses, so I'd
rather go for easy detection of errors until we know that other mutex
types actually perform better in specific cases. Also, I did a quick
microbenchmark of glibc mutex types on my host and found that the
error checking mutexes weren't any slower than the other types, at least
when the mutex is uncontended.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2013-08-20 13:40:02 -07:00
|
|
|
ovs_mutex_init_recursive(&mutex);
|
2013-07-22 12:32:19 -07:00
|
|
|
ovsthread_once_done(&once);
|
|
|
|
}
|
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
ds = xcalloc(1, sizeof *ds);
|
|
|
|
ds->next_tick = time_now() + 1;
|
|
|
|
hmap_init(&ds->ports);
|
2011-09-28 10:43:07 -07:00
|
|
|
ds->probability = 0;
|
2013-12-27 19:39:24 -08:00
|
|
|
ovs_refcount_init(&ds->ref_cnt);
|
2011-12-06 13:01:25 -08:00
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
return ds;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
2013-06-18 19:33:51 -07:00
|
|
|
struct dpif_sflow *
|
|
|
|
dpif_sflow_ref(const struct dpif_sflow *ds_)
|
|
|
|
{
|
|
|
|
struct dpif_sflow *ds = CONST_CAST(struct dpif_sflow *, ds_);
|
|
|
|
if (ds) {
|
2013-12-27 19:39:24 -08:00
|
|
|
ovs_refcount_ref(&ds->ref_cnt);
|
2013-06-18 19:33:51 -07:00
|
|
|
}
|
|
|
|
return ds;
|
|
|
|
}
|
|
|
|
|
2011-09-28 10:43:07 -07:00
|
|
|
/* 32-bit fraction of packets to sample with. A value of 0 samples no packets,
|
|
|
|
* a value of %UINT32_MAX samples all packets and intermediate values sample
|
|
|
|
* intermediate fractions of packets. */
|
|
|
|
uint32_t
|
2013-07-22 12:32:19 -07:00
|
|
|
dpif_sflow_get_probability(const struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
|
2011-09-28 10:43:07 -07:00
|
|
|
{
|
2013-07-22 12:32:19 -07:00
|
|
|
uint32_t probability;
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
probability = ds->probability;
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
return probability;
|
2011-09-28 10:43:07 -07:00
|
|
|
}
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
void
|
2013-07-22 12:32:19 -07:00
|
|
|
dpif_sflow_unref(struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2014-07-07 13:18:46 -07:00
|
|
|
if (ds && ovs_refcount_unref_relaxed(&ds->ref_cnt) == 1) {
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow_port *dsp, *next;
|
2010-05-07 09:29:02 -07:00
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
dpif_sflow_clear(ds);
|
|
|
|
HMAP_FOR_EACH_SAFE (dsp, next, hmap_node, &ds->ports) {
|
|
|
|
dpif_sflow_del_port__(ds, dsp);
|
2010-05-07 09:29:02 -07:00
|
|
|
}
|
2011-06-15 11:28:51 -07:00
|
|
|
hmap_destroy(&ds->ports);
|
|
|
|
free(ds);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2011-12-08 13:26:05 -08:00
|
|
|
dpif_sflow_add_poller(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
|
2013-08-08 15:14:19 -07:00
|
|
|
OVS_REQUIRES(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2011-06-15 11:28:51 -07:00
|
|
|
SFLPoller *poller = sfl_agent_addPoller(ds->sflow_agent, &dsp->dsi, ds,
|
2010-01-04 13:08:37 -08:00
|
|
|
sflow_agent_get_counters);
|
2011-06-15 11:28:51 -07:00
|
|
|
sfl_poller_set_sFlowCpInterval(poller, ds->options->polling_interval);
|
2010-01-04 13:08:37 -08:00
|
|
|
sfl_poller_set_sFlowCpReceiver(poller, RECEIVER_INDEX);
|
2013-06-19 16:58:44 -07:00
|
|
|
sfl_poller_set_bridgePort(poller, odp_to_u32(dsp->odp_port));
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-09-28 17:56:07 -07:00
|
|
|
dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport,
|
2013-07-22 12:32:19 -07:00
|
|
|
odp_port_t odp_port) OVS_EXCLUDED(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow_port *dsp;
|
2013-04-30 22:38:53 -07:00
|
|
|
int ifindex;
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2013-07-22 12:32:19 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2011-06-15 11:28:51 -07:00
|
|
|
dpif_sflow_del_port(ds, odp_port);
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2011-12-08 13:26:05 -08:00
|
|
|
ifindex = netdev_get_ifindex(ofport->netdev);
|
2013-04-30 22:38:53 -07:00
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
if (ifindex <= 0) {
|
2013-04-30 22:38:53 -07:00
|
|
|
/* Not an ifindex port, so do not add a cross-reference to it here */
|
2013-07-22 12:32:19 -07:00
|
|
|
goto out;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2013-04-30 22:38:53 -07:00
|
|
|
|
|
|
|
/* Add to table of ports. */
|
|
|
|
dsp = xmalloc(sizeof *dsp);
|
2011-12-08 13:26:05 -08:00
|
|
|
dsp->ofport = ofport;
|
2012-09-28 17:56:07 -07:00
|
|
|
dsp->odp_port = odp_port;
|
2013-04-30 22:38:53 -07:00
|
|
|
SFL_DS_SET(dsp->dsi, SFL_DSCLASS_IFINDEX, ifindex, 0);
|
2013-06-22 10:33:27 -07:00
|
|
|
hmap_insert(&ds->ports, &dsp->hmap_node, hash_odp_port(odp_port));
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2013-04-30 22:38:53 -07:00
|
|
|
/* Add poller. */
|
2011-06-15 11:28:51 -07:00
|
|
|
if (ds->sflow_agent) {
|
2011-12-08 13:26:05 -08:00
|
|
|
dpif_sflow_add_poller(ds, dsp);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2013-07-22 12:32:19 -07:00
|
|
|
|
|
|
|
out:
|
|
|
|
ovs_mutex_unlock(&mutex);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
2010-07-19 11:43:05 -07:00
|
|
|
static void
|
2011-06-15 11:28:51 -07:00
|
|
|
dpif_sflow_del_port__(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
|
2013-08-08 15:14:19 -07:00
|
|
|
OVS_REQUIRES(mutex)
|
2010-07-19 11:43:05 -07:00
|
|
|
{
|
2011-06-15 11:28:51 -07:00
|
|
|
if (ds->sflow_agent) {
|
|
|
|
sfl_agent_removePoller(ds->sflow_agent, &dsp->dsi);
|
|
|
|
sfl_agent_removeSampler(ds->sflow_agent, &dsp->dsi);
|
2010-07-19 11:43:05 -07:00
|
|
|
}
|
2011-06-15 11:28:51 -07:00
|
|
|
hmap_remove(&ds->ports, &dsp->hmap_node);
|
|
|
|
free(dsp);
|
2010-07-19 11:43:05 -07:00
|
|
|
}
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
void
|
2013-06-19 16:58:44 -07:00
|
|
|
dpif_sflow_del_port(struct dpif_sflow *ds, odp_port_t odp_port)
|
2013-07-22 12:32:19 -07:00
|
|
|
OVS_EXCLUDED(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2013-07-22 12:32:19 -07:00
|
|
|
struct dpif_sflow_port *dsp;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
dsp = dpif_sflow_find_port(ds, odp_port);
|
2011-06-15 11:28:51 -07:00
|
|
|
if (dsp) {
|
|
|
|
dpif_sflow_del_port__(ds, dsp);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2013-07-22 12:32:19 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-06-15 11:28:51 -07:00
|
|
|
dpif_sflow_set_options(struct dpif_sflow *ds,
|
|
|
|
const struct ofproto_sflow_options *options)
|
2013-07-22 12:32:19 -07:00
|
|
|
OVS_EXCLUDED(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2011-06-15 11:28:51 -07:00
|
|
|
struct dpif_sflow_port *dsp;
|
2010-01-04 13:08:37 -08:00
|
|
|
bool options_changed;
|
|
|
|
SFLReceiver *receiver;
|
|
|
|
SFLAddress agentIP;
|
|
|
|
time_t now;
|
2013-04-30 22:38:53 -07:00
|
|
|
SFLDataSource_instance dsi;
|
|
|
|
uint32_t dsIndex;
|
|
|
|
SFLSampler *sampler;
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2013-07-22 12:32:19 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2011-03-25 15:04:12 -07:00
|
|
|
if (sset_is_empty(&options->targets) || !options->sampling_rate) {
|
2010-01-11 11:09:14 -08:00
|
|
|
/* No point in doing any work if there are no targets or nothing to
|
|
|
|
* sample. */
|
2013-07-22 12:32:19 -07:00
|
|
|
dpif_sflow_clear__(ds);
|
|
|
|
goto out;
|
2010-01-11 11:09:14 -08:00
|
|
|
}
|
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
options_changed = (!ds->options
|
|
|
|
|| !ofproto_sflow_options_equal(options, ds->options));
|
2010-01-04 13:08:37 -08:00
|
|
|
|
|
|
|
/* Configure collectors if options have changed or if we're shortchanged in
|
|
|
|
* collectors (which indicates that opening one or more of the configured
|
|
|
|
* collectors failed, so that we should retry). */
|
|
|
|
if (options_changed
|
2011-06-15 11:28:51 -07:00
|
|
|
|| collectors_count(ds->collectors) < sset_count(&options->targets)) {
|
|
|
|
collectors_destroy(ds->collectors);
|
2010-02-10 11:04:30 -08:00
|
|
|
collectors_create(&options->targets, SFL_DEFAULT_COLLECTOR_PORT,
|
2011-06-15 11:28:51 -07:00
|
|
|
&ds->collectors);
|
|
|
|
if (ds->collectors == NULL) {
|
2010-01-11 11:09:14 -08:00
|
|
|
VLOG_WARN_RL(&rl, "no collectors could be initialized, "
|
|
|
|
"sFlow disabled");
|
2013-07-22 12:32:19 -07:00
|
|
|
dpif_sflow_clear__(ds);
|
|
|
|
goto out;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-06 13:01:25 -08:00
|
|
|
/* Choose agent IP address and agent device (if not yet setup) */
|
|
|
|
if (!sflow_choose_agent_address(options->agent_device,
|
|
|
|
&options->targets,
|
|
|
|
options->control_ip, &agentIP)) {
|
2013-07-22 12:32:19 -07:00
|
|
|
dpif_sflow_clear__(ds);
|
|
|
|
goto out;
|
2011-12-06 13:01:25 -08:00
|
|
|
}
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
/* Avoid reconfiguring if options didn't change. */
|
|
|
|
if (!options_changed) {
|
2013-07-22 12:32:19 -07:00
|
|
|
goto out;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2011-06-15 11:28:51 -07:00
|
|
|
ofproto_sflow_options_destroy(ds->options);
|
|
|
|
ds->options = ofproto_sflow_options_clone(options);
|
2010-01-04 13:08:37 -08:00
|
|
|
|
|
|
|
/* Create agent. */
|
|
|
|
VLOG_INFO("creating sFlow agent %d", options->sub_id);
|
2011-06-15 11:28:51 -07:00
|
|
|
if (ds->sflow_agent) {
|
|
|
|
sfl_agent_release(ds->sflow_agent);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2011-06-15 11:28:51 -07:00
|
|
|
ds->sflow_agent = xcalloc(1, sizeof *ds->sflow_agent);
|
2010-06-08 17:18:48 -07:00
|
|
|
now = time_wall();
|
2011-06-15 11:28:51 -07:00
|
|
|
sfl_agent_init(ds->sflow_agent,
|
2010-01-04 13:08:37 -08:00
|
|
|
&agentIP,
|
|
|
|
options->sub_id,
|
|
|
|
now, /* Boot time. */
|
|
|
|
now, /* Current time. */
|
2011-06-15 11:28:51 -07:00
|
|
|
ds, /* Pointer supplied to callbacks. */
|
2010-01-04 13:08:37 -08:00
|
|
|
sflow_agent_alloc_cb,
|
|
|
|
sflow_agent_free_cb,
|
|
|
|
sflow_agent_error_cb,
|
|
|
|
sflow_agent_send_packet_cb);
|
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
receiver = sfl_agent_addReceiver(ds->sflow_agent);
|
2010-01-11 11:10:01 -08:00
|
|
|
sfl_receiver_set_sFlowRcvrOwner(receiver, "Open vSwitch sFlow");
|
2010-01-04 13:08:37 -08:00
|
|
|
sfl_receiver_set_sFlowRcvrTimeout(receiver, 0xffffffff);
|
|
|
|
|
|
|
|
/* Set the sampling_rate down in the datapath. */
|
2011-09-28 10:43:07 -07:00
|
|
|
ds->probability = MAX(1, UINT32_MAX / ds->options->sampling_rate);
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2013-04-30 22:38:53 -07:00
|
|
|
/* Add a single sampler for the bridge. This appears as a PHYSICAL_ENTITY
|
|
|
|
because it is associated with the hypervisor, and interacts with the server
|
|
|
|
hardware directly. The sub_id is used to distinguish this sampler from
|
|
|
|
others on other bridges within the same agent. */
|
|
|
|
dsIndex = 1000 + options->sub_id;
|
|
|
|
SFL_DS_SET(dsi, SFL_DSCLASS_PHYSICAL_ENTITY, dsIndex, 0);
|
|
|
|
sampler = sfl_agent_addSampler(ds->sflow_agent, &dsi);
|
|
|
|
sfl_sampler_set_sFlowFsPacketSamplingRate(sampler, ds->options->sampling_rate);
|
|
|
|
sfl_sampler_set_sFlowFsMaximumHeaderSize(sampler, ds->options->header_len);
|
|
|
|
sfl_sampler_set_sFlowFsReceiver(sampler, RECEIVER_INDEX);
|
|
|
|
|
|
|
|
/* Add pollers for the currently known ifindex-ports */
|
2011-06-15 11:28:51 -07:00
|
|
|
HMAP_FOR_EACH (dsp, hmap_node, &ds->ports) {
|
2011-12-08 13:26:05 -08:00
|
|
|
dpif_sflow_add_poller(ds, dsp);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2013-07-22 12:32:19 -07:00
|
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
ovs_mutex_unlock(&mutex);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
2011-09-28 10:43:07 -07:00
|
|
|
int
|
2011-06-15 11:28:51 -07:00
|
|
|
dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds,
|
2013-07-22 12:32:19 -07:00
|
|
|
odp_port_t odp_port) OVS_EXCLUDED(mutex)
|
2010-01-06 10:24:52 -08:00
|
|
|
{
|
2013-07-22 12:32:19 -07:00
|
|
|
struct dpif_sflow_port *dsp;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
dsp = dpif_sflow_find_port(ds, odp_port);
|
|
|
|
ret = dsp ? SFL_DS_INDEX(dsp->dsi) : 0;
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
return ret;
|
2010-01-06 10:24:52 -08:00
|
|
|
}
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
void
|
2014-08-06 18:49:44 -07:00
|
|
|
dpif_sflow_received(struct dpif_sflow *ds, const struct ofpbuf *packet,
|
2013-06-19 16:58:44 -07:00
|
|
|
const struct flow *flow, odp_port_t odp_in_port,
|
2012-05-04 14:56:40 -07:00
|
|
|
const union user_action_cookie *cookie)
|
2013-07-22 12:32:19 -07:00
|
|
|
OVS_EXCLUDED(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
|
|
|
SFL_FLOW_SAMPLE_TYPE fs;
|
|
|
|
SFLFlow_sample_element hdrElem;
|
|
|
|
SFLSampled_header *header;
|
|
|
|
SFLFlow_sample_element switchElem;
|
sflow: Fix sFlow sampling structure.
According to Neil McKee, in an email archived at
http://openvswitch.org/pipermail/dev_openvswitch.org/2010-January/000934.html:
The containment rule is that a given sflow-datasource (sampler or
poller) should be scoped within only one sflow-agent (or
sub-agent). So the issue arrises when you have two
switches/datapaths defined on the same host being managed with
the same IP address: each switch is a separate sub-agent, so they
can run independently (e.g. with their own sequence numbers) but
they can't both claim to speak for the same sflow-datasource.
Specifically, they can't both represent the <ifindex>:0
data-source. This containment rule is necessary so that the
sFlow collector can scale and combine the results accurately.
One option would be to stick with the <ifindex>:0 data-source but
elevate it to be global across all bridges, with a global
sample_pool and a global sflow_agent. Not tempting. Better to
go the other way and allow each interface to have it's own
sampler, just as it already has it's own poller. The ifIndex
numbers are globally unique across all switches/datapaths on the
host, so the containment is now clean. Datasource <ifindex>:5
might be on one switch, whille <ifindex>:7 can be on another.
Other benefits are that 1) you can support the option of
overriding the default sampling-rate on an interface-by-interface
basis, and 2) this is how most sFlow implementations are coded,
so there will be no surprises or interoperability issues with any
sFlow collectors out there.
This commit implements the approach suggested by Neil.
This commit uses an atomic_t to represent the sampling pool. This is
because we do want access to it to be atomic, but we expect that it will
"mostly" be accessed from a single CPU at a time. Perhaps this is a bad
assumption; we can always switch to another form of synchronization later.
CC: Neil McKee <neil.mckee@inmon.com>
2010-01-20 13:52:42 -08:00
|
|
|
SFLSampler *sampler;
|
2011-09-28 10:43:07 -07:00
|
|
|
struct dpif_sflow_port *in_dsp;
|
2012-05-04 14:56:40 -07:00
|
|
|
ovs_be16 vlan_tci;
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2013-07-22 12:32:19 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2013-04-30 22:38:53 -07:00
|
|
|
sampler = ds->sflow_agent->samplers;
|
|
|
|
if (!sampler) {
|
2013-07-22 12:32:19 -07:00
|
|
|
goto out;
|
2011-09-28 10:43:07 -07:00
|
|
|
}
|
|
|
|
|
2013-04-30 22:38:53 -07:00
|
|
|
/* Build a flow sample. */
|
|
|
|
memset(&fs, 0, sizeof fs);
|
2010-01-04 13:08:37 -08:00
|
|
|
|
2013-04-30 22:38:53 -07:00
|
|
|
/* Look up the input ifIndex if this port has one. Otherwise just
|
|
|
|
* leave it as 0 (meaning 'unknown') and continue. */
|
|
|
|
in_dsp = dpif_sflow_find_port(ds, odp_in_port);
|
|
|
|
if (in_dsp) {
|
|
|
|
fs.input = SFL_DS_INDEX(in_dsp->dsi);
|
sflow: Fix sFlow sampling structure.
According to Neil McKee, in an email archived at
http://openvswitch.org/pipermail/dev_openvswitch.org/2010-January/000934.html:
The containment rule is that a given sflow-datasource (sampler or
poller) should be scoped within only one sflow-agent (or
sub-agent). So the issue arrises when you have two
switches/datapaths defined on the same host being managed with
the same IP address: each switch is a separate sub-agent, so they
can run independently (e.g. with their own sequence numbers) but
they can't both claim to speak for the same sflow-datasource.
Specifically, they can't both represent the <ifindex>:0
data-source. This containment rule is necessary so that the
sFlow collector can scale and combine the results accurately.
One option would be to stick with the <ifindex>:0 data-source but
elevate it to be global across all bridges, with a global
sample_pool and a global sflow_agent. Not tempting. Better to
go the other way and allow each interface to have it's own
sampler, just as it already has it's own poller. The ifIndex
numbers are globally unique across all switches/datapaths on the
host, so the containment is now clean. Datasource <ifindex>:5
might be on one switch, whille <ifindex>:7 can be on another.
Other benefits are that 1) you can support the option of
overriding the default sampling-rate on an interface-by-interface
basis, and 2) this is how most sFlow implementations are coded,
so there will be no surprises or interoperability issues with any
sFlow collectors out there.
This commit implements the approach suggested by Neil.
This commit uses an atomic_t to represent the sampling pool. This is
because we do want access to it to be atomic, but we expect that it will
"mostly" be accessed from a single CPU at a time. Perhaps this is a bad
assumption; we can always switch to another form of synchronization later.
CC: Neil McKee <neil.mckee@inmon.com>
2010-01-20 13:52:42 -08:00
|
|
|
}
|
|
|
|
|
2013-04-30 22:38:53 -07:00
|
|
|
/* Make the assumption that the random number generator in the datapath converges
|
|
|
|
* to the configured mean, and just increment the samplePool by the configured
|
|
|
|
* sampling rate every time. */
|
|
|
|
sampler->samplePool += sfl_sampler_get_sFlowFsPacketSamplingRate(sampler);
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
/* Sampled header. */
|
|
|
|
memset(&hdrElem, 0, sizeof hdrElem);
|
|
|
|
hdrElem.tag = SFLFLOW_HEADER;
|
|
|
|
header = &hdrElem.flowType.header;
|
|
|
|
header->header_protocol = SFLHEADER_ETHERNET_ISO8023;
|
2010-05-05 13:24:03 -07:00
|
|
|
/* The frame_length should include the Ethernet FCS (4 bytes),
|
2013-04-30 22:38:53 -07:00
|
|
|
* but it has already been stripped, so we need to add 4 here. */
|
2014-03-30 01:31:50 -07:00
|
|
|
header->frame_length = ofpbuf_size(packet) + 4;
|
2010-05-05 13:24:03 -07:00
|
|
|
/* Ethernet FCS stripped off. */
|
|
|
|
header->stripped = 4;
|
2014-03-30 01:31:50 -07:00
|
|
|
header->header_length = MIN(ofpbuf_size(packet),
|
datapath: Report kernel's flow key when passing packets up to userspace.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to change
the kernel's idea of the flow key separately from the userspace version.
This commit takes one step in that direction by making the kernel report
its idea of the flow that a packet belongs to whenever it passes a packet
up to userspace. This means that userspace can intelligently figure out
what to do:
- If userspace's notion of the flow for the packet matches the kernel's,
then nothing special is necessary.
- If the kernel has a more specific notion for the flow than userspace,
for example if the kernel decoded IPv6 headers but userspace stopped
at the Ethernet type (because it does not understand IPv6), then again
nothing special is necessary: userspace can still set up the flow in
the usual way.
- If userspace has a more specific notion for the flow than the kernel,
for example if userspace decoded an IPv6 header but the kernel
stopped at the Ethernet type, then userspace can forward the packet
manually, without setting up a flow in the kernel. (This case is
bad from a performance point of view, but at least it is correct.)
This commit does not actually make userspace flexible enough to handle
changes in the kernel flow key structure, although userspace does now
have enough information to do that intelligently. This will have to wait
for later commits.
This commit is bigger than it would otherwise be because it is rolled
together with changing "struct odp_msg" to a sequence of Netlink
attributes. The alternative, to do each of those changes in a separate
patch, seemed like overkill because it meant that either we would have to
introduce and then kill off Netlink attributes for in_port and tun_id, if
Netlink conversion went first, or shove yet another variable-length header
into the stuff already after odp_msg, if adding the flow key to odp_msg
went first.
This commit will slow down performance of checksumming packets sent up to
userspace. I'm not entirely pleased with how I did it. I considered a
couple of alternatives, but none of them seemed that much better.
Suggestions welcome. Not changing anything wasn't an option,
unfortunately. At any rate some slowdown will become unavoidable when OVS
actually starts using Netlink instead of just Netlink framing.
(Actually, I thought of one option where we could avoid that: make
userspace do the checksum instead, by passing csum_start and csum_offset as
part of what goes to userspace. But that's not perfect either.)
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
2011-01-24 14:59:57 -08:00
|
|
|
sampler->sFlowFsMaximumHeaderSize);
|
2014-03-30 01:31:50 -07:00
|
|
|
header->header_bytes = ofpbuf_data(packet);
|
2010-01-04 13:08:37 -08:00
|
|
|
|
|
|
|
/* Add extended switch element. */
|
|
|
|
memset(&switchElem, 0, sizeof(switchElem));
|
|
|
|
switchElem.tag = SFLFLOW_EX_SWITCH;
|
datapath: Report kernel's flow key when passing packets up to userspace.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to change
the kernel's idea of the flow key separately from the userspace version.
This commit takes one step in that direction by making the kernel report
its idea of the flow that a packet belongs to whenever it passes a packet
up to userspace. This means that userspace can intelligently figure out
what to do:
- If userspace's notion of the flow for the packet matches the kernel's,
then nothing special is necessary.
- If the kernel has a more specific notion for the flow than userspace,
for example if the kernel decoded IPv6 headers but userspace stopped
at the Ethernet type (because it does not understand IPv6), then again
nothing special is necessary: userspace can still set up the flow in
the usual way.
- If userspace has a more specific notion for the flow than the kernel,
for example if userspace decoded an IPv6 header but the kernel
stopped at the Ethernet type, then userspace can forward the packet
manually, without setting up a flow in the kernel. (This case is
bad from a performance point of view, but at least it is correct.)
This commit does not actually make userspace flexible enough to handle
changes in the kernel flow key structure, although userspace does now
have enough information to do that intelligently. This will have to wait
for later commits.
This commit is bigger than it would otherwise be because it is rolled
together with changing "struct odp_msg" to a sequence of Netlink
attributes. The alternative, to do each of those changes in a separate
patch, seemed like overkill because it meant that either we would have to
introduce and then kill off Netlink attributes for in_port and tun_id, if
Netlink conversion went first, or shove yet another variable-length header
into the stuff already after odp_msg, if adding the flow key to odp_msg
went first.
This commit will slow down performance of checksumming packets sent up to
userspace. I'm not entirely pleased with how I did it. I considered a
couple of alternatives, but none of them seemed that much better.
Suggestions welcome. Not changing anything wasn't an option,
unfortunately. At any rate some slowdown will become unavoidable when OVS
actually starts using Netlink instead of just Netlink framing.
(Actually, I thought of one option where we could avoid that: make
userspace do the checksum instead, by passing csum_start and csum_offset as
part of what goes to userspace. But that's not perfect either.)
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
2011-01-24 14:59:57 -08:00
|
|
|
switchElem.flowType.sw.src_vlan = vlan_tci_to_vid(flow->vlan_tci);
|
|
|
|
switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow->vlan_tci);
|
2011-09-28 10:43:07 -07:00
|
|
|
|
|
|
|
/* Retrieve data from user_action_cookie. */
|
2012-05-04 14:56:40 -07:00
|
|
|
vlan_tci = cookie->sflow.vlan_tci;
|
|
|
|
switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(vlan_tci);
|
|
|
|
switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(vlan_tci);
|
2010-01-11 11:09:43 -08:00
|
|
|
|
2012-05-04 14:56:40 -07:00
|
|
|
fs.output = cookie->sflow.output;
|
2010-01-04 13:08:37 -08:00
|
|
|
|
|
|
|
/* Submit the flow sample to be encoded into the next datagram. */
|
|
|
|
SFLADD_ELEMENT(&fs, &hdrElem);
|
|
|
|
SFLADD_ELEMENT(&fs, &switchElem);
|
|
|
|
sfl_sampler_writeFlowSample(sampler, &fs);
|
2013-07-22 12:32:19 -07:00
|
|
|
|
|
|
|
out:
|
|
|
|
ovs_mutex_unlock(&mutex);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-07-22 12:32:19 -07:00
|
|
|
dpif_sflow_run(struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2013-07-22 12:32:19 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
if (ds->collectors != NULL) {
|
2010-01-04 13:08:37 -08:00
|
|
|
time_t now = time_now();
|
2011-12-06 13:01:25 -08:00
|
|
|
route_table_run();
|
2011-06-15 11:28:51 -07:00
|
|
|
if (now >= ds->next_tick) {
|
|
|
|
sfl_agent_tick(ds->sflow_agent, time_wall());
|
|
|
|
ds->next_tick = now + 1;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
}
|
2013-07-22 12:32:19 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-07-22 12:32:19 -07:00
|
|
|
dpif_sflow_wait(struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
|
2010-01-04 13:08:37 -08:00
|
|
|
{
|
2013-07-22 12:32:19 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
if (ds->collectors != NULL) {
|
2011-06-15 11:28:51 -07:00
|
|
|
poll_timer_wait_until(ds->next_tick * 1000LL);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2013-07-22 12:32:19 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|