2010-01-04 13:08:37 -08:00
|
|
|
/*
|
2014-12-16 14:42:05 -08:00
|
|
|
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 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>
|
2014-12-16 14:42:05 -08:00
|
|
|
#include <sys/resource.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"
|
2014-12-15 14:10:38 +01:00
|
|
|
#include "openvswitch/vlog.h"
|
2011-09-19 14:55:31 -07:00
|
|
|
#include "lib/odp-util.h"
|
2015-07-17 21:37:02 -07:00
|
|
|
#include "lib/unaligned.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;
|
|
|
|
|
2014-12-16 14:42:05 -08:00
|
|
|
/* This global var is used to determine which sFlow
|
|
|
|
sub-agent should send the datapath counters. */
|
|
|
|
#define SFLOW_GC_SUBID_UNCLAIMED (uint32_t)-1
|
|
|
|
static uint32_t sflow_global_counters_subid = SFLOW_GC_SUBID_UNCLAIMED;
|
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
/*
|
|
|
|
* The enum dpif_sflow_tunnel_type is to declare the types supported
|
|
|
|
*/
|
|
|
|
enum dpif_sflow_tunnel_type {
|
|
|
|
DPIF_SFLOW_TUNNEL_UNKNOWN = 0,
|
|
|
|
DPIF_SFLOW_TUNNEL_VXLAN,
|
|
|
|
DPIF_SFLOW_TUNNEL_GRE,
|
|
|
|
DPIF_SFLOW_TUNNEL_LISP,
|
|
|
|
DPIF_SFLOW_TUNNEL_IPSEC_GRE,
|
|
|
|
DPIF_SFLOW_TUNNEL_GENEVE
|
|
|
|
};
|
|
|
|
|
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;
|
2015-07-17 21:37:02 -07:00
|
|
|
enum dpif_sflow_tunnel_type tunnel_type;
|
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;
|
|
|
|
}
|
|
|
|
|
2014-12-16 14:42:05 -08:00
|
|
|
/* Call to get the datapath stats. Modeled after the dpctl utility.
|
|
|
|
*
|
|
|
|
* It might be more efficient for this module to be given a handle it can use
|
|
|
|
* to get these stats more efficiently, but this is only going to be called
|
|
|
|
* once every 20-30 seconds. Return number of datapaths found (normally expect
|
|
|
|
* 1). */
|
|
|
|
static int
|
|
|
|
sflow_get_dp_stats(struct dpif_sflow *ds OVS_UNUSED,
|
|
|
|
struct dpif_dp_stats *dp_totals)
|
|
|
|
{
|
|
|
|
struct sset types;
|
|
|
|
const char *type;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
memset(dp_totals, 0, sizeof *dp_totals);
|
|
|
|
sset_init(&types);
|
|
|
|
dp_enumerate_types(&types);
|
|
|
|
SSET_FOR_EACH (type, &types) {
|
|
|
|
struct sset names;
|
|
|
|
const char *name;
|
|
|
|
sset_init(&names);
|
|
|
|
if (dp_enumerate_names(type, &names) == 0) {
|
|
|
|
SSET_FOR_EACH (name, &names) {
|
|
|
|
struct dpif *dpif;
|
|
|
|
if (dpif_open(name, type, &dpif) == 0) {
|
|
|
|
struct dpif_dp_stats dp_stats;
|
|
|
|
if (dpif_get_dp_stats(dpif, &dp_stats) == 0) {
|
|
|
|
count++;
|
|
|
|
dp_totals->n_hit += dp_stats.n_hit;
|
|
|
|
dp_totals->n_missed += dp_stats.n_missed;
|
|
|
|
dp_totals->n_lost += dp_stats.n_lost;
|
|
|
|
dp_totals->n_flows += dp_stats.n_flows;
|
|
|
|
dp_totals->n_mask_hit += dp_stats.n_mask_hit;
|
|
|
|
dp_totals->n_masks += dp_stats.n_masks;
|
|
|
|
}
|
|
|
|
dpif_close(dpif);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sset_destroy(&names);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sset_destroy(&types);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there are multiple bridges defined then we need some
|
|
|
|
minimal artibration to decide which one should send the
|
|
|
|
global counters. This function allows each sub-agent to
|
|
|
|
ask if he should do it or not. */
|
|
|
|
static bool
|
|
|
|
sflow_global_counters_subid_test(uint32_t subid)
|
|
|
|
OVS_REQUIRES(mutex)
|
|
|
|
{
|
|
|
|
if (sflow_global_counters_subid == SFLOW_GC_SUBID_UNCLAIMED) {
|
|
|
|
/* The role is up for grabs. */
|
|
|
|
sflow_global_counters_subid = subid;
|
|
|
|
}
|
|
|
|
return (sflow_global_counters_subid == subid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sflow_global_counters_subid_clear(uint32_t subid)
|
|
|
|
OVS_REQUIRES(mutex)
|
|
|
|
{
|
|
|
|
if (sflow_global_counters_subid == subid) {
|
|
|
|
/* The sub-agent that was sending global counters
|
|
|
|
is going away, so reset to allow another
|
|
|
|
to take over. */
|
|
|
|
sflow_global_counters_subid = SFLOW_GC_SUBID_UNCLAIMED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sflow_agent_get_global_counters(void *ds_, SFLPoller *poller,
|
|
|
|
SFL_COUNTERS_SAMPLE_TYPE *cs)
|
|
|
|
OVS_REQUIRES(mutex)
|
|
|
|
{
|
|
|
|
struct dpif_sflow *ds = ds_;
|
|
|
|
SFLCounters_sample_element dp_elem, res_elem;
|
|
|
|
struct dpif_dp_stats dp_totals;
|
|
|
|
struct rusage usage;
|
|
|
|
|
|
|
|
if (!sflow_global_counters_subid_test(poller->agent->subId)) {
|
|
|
|
/* Another sub-agent is currently responsible for this. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* datapath stats */
|
|
|
|
if (sflow_get_dp_stats(ds, &dp_totals)) {
|
|
|
|
dp_elem.tag = SFLCOUNTERS_OVSDP;
|
|
|
|
dp_elem.counterBlock.ovsdp.n_hit = dp_totals.n_hit;
|
|
|
|
dp_elem.counterBlock.ovsdp.n_missed = dp_totals.n_missed;
|
|
|
|
dp_elem.counterBlock.ovsdp.n_lost = dp_totals.n_lost;
|
|
|
|
dp_elem.counterBlock.ovsdp.n_mask_hit = dp_totals.n_mask_hit;
|
|
|
|
dp_elem.counterBlock.ovsdp.n_flows = dp_totals.n_flows;
|
|
|
|
dp_elem.counterBlock.ovsdp.n_masks = dp_totals.n_masks;
|
|
|
|
SFLADD_ELEMENT(cs, &dp_elem);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* resource usage */
|
|
|
|
getrusage(RUSAGE_SELF, &usage);
|
|
|
|
res_elem.tag = SFLCOUNTERS_APP_RESOURCES;
|
|
|
|
res_elem.counterBlock.appResources.user_time
|
|
|
|
= timeval_to_msec(&usage.ru_utime);
|
|
|
|
res_elem.counterBlock.appResources.system_time
|
|
|
|
= timeval_to_msec(&usage.ru_stime);
|
|
|
|
res_elem.counterBlock.appResources.mem_used = (usage.ru_maxrss * 1024);
|
|
|
|
SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.mem_max);
|
|
|
|
SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.fd_open);
|
|
|
|
SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.fd_max);
|
|
|
|
SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.conn_open);
|
|
|
|
SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.conn_max);
|
|
|
|
|
|
|
|
SFLADD_ELEMENT(cs, &res_elem);
|
|
|
|
sfl_poller_writeCountersSample(poller, cs);
|
|
|
|
}
|
|
|
|
|
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;
|
2015-08-28 14:55:11 -07:00
|
|
|
lacp_elem.counterBlock.lacp.actorSystemID =
|
|
|
|
lacp_stats.dot3adAggPortActorSystemID;
|
|
|
|
lacp_elem.counterBlock.lacp.partnerSystemID =
|
|
|
|
lacp_stats.dot3adAggPortPartnerOperSystemID;
|
2014-06-27 11:19:59 -07:00
|
|
|
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) {
|
2014-12-16 14:42:05 -08:00
|
|
|
sflow_global_counters_subid_clear(ds->sflow_agent->subId);
|
2011-06-15 11:28:51 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
static enum dpif_sflow_tunnel_type
|
|
|
|
dpif_sflow_tunnel_type(struct ofport *ofport) {
|
|
|
|
const char *type = netdev_get_type(ofport->netdev);
|
|
|
|
if (type) {
|
|
|
|
if (strcmp(type, "gre") == 0) {
|
|
|
|
return DPIF_SFLOW_TUNNEL_GRE;
|
|
|
|
} else if (strcmp(type, "ipsec_gre") == 0) {
|
|
|
|
return DPIF_SFLOW_TUNNEL_IPSEC_GRE;
|
|
|
|
} else if (strcmp(type, "vxlan") == 0) {
|
|
|
|
return DPIF_SFLOW_TUNNEL_VXLAN;
|
|
|
|
} else if (strcmp(type, "lisp") == 0) {
|
|
|
|
return DPIF_SFLOW_TUNNEL_LISP;
|
|
|
|
} else if (strcmp(type, "geneve") == 0) {
|
|
|
|
return DPIF_SFLOW_TUNNEL_GENEVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return DPIF_SFLOW_TUNNEL_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
dpif_sflow_tunnel_proto(enum dpif_sflow_tunnel_type tunnel_type)
|
|
|
|
{
|
|
|
|
/* Default to 0 (IPPROTO_IP), meaning "unknown". */
|
|
|
|
uint8_t ipproto = 0;
|
|
|
|
switch(tunnel_type) {
|
|
|
|
|
|
|
|
case DPIF_SFLOW_TUNNEL_GRE:
|
|
|
|
ipproto = IPPROTO_GRE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DPIF_SFLOW_TUNNEL_IPSEC_GRE:
|
|
|
|
ipproto = IPPROTO_ESP;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DPIF_SFLOW_TUNNEL_VXLAN:
|
|
|
|
case DPIF_SFLOW_TUNNEL_LISP:
|
|
|
|
case DPIF_SFLOW_TUNNEL_GENEVE:
|
|
|
|
ipproto = IPPROTO_UDP;
|
|
|
|
|
|
|
|
case DPIF_SFLOW_TUNNEL_UNKNOWN:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ipproto;
|
|
|
|
}
|
|
|
|
|
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;
|
2015-07-17 21:37:02 -07:00
|
|
|
enum dpif_sflow_tunnel_type tunnel_type;
|
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
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
tunnel_type = dpif_sflow_tunnel_type(ofport);
|
2011-12-08 13:26:05 -08:00
|
|
|
ifindex = netdev_get_ifindex(ofport->netdev);
|
2013-04-30 22:38:53 -07:00
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
if (ifindex <= 0
|
|
|
|
&& tunnel_type == DPIF_SFLOW_TUNNEL_UNKNOWN) {
|
|
|
|
/* Not an ifindex port, and not a tunnel port either
|
|
|
|
* 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;
|
2015-07-17 21:37:02 -07:00
|
|
|
dsp->tunnel_type = tunnel_type;
|
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
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
if (ifindex > 0) {
|
|
|
|
/* Add poller for ports that have ifindex. */
|
|
|
|
SFL_DS_SET(dsp->dsi, SFL_DSCLASS_IFINDEX, ifindex, 0);
|
|
|
|
if (ds->sflow_agent) {
|
|
|
|
dpif_sflow_add_poller(ds, dsp);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Record "ifindex unknown" for the others */
|
|
|
|
SFL_DS_SET(dsp->dsi, SFL_DSCLASS_IFINDEX, 0, 0);
|
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
|
|
|
{
|
2015-07-17 21:37:02 -07:00
|
|
|
if (ds->sflow_agent
|
|
|
|
&& SFL_DS_INDEX(dsp->dsi)) {
|
|
|
|
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;
|
2014-12-16 14:42:05 -08:00
|
|
|
SFLPoller *poller;
|
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) {
|
2014-12-16 14:42:05 -08:00
|
|
|
sflow_global_counters_subid_clear(ds->sflow_agent->subId);
|
2011-06-15 11:28:51 -07:00
|
|
|
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);
|
|
|
|
|
2014-12-16 14:42:05 -08:00
|
|
|
/* Add a counter poller for the bridge so we can use it to send
|
|
|
|
global counters such as datapath cache hit/miss stats. */
|
|
|
|
poller = sfl_agent_addPoller(ds->sflow_agent, &dsi, ds,
|
|
|
|
sflow_agent_get_global_counters);
|
|
|
|
sfl_poller_set_sFlowCpInterval(poller, ds->options->polling_interval);
|
|
|
|
sfl_poller_set_sFlowCpReceiver(poller, RECEIVER_INDEX);
|
|
|
|
|
2013-04-30 22:38:53 -07:00
|
|
|
/* Add pollers for the currently known ifindex-ports */
|
2011-06-15 11:28:51 -07:00
|
|
|
HMAP_FOR_EACH (dsp, hmap_node, &ds->ports) {
|
2015-07-17 21:37:02 -07:00
|
|
|
if (SFL_DS_INDEX(dsp->dsi)) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
static void
|
|
|
|
dpif_sflow_tunnel_v4(uint8_t tunnel_ipproto,
|
|
|
|
const struct flow_tnl *tunnel,
|
|
|
|
SFLSampled_ipv4 *ipv4)
|
|
|
|
|
|
|
|
{
|
|
|
|
ipv4->protocol = tunnel_ipproto;
|
|
|
|
ipv4->tos = tunnel->ip_tos;
|
|
|
|
ipv4->src_ip.addr = (OVS_FORCE uint32_t) tunnel->ip_src;
|
|
|
|
ipv4->dst_ip.addr = (OVS_FORCE uint32_t) tunnel->ip_dst;
|
|
|
|
ipv4->src_port = (OVS_FORCE uint16_t) tunnel->tp_src;
|
|
|
|
ipv4->dst_port = (OVS_FORCE uint16_t) tunnel->tp_dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dpif_sflow_push_mpls_lse(struct dpif_sflow_actions *sflow_actions,
|
|
|
|
ovs_be32 lse)
|
|
|
|
{
|
|
|
|
if (sflow_actions->mpls_stack_depth >= FLOW_MAX_MPLS_LABELS) {
|
|
|
|
sflow_actions->mpls_err = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Record the new lse in host-byte-order. */
|
|
|
|
/* BOS flag will be fixed later when we send stack to sFlow library. */
|
|
|
|
sflow_actions->mpls_lse[sflow_actions->mpls_stack_depth++] = ntohl(lse);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dpif_sflow_pop_mpls_lse(struct dpif_sflow_actions *sflow_actions)
|
|
|
|
{
|
|
|
|
if (sflow_actions->mpls_stack_depth == 0) {
|
|
|
|
sflow_actions->mpls_err = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sflow_actions->mpls_stack_depth--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dpif_sflow_set_mpls(struct dpif_sflow_actions *sflow_actions,
|
|
|
|
const struct ovs_key_mpls *mpls_key, int n)
|
|
|
|
{
|
|
|
|
int ii;
|
|
|
|
if (n > FLOW_MAX_MPLS_LABELS) {
|
|
|
|
sflow_actions->mpls_err = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ii = 0; ii < n; ii++) {
|
|
|
|
/* Reverse stack order, and use host-byte-order for each lse. */
|
|
|
|
sflow_actions->mpls_lse[n - ii - 1] = ntohl(mpls_key[ii].mpls_lse);
|
|
|
|
}
|
|
|
|
sflow_actions->mpls_stack_depth = n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sflow_read_tnl_push_action(const struct nlattr *attr,
|
|
|
|
struct dpif_sflow_actions *sflow_actions)
|
|
|
|
{
|
|
|
|
/* Modeled on lib/odp-util.c: format_odp_tnl_push_header */
|
|
|
|
const struct ovs_action_push_tnl *data = nl_attr_get(attr);
|
|
|
|
const struct eth_header *eth = (const struct eth_header *) data->header;
|
|
|
|
const struct ip_header *ip
|
|
|
|
= ALIGNED_CAST(const struct ip_header *, eth + 1);
|
|
|
|
|
|
|
|
sflow_actions->out_port = u32_to_odp(data->out_port);
|
|
|
|
|
|
|
|
/* Ethernet. */
|
|
|
|
/* TODO: SFlow does not currently define a MAC-in-MAC
|
|
|
|
* encapsulation structure. We could use an extension
|
|
|
|
* structure to report this.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* IPv4 */
|
|
|
|
/* Cannot assume alignment so just use memcpy. */
|
|
|
|
sflow_actions->tunnel.ip_src = get_16aligned_be32(&ip->ip_src);
|
|
|
|
sflow_actions->tunnel.ip_dst = get_16aligned_be32(&ip->ip_dst);
|
|
|
|
sflow_actions->tunnel.ip_tos = ip->ip_tos;
|
|
|
|
sflow_actions->tunnel.ip_ttl = ip->ip_ttl;
|
|
|
|
/* The tnl_push action can supply the ip_protocol too. */
|
|
|
|
sflow_actions->tunnel_ipproto = ip->ip_proto;
|
|
|
|
|
|
|
|
/* Layer 4 */
|
|
|
|
if (data->tnl_type == OVS_VPORT_TYPE_VXLAN
|
|
|
|
|| data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
|
|
|
|
const struct udp_header *udp = (const struct udp_header *) (ip + 1);
|
|
|
|
sflow_actions->tunnel.tp_src = udp->udp_src;
|
|
|
|
sflow_actions->tunnel.tp_dst = udp->udp_dst;
|
|
|
|
|
|
|
|
if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
|
|
|
|
const struct vxlanhdr *vxh = (const struct vxlanhdr *) (udp + 1);
|
|
|
|
uint64_t tun_id = ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8;
|
|
|
|
sflow_actions->tunnel.tun_id = htonll(tun_id);
|
|
|
|
} else {
|
|
|
|
const struct genevehdr *gnh = (const struct genevehdr *) (udp + 1);
|
|
|
|
uint64_t tun_id = ntohl(get_16aligned_be32(&gnh->vni)) >> 8;
|
|
|
|
sflow_actions->tunnel.tun_id = htonll(tun_id);
|
|
|
|
}
|
|
|
|
} else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
|
|
|
|
const void *l4 = ip + 1;
|
|
|
|
const struct gre_base_hdr *greh = (const struct gre_base_hdr *) l4;
|
|
|
|
ovs_16aligned_be32 *options = (ovs_16aligned_be32 *)(greh + 1);
|
|
|
|
if (greh->flags & htons(GRE_CSUM)) {
|
|
|
|
options++;
|
|
|
|
}
|
|
|
|
if (greh->flags & htons(GRE_KEY)) {
|
|
|
|
uint64_t tun_id = ntohl(get_16aligned_be32(options));
|
|
|
|
sflow_actions->tunnel.tun_id = htonll(tun_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sflow_read_set_action(const struct nlattr *attr,
|
|
|
|
struct dpif_sflow_actions *sflow_actions)
|
|
|
|
{
|
|
|
|
enum ovs_key_attr type = nl_attr_type(attr);
|
|
|
|
switch (type) {
|
|
|
|
case OVS_KEY_ATTR_ENCAP:
|
|
|
|
if (++sflow_actions->encap_depth > 1) {
|
|
|
|
/* Do not handle multi-encap for now. */
|
|
|
|
sflow_actions->tunnel_err = true;
|
|
|
|
} else {
|
|
|
|
dpif_sflow_read_actions(NULL,
|
|
|
|
nl_attr_get(attr), nl_attr_get_size(attr),
|
|
|
|
sflow_actions);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OVS_KEY_ATTR_PRIORITY:
|
|
|
|
case OVS_KEY_ATTR_SKB_MARK:
|
|
|
|
case OVS_KEY_ATTR_DP_HASH:
|
|
|
|
case OVS_KEY_ATTR_RECIRC_ID:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_KEY_ATTR_TUNNEL: {
|
|
|
|
if (++sflow_actions->encap_depth > 1) {
|
|
|
|
/* Do not handle multi-encap for now. */
|
|
|
|
sflow_actions->tunnel_err = true;
|
|
|
|
} else {
|
2015-06-29 18:01:59 -07:00
|
|
|
if (odp_tun_key_from_attr(attr, false, &sflow_actions->tunnel)
|
2015-07-17 21:37:02 -07:00
|
|
|
== ODP_FIT_ERROR) {
|
|
|
|
/* Tunnel parsing error. */
|
|
|
|
sflow_actions->tunnel_err = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OVS_KEY_ATTR_IN_PORT:
|
|
|
|
case OVS_KEY_ATTR_ETHERNET:
|
|
|
|
case OVS_KEY_ATTR_VLAN:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_KEY_ATTR_MPLS: {
|
|
|
|
const struct ovs_key_mpls *mpls_key = nl_attr_get(attr);
|
|
|
|
size_t size = nl_attr_get_size(attr);
|
|
|
|
dpif_sflow_set_mpls(sflow_actions, mpls_key, size / sizeof *mpls_key);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OVS_KEY_ATTR_ETHERTYPE:
|
|
|
|
case OVS_KEY_ATTR_IPV4:
|
|
|
|
if (sflow_actions->encap_depth == 1) {
|
|
|
|
const struct ovs_key_ipv4 *key = nl_attr_get(attr);
|
|
|
|
if (key->ipv4_src) {
|
|
|
|
sflow_actions->tunnel.ip_src = key->ipv4_src;
|
|
|
|
}
|
|
|
|
if (key->ipv4_dst) {
|
|
|
|
sflow_actions->tunnel.ip_dst = key->ipv4_dst;
|
|
|
|
}
|
|
|
|
if (key->ipv4_proto) {
|
|
|
|
sflow_actions->tunnel_ipproto = key->ipv4_proto;
|
|
|
|
}
|
|
|
|
if (key->ipv4_tos) {
|
|
|
|
sflow_actions->tunnel.ip_tos = key->ipv4_tos;
|
|
|
|
}
|
|
|
|
if (key->ipv4_ttl) {
|
|
|
|
sflow_actions->tunnel.ip_tos = key->ipv4_ttl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_KEY_ATTR_IPV6:
|
|
|
|
/* TODO: parse IPv6 encap. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* These have the same structure and format. */
|
|
|
|
case OVS_KEY_ATTR_TCP:
|
|
|
|
case OVS_KEY_ATTR_UDP:
|
|
|
|
case OVS_KEY_ATTR_SCTP:
|
|
|
|
if (sflow_actions->encap_depth == 1) {
|
|
|
|
const struct ovs_key_tcp *key = nl_attr_get(attr);
|
|
|
|
if (key->tcp_src) {
|
|
|
|
sflow_actions->tunnel.tp_src = key->tcp_src;
|
|
|
|
}
|
|
|
|
if (key->tcp_dst) {
|
|
|
|
sflow_actions->tunnel.tp_dst = key->tcp_dst;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_KEY_ATTR_TCP_FLAGS:
|
|
|
|
case OVS_KEY_ATTR_ICMP:
|
|
|
|
case OVS_KEY_ATTR_ICMPV6:
|
|
|
|
case OVS_KEY_ATTR_ARP:
|
|
|
|
case OVS_KEY_ATTR_ND:
|
|
|
|
case OVS_KEY_ATTR_UNSPEC:
|
|
|
|
case __OVS_KEY_ATTR_MAX:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dpif_sflow_capture_input_mpls(const struct flow *flow,
|
|
|
|
struct dpif_sflow_actions *sflow_actions)
|
|
|
|
{
|
|
|
|
if (eth_type_mpls(flow->dl_type)) {
|
|
|
|
int depth = 0;
|
|
|
|
int ii;
|
|
|
|
ovs_be32 lse;
|
|
|
|
/* Calculate depth by detecting BOS. */
|
|
|
|
for (ii = 0; ii < FLOW_MAX_MPLS_LABELS; ii++) {
|
|
|
|
lse = flow->mpls_lse[ii];
|
|
|
|
depth++;
|
|
|
|
if (lse & htonl(MPLS_BOS_MASK)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Capture stack, reversing stack order, and
|
|
|
|
* using host-byte-order for each lse. BOS flag
|
|
|
|
* is ignored for now. It is set later when
|
|
|
|
* the output stack is encoded.
|
|
|
|
*/
|
|
|
|
for (ii = 0; ii < depth; ii++) {
|
|
|
|
lse = flow->mpls_lse[ii];
|
|
|
|
sflow_actions->mpls_lse[depth - ii - 1] = ntohl(lse);
|
|
|
|
}
|
|
|
|
sflow_actions->mpls_stack_depth = depth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dpif_sflow_read_actions(const struct flow *flow,
|
|
|
|
const struct nlattr *actions, size_t actions_len,
|
|
|
|
struct dpif_sflow_actions *sflow_actions)
|
|
|
|
{
|
|
|
|
const struct nlattr *a;
|
|
|
|
unsigned int left;
|
|
|
|
|
|
|
|
if (actions_len == 0) {
|
|
|
|
/* Packet dropped.*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flow != NULL) {
|
|
|
|
/* Make sure the MPLS output stack
|
|
|
|
* is seeded with the input stack.
|
|
|
|
*/
|
|
|
|
dpif_sflow_capture_input_mpls(flow, sflow_actions);
|
|
|
|
|
|
|
|
/* XXX when 802.1AD(QinQ) is supported then
|
|
|
|
* we can do the same with VLAN stacks here
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (a, left, actions, actions_len) {
|
|
|
|
enum ovs_action_attr type = nl_attr_type(a);
|
|
|
|
switch (type) {
|
|
|
|
case OVS_ACTION_ATTR_OUTPUT:
|
|
|
|
/* Capture the output port in case we need it
|
|
|
|
* to get the output tunnel type.
|
|
|
|
*/
|
|
|
|
sflow_actions->out_port = u32_to_odp(nl_attr_get_u32(a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_POP:
|
|
|
|
/* XXX: Do not handle this for now. It's not clear
|
|
|
|
* if we should start with encap_depth == 1 when we
|
|
|
|
* see an input tunnel, or if we should assume
|
|
|
|
* that the input tunnel was always "popped" if it
|
|
|
|
* was presented to us decoded in flow->tunnel?
|
|
|
|
*
|
|
|
|
* If we do handle this it might look like this,
|
|
|
|
* as we clear the captured tunnel info and decrement
|
|
|
|
* the encap_depth:
|
|
|
|
*
|
|
|
|
* memset(&sflow_actions->tunnel, 0, sizeof struct flow_tnl);
|
|
|
|
* sflow_actions->tunnel_ipproto = 0;
|
|
|
|
* --sflow_actions->encap_depth;
|
|
|
|
*
|
|
|
|
* but for now just disable the tunnel annotation:
|
|
|
|
*/
|
|
|
|
sflow_actions->tunnel_err = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_PUSH:
|
|
|
|
/* XXX: This actions appears to come with it's own
|
|
|
|
* OUTPUT action, so should it be regarded as having
|
|
|
|
* an implicit "pop" following it too? Put another
|
|
|
|
* way, would two tnl_push() actions in succession
|
|
|
|
* result in a packet with two layers of encap?
|
|
|
|
*/
|
|
|
|
if (++sflow_actions->encap_depth > 1) {
|
|
|
|
/* Do not handle multi-encap for now. */
|
|
|
|
sflow_actions->tunnel_err = true;
|
|
|
|
} else {
|
|
|
|
sflow_read_tnl_push_action(a, sflow_actions);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_USERSPACE:
|
|
|
|
case OVS_ACTION_ATTR_RECIRC:
|
|
|
|
case OVS_ACTION_ATTR_HASH:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_SET_MASKED:
|
|
|
|
/* TODO: apply mask. XXX: Are we likely to see this? */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_SET:
|
|
|
|
sflow_read_set_action(nl_attr_get(a), sflow_actions);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_PUSH_VLAN:
|
|
|
|
case OVS_ACTION_ATTR_POP_VLAN:
|
|
|
|
/* TODO: 802.1AD(QinQ) is not supported by OVS (yet), so do not
|
|
|
|
* construct a VLAN-stack. The sFlow user-action cookie already
|
|
|
|
* captures the egress VLAN ID so there is nothing more to do here.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_PUSH_MPLS: {
|
|
|
|
const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
|
|
|
|
if (mpls) {
|
|
|
|
dpif_sflow_push_mpls_lse(sflow_actions, mpls->mpls_lse);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case OVS_ACTION_ATTR_POP_MPLS: {
|
|
|
|
dpif_sflow_pop_mpls_lse(sflow_actions);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case OVS_ACTION_ATTR_SAMPLE:
|
|
|
|
case OVS_ACTION_ATTR_UNSPEC:
|
|
|
|
case __OVS_ACTION_ATTR_MAX:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dpif_sflow_encode_mpls_stack(SFLLabelStack *stack,
|
|
|
|
uint32_t *mpls_lse_buf,
|
|
|
|
const struct dpif_sflow_actions *sflow_actions)
|
|
|
|
{
|
|
|
|
/* Put the MPLS stack back into "packet header" order,
|
|
|
|
* and make sure the BOS flag is set correctly on the last
|
|
|
|
* one. Each lse is still in host-byte-order.
|
|
|
|
*/
|
|
|
|
int ii;
|
|
|
|
uint32_t lse;
|
|
|
|
stack->depth = sflow_actions->mpls_stack_depth;
|
|
|
|
stack->stack = mpls_lse_buf;
|
|
|
|
for (ii = 0; ii < stack->depth; ii++) {
|
|
|
|
lse = sflow_actions->mpls_lse[stack->depth - ii - 1];
|
|
|
|
stack->stack[ii] = (lse & ~MPLS_BOS_MASK);
|
|
|
|
}
|
|
|
|
stack->stack[stack->depth - 1] |= MPLS_BOS_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extract the output port count from the user action cookie.
|
|
|
|
* See http://sflow.org/sflow_version_5.txt "Input/Output port information"
|
|
|
|
*/
|
|
|
|
static uint32_t
|
|
|
|
dpif_sflow_cookie_num_outputs(const union user_action_cookie *cookie)
|
|
|
|
{
|
|
|
|
uint32_t format = cookie->sflow.output & 0xC0000000;
|
|
|
|
uint32_t port_n = cookie->sflow.output & 0x3FFFFFFF;
|
|
|
|
if (format == 0) {
|
|
|
|
return port_n ? 1 : 0;
|
|
|
|
}
|
|
|
|
else if (format == 0x80000000) {
|
|
|
|
return port_n;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-04 13:08:37 -08:00
|
|
|
void
|
2015-02-22 03:21:09 -08:00
|
|
|
dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet,
|
2015-07-17 21:37:02 -07:00
|
|
|
const struct flow *flow, odp_port_t odp_in_port,
|
|
|
|
const union user_action_cookie *cookie,
|
|
|
|
const struct dpif_sflow_actions *sflow_actions)
|
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;
|
2015-07-17 21:37:02 -07:00
|
|
|
uint8_t tnlInProto, tnlOutProto;
|
|
|
|
SFLFlow_sample_element tnlInElem, tnlOutElem;
|
|
|
|
SFLFlow_sample_element vniInElem, vniOutElem;
|
|
|
|
SFLFlow_sample_element mplsElem;
|
|
|
|
uint32_t mpls_lse_buf[FLOW_MAX_MPLS_LABELS];
|
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;
|
2015-07-17 21:37:02 -07:00
|
|
|
struct dpif_sflow_port *out_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. */
|
2015-02-22 03:21:09 -08:00
|
|
|
header->frame_length = dp_packet_size(packet) + 4;
|
2010-05-05 13:24:03 -07:00
|
|
|
/* Ethernet FCS stripped off. */
|
|
|
|
header->stripped = 4;
|
2015-02-22 03:21:09 -08:00
|
|
|
header->header_length = MIN(dp_packet_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);
|
2015-02-22 03:21:09 -08:00
|
|
|
header->header_bytes = dp_packet_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
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
/* Input tunnel. */
|
|
|
|
if (flow->tunnel.ip_dst) {
|
|
|
|
memset(&tnlInElem, 0, sizeof(tnlInElem));
|
|
|
|
tnlInElem.tag = SFLFLOW_EX_IPV4_TUNNEL_INGRESS;
|
|
|
|
tnlInProto = dpif_sflow_tunnel_proto(in_dsp->tunnel_type);
|
|
|
|
dpif_sflow_tunnel_v4(tnlInProto,
|
|
|
|
&flow->tunnel,
|
|
|
|
&tnlInElem.flowType.ipv4);
|
|
|
|
SFLADD_ELEMENT(&fs, &tnlInElem);
|
|
|
|
if (flow->tunnel.tun_id) {
|
|
|
|
memset(&vniInElem, 0, sizeof(vniInElem));
|
|
|
|
vniInElem.tag = SFLFLOW_EX_VNI_INGRESS;
|
|
|
|
vniInElem.flowType.tunnel_vni.vni
|
|
|
|
= ntohll(flow->tunnel.tun_id);
|
|
|
|
SFLADD_ELEMENT(&fs, &vniInElem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output tunnel. */
|
|
|
|
if (sflow_actions
|
|
|
|
&& sflow_actions->encap_depth == 1
|
|
|
|
&& !sflow_actions->tunnel_err
|
|
|
|
&& dpif_sflow_cookie_num_outputs(cookie) == 1) {
|
|
|
|
tnlOutProto = sflow_actions->tunnel_ipproto;
|
|
|
|
if (tnlOutProto == 0) {
|
|
|
|
/* Try to infer the ip-protocol from the output port. */
|
|
|
|
if (sflow_actions->out_port != ODPP_NONE) {
|
|
|
|
out_dsp = dpif_sflow_find_port(ds, sflow_actions->out_port);
|
|
|
|
if (out_dsp) {
|
|
|
|
tnlOutProto = dpif_sflow_tunnel_proto(out_dsp->tunnel_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
memset(&tnlOutElem, 0, sizeof(tnlOutElem));
|
|
|
|
tnlOutElem.tag = SFLFLOW_EX_IPV4_TUNNEL_EGRESS;
|
|
|
|
dpif_sflow_tunnel_v4(tnlOutProto,
|
|
|
|
&sflow_actions->tunnel,
|
|
|
|
&tnlOutElem.flowType.ipv4);
|
|
|
|
SFLADD_ELEMENT(&fs, &tnlOutElem);
|
|
|
|
if (sflow_actions->tunnel.tun_id) {
|
|
|
|
memset(&vniOutElem, 0, sizeof(vniOutElem));
|
|
|
|
vniOutElem.tag = SFLFLOW_EX_VNI_EGRESS;
|
|
|
|
vniOutElem.flowType.tunnel_vni.vni
|
|
|
|
= ntohll(sflow_actions->tunnel.tun_id);
|
|
|
|
SFLADD_ELEMENT(&fs, &vniOutElem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* MPLS output label stack. */
|
|
|
|
if (sflow_actions
|
|
|
|
&& sflow_actions->mpls_stack_depth > 0
|
|
|
|
&& !sflow_actions->mpls_err
|
|
|
|
&& dpif_sflow_cookie_num_outputs(cookie) == 1) {
|
|
|
|
memset(&mplsElem, 0, sizeof(mplsElem));
|
|
|
|
mplsElem.tag = SFLFLOW_EX_MPLS;
|
|
|
|
dpif_sflow_encode_mpls_stack(&mplsElem.flowType.mpls.out_stack,
|
|
|
|
mpls_lse_buf,
|
|
|
|
sflow_actions);
|
|
|
|
SFLADD_ELEMENT(&fs, &mplsElem);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|