2010-01-04 13:08:37 -08:00
|
|
|
/*
|
2016-06-24 21:23:16 -07:00
|
|
|
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 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"
|
2016-07-12 16:37:34 -05:00
|
|
|
#include "openvswitch/hmap.h"
|
2010-01-04 13:08:37 -08:00
|
|
|
#include "netdev.h"
|
2010-12-10 10:40:58 -08:00
|
|
|
#include "netlink.h"
|
2016-03-25 14:10:24 -07:00
|
|
|
#include "openvswitch/ofpbuf.h"
|
2010-01-04 13:08:37 -08:00
|
|
|
#include "ofproto.h"
|
2010-10-08 16:26:21 -07:00
|
|
|
#include "packets.h"
|
2017-11-03 13:53:53 +08:00
|
|
|
#include "openvswitch/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_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. */
|
2022-07-08 08:53:08 +02:00
|
|
|
struct ofport *ofport; /* To retrieve 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);
|
|
|
|
|
|
|
|
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);
|
2016-06-24 21:23:16 -07:00
|
|
|
new->agent_device = nullable_xstrdup(old->agent_device);
|
|
|
|
new->control_ip = nullable_xstrdup(old->control_ip);
|
2010-01-04 13:08:37 -08:00
|
|
|
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
|
|
|
{
|
2015-09-16 08:35:22 -07:00
|
|
|
return xzalloc(bytes);
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2017-02-24 11:57:04 +00:00
|
|
|
SFLCounters_sample_element eth_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;
|
2017-02-24 11:57:04 +00:00
|
|
|
SFLEthernet_counters* eth_counters;
|
2010-01-04 13:08:37 -08:00
|
|
|
struct netdev_stats stats;
|
|
|
|
enum netdev_flags flags;
|
2020-06-17 14:16:08 -07:00
|
|
|
struct lacp_member_stats lacp_stats;
|
2023-07-17 10:08:11 +02:00
|
|
|
uint32_t curr_speed;
|
2014-06-27 11:19:59 -07:00
|
|
|
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 */
|
2010-01-04 13:08:37 -08:00
|
|
|
counters->ifDirection = (netdev_features_is_full_duplex(current)
|
|
|
|
? 1 : 2);
|
|
|
|
} else {
|
2010-01-11 11:08:29 -08:00
|
|
|
counters->ifDirection = 0;
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
2023-07-17 10:08:11 +02:00
|
|
|
|
|
|
|
netdev_get_speed(dsp->ofport->netdev, &curr_speed, NULL);
|
|
|
|
if (curr_speed) {
|
|
|
|
counters->ifSpeed = curr_speed * 1000000;
|
|
|
|
} else {
|
|
|
|
counters->ifSpeed = 100000000;
|
|
|
|
}
|
|
|
|
|
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;
|
2017-02-24 11:57:04 +00:00
|
|
|
counters->ifInBroadcastPkts = stats.rx_broadcast_packets;
|
2010-01-04 13:08:37 -08:00
|
|
|
counters->ifInDiscards = stats.rx_dropped;
|
|
|
|
counters->ifInErrors = stats.rx_errors;
|
|
|
|
counters->ifInUnknownProtos = -1;
|
|
|
|
counters->ifOutOctets = stats.tx_bytes;
|
|
|
|
counters->ifOutUcastPkts = stats.tx_packets;
|
2017-02-24 11:57:04 +00:00
|
|
|
counters->ifOutMulticastPkts = stats.tx_multicast_packets;
|
|
|
|
counters->ifOutBroadcastPkts = stats.tx_broadcast_packets;
|
2010-01-04 13:08:37 -08:00
|
|
|
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) {
|
2018-05-25 17:11:07 -07:00
|
|
|
memset(&lacp_elem, 0, sizeof lacp_elem);
|
|
|
|
lacp_elem.tag = SFLCOUNTERS_LACP;
|
|
|
|
lacp_elem.counterBlock.lacp.actorSystemID =
|
2015-08-28 14:55:11 -07:00
|
|
|
lacp_stats.dot3adAggPortActorSystemID;
|
2018-05-25 17:11:07 -07:00
|
|
|
lacp_elem.counterBlock.lacp.partnerSystemID =
|
2015-08-28 14:55:11 -07:00
|
|
|
lacp_stats.dot3adAggPortPartnerOperSystemID;
|
2018-05-25 17:11:07 -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);
|
2014-06-27 11:19:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Include Port name. */
|
|
|
|
if ((ifName = netdev_get_name(dsp->ofport->netdev)) != NULL) {
|
2018-05-25 17:11:07 -07:00
|
|
|
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);
|
2014-06-27 11:19:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 =
|
2018-05-25 17:11:07 -07:00
|
|
|
ofproto_get_datapath_id(dsp->ofport->ofproto);
|
2014-06-27 11:19:59 -07:00
|
|
|
of_elem.counterBlock.ofPort.port_no =
|
|
|
|
(OVS_FORCE uint32_t)dsp->ofport->ofp_port;
|
|
|
|
SFLADD_ELEMENT(cs, &of_elem);
|
|
|
|
|
2017-02-24 11:57:04 +00:00
|
|
|
/* Include ethernet counters */
|
|
|
|
memset(ð_elem, 0, sizeof eth_elem);
|
|
|
|
eth_elem.tag = SFLCOUNTERS_ETHERNET;
|
|
|
|
eth_counters = ð_elem.counterBlock.ethernet;
|
|
|
|
eth_counters->dot3StatsAlignmentErrors = stats.rx_frame_errors;
|
|
|
|
eth_counters->dot3StatsFCSErrors = stats.rx_crc_errors;
|
|
|
|
eth_counters->dot3StatsFrameTooLongs = stats.rx_oversize_errors;
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsSingleCollisionFrames);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsMultipleCollisionFrames);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsSQETestErrors);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsDeferredTransmissions);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsLateCollisions);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsExcessiveCollisions);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsInternalMacTransmitErrors);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsCarrierSenseErrors);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsInternalMacReceiveErrors);
|
|
|
|
SFL_UNDEF_COUNTER(eth_counters->dot3StatsSymbolErrors);
|
|
|
|
SFLADD_ELEMENT(cs, ð_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)
|
|
|
|
{
|
2018-04-13 10:04:26 -07:00
|
|
|
struct in6_addr ip;
|
2010-01-04 13:08:37 -08:00
|
|
|
|
|
|
|
if (agent_device) {
|
2018-04-13 10:04:26 -07:00
|
|
|
/* If 'agent_device' is the name of a network device, use its IP
|
|
|
|
* address. */
|
|
|
|
if (!netdev_get_ip_by_name(agent_device, &ip)) {
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If 'agent_device' is itself an IP address, use it. */
|
|
|
|
struct sockaddr_storage ss;
|
|
|
|
if (inet_parse_address(agent_device, &ss)) {
|
|
|
|
ip = ss_get_address(&ss);
|
2011-12-06 13:01:25 -08:00
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 10:04:26 -07:00
|
|
|
/* Otherwise, use an appropriate local IP address for one of the
|
|
|
|
* collectors' remote IP addresses. */
|
|
|
|
const char *target;
|
2011-12-06 13:01:25 -08:00
|
|
|
SSET_FOR_EACH (target, targets) {
|
2018-04-13 10:04:26 -07:00
|
|
|
struct sockaddr_storage ss;
|
2022-03-10 23:33:17 +01:00
|
|
|
if (inet_parse_active(target, SFL_DEFAULT_COLLECTOR_PORT,
|
|
|
|
&ss, true, NULL)) {
|
2016-12-28 11:41:25 -08:00
|
|
|
/* sFlow only supports target in default routing table with
|
|
|
|
* packet mark zero.
|
|
|
|
*/
|
2018-09-19 13:25:58 -07:00
|
|
|
struct in6_addr target_ip = ss_get_address(&ss);
|
2016-03-24 09:30:57 -07:00
|
|
|
|
2018-04-18 11:03:47 +08:00
|
|
|
struct in6_addr gw, src = in6addr_any;
|
2018-04-13 10:04:26 -07:00
|
|
|
char name[IFNAMSIZ];
|
2018-09-19 13:25:58 -07:00
|
|
|
if (ovs_router_lookup(0, &target_ip, name, &src, &gw)) {
|
|
|
|
ip = src;
|
2014-02-06 16:04:05 -08:00
|
|
|
goto success;
|
|
|
|
}
|
2010-01-04 13:08:37 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 10:04:26 -07:00
|
|
|
struct sockaddr_storage ss;
|
|
|
|
if (control_ip && inet_parse_address(control_ip, &ss)) {
|
|
|
|
ip = ss_get_address(&ss);
|
2010-01-04 13:08:37 -08:00
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
|
|
|
|
VLOG_ERR("could not determine IP address for sFlow agent");
|
|
|
|
return false;
|
|
|
|
|
|
|
|
success:
|
2018-04-13 10:04:26 -07:00
|
|
|
memset(agent_addr, 0, sizeof *agent_addr);
|
|
|
|
if (IN6_IS_ADDR_V4MAPPED(&ip)) {
|
|
|
|
agent_addr->type = SFLADDRESSTYPE_IP_V4;
|
|
|
|
agent_addr->address.ip_v4.addr
|
|
|
|
= (OVS_FORCE uint32_t) in6_addr_get_mapped_ipv4(&ip);
|
|
|
|
} else {
|
|
|
|
agent_addr->type = SFLADDRESSTYPE_IP_V6;
|
|
|
|
memcpy(agent_addr->address.ip_v6.addr, ip.s6_addr,
|
|
|
|
sizeof agent_addr->address.ip_v6.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) {
|
2022-03-23 12:56:17 +01:00
|
|
|
struct dpif_sflow_port *dsp;
|
2010-05-07 09:29:02 -07:00
|
|
|
|
2011-06-15 11:28:51 -07:00
|
|
|
dpif_sflow_clear(ds);
|
2022-03-23 12:56:17 +01:00
|
|
|
HMAP_FOR_EACH_SAFE (dsp, hmap_node, &ds->ports) {
|
2011-06-15 11:28:51 -07:00
|
|
|
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) {
|
2018-05-25 17:11:07 -07:00
|
|
|
if (strcmp(type, "gre") == 0) {
|
|
|
|
return DPIF_SFLOW_TUNNEL_GRE;
|
|
|
|
} else if (strcmp(type, "vxlan") == 0) {
|
|
|
|
return DPIF_SFLOW_TUNNEL_VXLAN;
|
|
|
|
} else if (strcmp(type, "geneve") == 0) {
|
|
|
|
return DPIF_SFLOW_TUNNEL_GENEVE;
|
|
|
|
}
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
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_VXLAN:
|
|
|
|
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
|
2018-05-25 17:11:07 -07:00
|
|
|
&& tunnel_type == DPIF_SFLOW_TUNNEL_UNKNOWN) {
|
2015-07-17 21:37:02 -07:00
|
|
|
/* Not an ifindex port, and not a tunnel port either
|
2018-05-25 17:11:07 -07:00
|
|
|
* 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) {
|
2018-05-25 17:11:07 -07:00
|
|
|
/* 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);
|
|
|
|
}
|
2015-07-17 21:37:02 -07:00
|
|
|
} else {
|
2018-05-25 17:11:07 -07:00
|
|
|
/* 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
|
2018-05-25 17:11:07 -07:00
|
|
|
&& 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");
|
2024-05-29 12:53:30 +02:00
|
|
|
sfl_receiver_set_sFlowRcvrTimeout(receiver, UINT32_MAX);
|
2010-01-04 13:08:37 -08:00
|
|
|
|
|
|
|
/* 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);
|
2018-05-25 17:11:07 -07:00
|
|
|
}
|
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) {
|
2018-05-25 17:11:07 -07:00
|
|
|
sflow_actions->mpls_err = true;
|
|
|
|
return;
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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) {
|
2018-05-25 17:11:07 -07:00
|
|
|
sflow_actions->mpls_err = true;
|
|
|
|
return;
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
sflow_actions->mpls_stack_depth--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dpif_sflow_set_mpls(struct dpif_sflow_actions *sflow_actions,
|
2018-05-25 17:11:07 -07:00
|
|
|
const struct ovs_key_mpls *mpls_key, int n)
|
2015-07-17 21:37:02 -07:00
|
|
|
{
|
|
|
|
int ii;
|
|
|
|
if (n > FLOW_MAX_MPLS_LABELS) {
|
2018-05-25 17:11:07 -07:00
|
|
|
sflow_actions->mpls_err = true;
|
|
|
|
return;
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (ii = 0; ii < n; ii++) {
|
2018-05-25 17:11:07 -07:00
|
|
|
/* Reverse stack order, and use host-byte-order for each lse. */
|
|
|
|
sflow_actions->mpls_lse[n - ii - 1] = ntohl(mpls_key[ii].mpls_lse);
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
|
2017-06-18 08:59:30 +08:00
|
|
|
sflow_actions->out_port = data->out_port;
|
2015-07-17 21:37:02 -07:00
|
|
|
|
|
|
|
/* 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),
|
2018-01-09 19:54:31 +01:00
|
|
|
sflow_actions, true);
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
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 {
|
2018-12-14 18:16:55 -08:00
|
|
|
if (odp_tun_key_from_attr(attr, &sflow_actions->tunnel, NULL)
|
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) {
|
2019-10-07 00:34:55 -04:00
|
|
|
sflow_actions->tunnel.ip_ttl = key->ipv4_ttl;
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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:
|
2019-01-28 11:41:06 +00:00
|
|
|
case OVS_KEY_ATTR_ND_EXTENSIONS:
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
case OVS_KEY_ATTR_CT_STATE:
|
|
|
|
case OVS_KEY_ATTR_CT_ZONE:
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
case OVS_KEY_ATTR_CT_MARK:
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
case OVS_KEY_ATTR_CT_LABELS:
|
datapath: Add original direction conntrack tuple to sw_flow_key.
Upstream commit:
commit 9dd7f8907c3705dc7a7a375d1c6e30b06e6daffc
Author: Jarno Rajahalme <jarno@ovn.org>
Date: Thu Feb 9 11:21:59 2017 -0800
openvswitch: Add original direction conntrack tuple to sw_flow_key.
Add the fields of the conntrack original direction 5-tuple to struct
sw_flow_key. The new fields are initially marked as non-existent, and
are populated whenever a conntrack action is executed and either finds
or generates a conntrack entry. This means that these fields exist
for all packets that were not rejected by conntrack as untrackable.
The original tuple fields in the sw_flow_key are filled from the
original direction tuple of the conntrack entry relating to the
current packet, or from the original direction tuple of the master
conntrack entry, if the current conntrack entry has a master.
Generally, expected connections of connections having an assigned
helper (e.g., FTP), have a master conntrack entry.
The main purpose of the new conntrack original tuple fields is to
allow matching on them for policy decision purposes, with the premise
that the admissibility of tracked connections reply packets (as well
as original direction packets), and both direction packets of any
related connections may be based on ACL rules applying to the master
connection's original direction 5-tuple. This also makes it easier to
make policy decisions when the actual packet headers might have been
transformed by NAT, as the original direction 5-tuple represents the
packet headers before any such transformation.
When using the original direction 5-tuple the admissibility of return
and/or related packets need not be based on the mere existence of a
conntrack entry, allowing separation of admission policy from the
established conntrack state. While existence of a conntrack entry is
required for admission of the return or related packets, policy
changes can render connections that were initially admitted to be
rejected or dropped afterwards. If the admission of the return and
related packets was based on mere conntrack state (e.g., connection
being in an established state), a policy change that would make the
connection rejected or dropped would need to find and delete all
conntrack entries affected by such a change. When using the original
direction 5-tuple matching the affected conntrack entries can be
allowed to time out instead, as the established state of the
connection would not need to be the basis for packet admission any
more.
It should be noted that the directionality of related connections may
be the same or different than that of the master connection, and
neither the original direction 5-tuple nor the conntrack state bits
carry this information. If needed, the directionality of the master
connection can be stored in master's conntrack mark or labels, which
are automatically inherited by the expected related connections.
The fact that neither ARP nor ND packets are trackable by conntrack
allows mutual exclusion between ARP/ND and the new conntrack original
tuple fields. Hence, the IP addresses are overlaid in union with ARP
and ND fields. This allows the sw_flow_key to not grow much due to
this patch, but it also means that we must be careful to never use the
new key fields with ARP or ND packets. ARP is easy to distinguish and
keep mutually exclusive based on the ethernet type, but ND being an
ICMPv6 protocol requires a bit more attention.
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: Joe Stringer <joe@ovn.org>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This patch squashes in minimal amount of OVS userspace code to not
break the build. Later patches contain the full userspace support.
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: Joe Stringer <joe@ovn.org>
2017-03-08 17:18:22 -08:00
|
|
|
case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4:
|
|
|
|
case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6:
|
2015-07-17 21:37:02 -07:00
|
|
|
case OVS_KEY_ATTR_UNSPEC:
|
2017-06-02 16:16:17 +00:00
|
|
|
case OVS_KEY_ATTR_PACKET_TYPE:
|
userspace: Add support for NSH MD1 match fields
This patch adds support for NSH packet header fields to the OVS
control plane and the userspace datapath. Initially we support the
fields of the NSH base header as defined in
https://www.ietf.org/id/draft-ietf-sfc-nsh-13.txt
and the fixed context headers specified for metadata format MD1.
The variable length MD2 format is parsed but the TLV context headers
are not yet available for matching.
The NSH fields are modelled as experimenter fields with the dedicated
experimenter class 0x005ad650 proposed for NSH in ONF. The following
fields are defined:
NXOXM code ofctl name Size Comment
=====================================================================
NXOXM_NSH_FLAGS nsh_flags 8 Bits 2-9 of 1st NSH word
(0x005ad650,1)
NXOXM_NSH_MDTYPE nsh_mdtype 8 Bits 16-23
(0x005ad650,2)
NXOXM_NSH_NEXTPROTO nsh_np 8 Bits 24-31
(0x005ad650,3)
NXOXM_NSH_SPI nsh_spi 24 Bits 0-23 of 2nd NSH word
(0x005ad650,4)
NXOXM_NSH_SI nsh_si 8 Bits 24-31
(0x005ad650,5)
NXOXM_NSH_C1 nsh_c1 32 Maskable, nsh_mdtype==1
(0x005ad650,6)
NXOXM_NSH_C2 nsh_c2 32 Maskable, nsh_mdtype==1
(0x005ad650,7)
NXOXM_NSH_C3 nsh_c3 32 Maskable, nsh_mdtype==1
(0x005ad650,8)
NXOXM_NSH_C4 nsh_c4 32 Maskable, nsh_mdtype==1
(0x005ad650,9)
Co-authored-by: Johnson Li <johnson.li@intel.com>
Signed-off-by: Yi Yang <yi.y.yang@intel.com>
Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2017-08-05 13:41:08 +08:00
|
|
|
case OVS_KEY_ATTR_NSH:
|
2022-03-25 14:48:23 +01:00
|
|
|
case OVS_KEY_ATTR_TUNNEL_INFO:
|
2015-07-17 21:37:02 -07:00
|
|
|
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,
|
2018-01-09 19:54:31 +01:00
|
|
|
const struct nlattr *actions, size_t actions_len,
|
|
|
|
struct dpif_sflow_actions *sflow_actions,
|
|
|
|
bool capture_mpls)
|
2015-07-17 21:37:02 -07:00
|
|
|
{
|
|
|
|
const struct nlattr *a;
|
|
|
|
unsigned int left;
|
|
|
|
|
|
|
|
if (actions_len == 0) {
|
2018-05-25 17:11:07 -07:00
|
|
|
/* Packet dropped.*/
|
|
|
|
return;
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
|
2018-01-09 19:54:31 +01:00
|
|
|
if (flow != NULL && capture_mpls == true) {
|
2018-05-25 17:11:07 -07:00
|
|
|
/* 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
|
|
|
|
*/
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (a, left, actions, actions_len) {
|
2018-05-25 17:11:07 -07:00
|
|
|
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 = nl_attr_get_odp_port(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_TRUNC:
|
|
|
|
case OVS_ACTION_ATTR_USERSPACE:
|
|
|
|
case OVS_ACTION_ATTR_RECIRC:
|
|
|
|
case OVS_ACTION_ATTR_HASH:
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
case OVS_ACTION_ATTR_CT:
|
userspace: Avoid dp_hash recirculation for balance-tcp bond mode.
Problem:
In OVS, flows with output over a bond interface of type “balance-tcp”
gets translated by the ofproto layer into "HASH" and "RECIRC" datapath
actions. After recirculation, the packet is forwarded to the bond
member port based on 8-bits of the datapath hash value computed through
dp_hash. This causes performance degradation in the following ways:
1. The recirculation of the packet implies another lookup of the
packet’s flow key in the exact match cache (EMC) and potentially
Megaflow classifier (DPCLS). This is the biggest cost factor.
2. The recirculated packets have a new “RSS” hash and compete with the
original packets for the scarce number of EMC slots. This implies more
EMC misses and potentially EMC thrashing causing costly DPCLS lookups.
3. The 256 extra megaflow entries per bond for dp_hash bond selection
put additional load on the revalidation threads.
Owing to this performance degradation, deployments stick to “balance-slb”
bond mode even though it does not do active-active load balancing for
VXLAN- and GRE-tunnelled traffic because all tunnel packet have the
same source MAC address.
Proposed optimization:
This proposal introduces a new load-balancing output action instead of
recirculation.
Maintain one table per-bond (could just be an array of uint16's) and
program it the same way internal flows are created today for each
possible hash value (256 entries) from ofproto layer. Use this table to
load-balance flows as part of output action processing.
Currently xlate_normal() -> output_normal() ->
bond_update_post_recirc_rules() -> bond_may_recirc() and
compose_output_action__() generate 'dp_hash(hash_l4(0))' and
'recirc(<RecircID>)' actions. In this case the RecircID identifies the
bond. For the recirculated packets the ofproto layer installs megaflow
entries that match on RecircID and masked dp_hash and send them to the
corresponding output port.
Instead, we will now generate action as
'lb_output(<bond id>)'
This combines hash computation (only if needed, else re-use RSS hash)
and inline load-balancing over the bond. This action is used *only* for
balance-tcp bonds in userspace datapath (the OVS kernel datapath
remains unchanged).
Example:
Current scheme:
With 8 UDP flows (with random UDP src port):
flow-dump from pmd on cpu core: 2
recirc_id(0),in_port(7),<...> actions:hash(hash_l4(0)),recirc(0x1)
recirc_id(0x1),dp_hash(0xf8e02b7e/0xff),<...> actions:2
recirc_id(0x1),dp_hash(0xb236c260/0xff),<...> actions:1
recirc_id(0x1),dp_hash(0x7d89eb18/0xff),<...> actions:1
recirc_id(0x1),dp_hash(0xa78d75df/0xff),<...> actions:2
recirc_id(0x1),dp_hash(0xb58d846f/0xff),<...> actions:2
recirc_id(0x1),dp_hash(0x24534406/0xff),<...> actions:1
recirc_id(0x1),dp_hash(0x3cf32550/0xff),<...> actions:1
New scheme:
We can do with a single flow entry (for any number of new flows):
in_port(7),<...> actions:lb_output(1)
A new CLI has been added to dump datapath bond cache as given below.
# ovs-appctl dpif-netdev/bond-show [dp]
Bond cache:
bond-id 1 :
bucket 0 - slave 2
bucket 1 - slave 1
bucket 2 - slave 2
bucket 3 - slave 1
Co-authored-by: Manohar Krishnappa Chidambaraswamy <manukc@gmail.com>
Signed-off-by: Manohar Krishnappa Chidambaraswamy <manukc@gmail.com>
Signed-off-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
Tested-by: Matteo Croce <mcroce@redhat.com>
Tested-by: Adrian Moreno <amorenoz@redhat.com>
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2020-05-22 10:50:05 +02:00
|
|
|
case OVS_ACTION_ATTR_CT_CLEAR:
|
2017-02-23 11:27:54 -08:00
|
|
|
case OVS_ACTION_ATTR_METER:
|
userspace: Avoid dp_hash recirculation for balance-tcp bond mode.
Problem:
In OVS, flows with output over a bond interface of type “balance-tcp”
gets translated by the ofproto layer into "HASH" and "RECIRC" datapath
actions. After recirculation, the packet is forwarded to the bond
member port based on 8-bits of the datapath hash value computed through
dp_hash. This causes performance degradation in the following ways:
1. The recirculation of the packet implies another lookup of the
packet’s flow key in the exact match cache (EMC) and potentially
Megaflow classifier (DPCLS). This is the biggest cost factor.
2. The recirculated packets have a new “RSS” hash and compete with the
original packets for the scarce number of EMC slots. This implies more
EMC misses and potentially EMC thrashing causing costly DPCLS lookups.
3. The 256 extra megaflow entries per bond for dp_hash bond selection
put additional load on the revalidation threads.
Owing to this performance degradation, deployments stick to “balance-slb”
bond mode even though it does not do active-active load balancing for
VXLAN- and GRE-tunnelled traffic because all tunnel packet have the
same source MAC address.
Proposed optimization:
This proposal introduces a new load-balancing output action instead of
recirculation.
Maintain one table per-bond (could just be an array of uint16's) and
program it the same way internal flows are created today for each
possible hash value (256 entries) from ofproto layer. Use this table to
load-balance flows as part of output action processing.
Currently xlate_normal() -> output_normal() ->
bond_update_post_recirc_rules() -> bond_may_recirc() and
compose_output_action__() generate 'dp_hash(hash_l4(0))' and
'recirc(<RecircID>)' actions. In this case the RecircID identifies the
bond. For the recirculated packets the ofproto layer installs megaflow
entries that match on RecircID and masked dp_hash and send them to the
corresponding output port.
Instead, we will now generate action as
'lb_output(<bond id>)'
This combines hash computation (only if needed, else re-use RSS hash)
and inline load-balancing over the bond. This action is used *only* for
balance-tcp bonds in userspace datapath (the OVS kernel datapath
remains unchanged).
Example:
Current scheme:
With 8 UDP flows (with random UDP src port):
flow-dump from pmd on cpu core: 2
recirc_id(0),in_port(7),<...> actions:hash(hash_l4(0)),recirc(0x1)
recirc_id(0x1),dp_hash(0xf8e02b7e/0xff),<...> actions:2
recirc_id(0x1),dp_hash(0xb236c260/0xff),<...> actions:1
recirc_id(0x1),dp_hash(0x7d89eb18/0xff),<...> actions:1
recirc_id(0x1),dp_hash(0xa78d75df/0xff),<...> actions:2
recirc_id(0x1),dp_hash(0xb58d846f/0xff),<...> actions:2
recirc_id(0x1),dp_hash(0x24534406/0xff),<...> actions:1
recirc_id(0x1),dp_hash(0x3cf32550/0xff),<...> actions:1
New scheme:
We can do with a single flow entry (for any number of new flows):
in_port(7),<...> actions:lb_output(1)
A new CLI has been added to dump datapath bond cache as given below.
# ovs-appctl dpif-netdev/bond-show [dp]
Bond cache:
bond-id 1 :
bucket 0 - slave 2
bucket 1 - slave 1
bucket 2 - slave 2
bucket 3 - slave 1
Co-authored-by: Manohar Krishnappa Chidambaraswamy <manukc@gmail.com>
Signed-off-by: Manohar Krishnappa Chidambaraswamy <manukc@gmail.com>
Signed-off-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
Tested-by: Matteo Croce <mcroce@redhat.com>
Tested-by: Adrian Moreno <amorenoz@redhat.com>
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2020-05-22 10:50:05 +02:00
|
|
|
case OVS_ACTION_ATTR_LB_OUTPUT:
|
2018-05-25 17:11:07 -07:00
|
|
|
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_PUSH_ETH:
|
|
|
|
case OVS_ACTION_ATTR_POP_ETH:
|
|
|
|
/* TODO: SFlow does not currently define a MAC-in-MAC
|
|
|
|
* encapsulation structure. We could use an extension
|
|
|
|
* structure to report this.
|
|
|
|
*/
|
|
|
|
break;
|
2018-01-09 19:54:31 +01:00
|
|
|
case OVS_ACTION_ATTR_CLONE:
|
|
|
|
if (flow != NULL) {
|
|
|
|
dpif_sflow_read_actions(flow, nl_attr_get(a), nl_attr_get_size(a),
|
|
|
|
sflow_actions, false);
|
|
|
|
}
|
|
|
|
break;
|
2018-05-25 17:11:07 -07:00
|
|
|
case OVS_ACTION_ATTR_SAMPLE:
|
2018-01-06 13:47:51 +08:00
|
|
|
case OVS_ACTION_ATTR_PUSH_NSH:
|
|
|
|
case OVS_ACTION_ATTR_POP_NSH:
|
2018-05-25 17:11:07 -07:00
|
|
|
case OVS_ACTION_ATTR_UNSPEC:
|
Add a new OVS action check_pkt_larger
This patch adds a new action 'check_pkt_larger' which checks if the
packet is larger than the given size and stores the result in the
destination register.
Usage: check_pkt_larger(len)->REGISTER
Eg. match=...,actions=check_pkt_larger(1442)->NXM_NX_REG0[0],next;
This patch makes use of the new datapath action - 'check_pkt_len'
which was recently added in the commit [1].
At the start of ovs-vswitchd, datapath is probed for this action.
If the datapath action is present, then 'check_pkt_larger'
makes use of this datapath action.
Datapath action 'check_pkt_len' takes these nlattrs
* OVS_CHECK_PKT_LEN_ATTR_PKT_LEN - 'pkt_len' to check for
* OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER (optional) - Nested actions
to apply if the packet length is greater than the specified 'pkt_len'
* OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL (optional) - Nested
actions to apply if the packet length is lesser or equal to the
specified 'pkt_len'.
Let's say we have these flows added to an OVS bridge br-int
table=0, priority=100 in_port=1,ip,actions=check_pkt_larger:100->NXM_NX_REG0[0],resubmit(,1)
table=1, priority=200,in_port=1,ip,reg0=0x1/0x1 actions=output:3
table=1, priority=100,in_port=1,ip,actions=output:4
Then the action 'check_pkt_larger' will be translated as
- check_pkt_len(size=100,gt(3),le(4))
datapath will check the packet length and if the packet length is greater than 100,
it will output to port 3, else it will output to port 4.
In case, datapath doesn't support 'check_pkt_len' action, the OVS action
'check_pkt_larger' sets SLOW_ACTION so that datapath flow is not added.
This OVS action is intended to be used by OVN to check the packet length
and generate an ICMP packet with type 3, code 4 and next hop mtu
in the logical router pipeline if the MTU of the physical interface
is lesser than the packet length. More information can be found here [2]
[1] - https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/4d5ec89fc8d14dcdab7214a0c13a1c7321dc6ea9
[2] - https://mail.openvswitch.org/pipermail/ovs-discuss/2018-July/047039.html
Reported-at:
https://mail.openvswitch.org/pipermail/ovs-discuss/2018-July/047039.html
Suggested-by: Ben Pfaff <blp@ovn.org>
Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
CC: Ben Pfaff <blp@ovn.org>
CC: Gregory Rose <gvrose8192@gmail.com>
Acked-by: Mark Michelson <mmichels@redhat.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2019-04-23 00:53:38 +05:30
|
|
|
case OVS_ACTION_ATTR_CHECK_PKT_LEN:
|
2019-12-18 05:48:12 +01:00
|
|
|
case OVS_ACTION_ATTR_DROP:
|
2021-11-29 11:52:05 +05:30
|
|
|
case OVS_ACTION_ATTR_ADD_MPLS:
|
2024-04-03 10:35:28 -04:00
|
|
|
case OVS_ACTION_ATTR_DEC_TTL:
|
2024-07-13 23:23:38 +02:00
|
|
|
case OVS_ACTION_ATTR_PSAMPLE:
|
2018-05-25 17:11:07 -07:00
|
|
|
case __OVS_ACTION_ATTR_MAX:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2018-01-04 12:37:57 -08:00
|
|
|
dpif_sflow_cookie_num_outputs(const struct user_action_cookie *cookie)
|
2015-07-17 21:37:02 -07:00
|
|
|
{
|
|
|
|
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,
|
2018-01-04 12:37:57 -08:00
|
|
|
const struct flow *flow, odp_port_t odp_in_port,
|
|
|
|
const struct 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);
|
2021-04-19 10:26:04 +03:00
|
|
|
if (!ds->sflow_agent || !ds->sflow_agent->samplers) {
|
2013-07-22 12:32:19 -07:00
|
|
|
goto out;
|
2011-09-28 10:43:07 -07:00
|
|
|
}
|
2021-04-19 10:26:04 +03:00
|
|
|
sampler = ds->sflow_agent->samplers;
|
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
|
|
|
}
|
|
|
|
|
2018-01-04 12:37:57 -08: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. */
|
2013-04-30 22:38:53 -07:00
|
|
|
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;
|
2017-03-01 17:47:59 -05:00
|
|
|
switchElem.flowType.sw.src_vlan = vlan_tci_to_vid(flow->vlans[0].tci);
|
|
|
|
switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow->vlans[0].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) {
|
2018-05-25 17:11:07 -07:00
|
|
|
memset(&tnlInElem, 0, sizeof(tnlInElem));
|
|
|
|
tnlInElem.tag = SFLFLOW_EX_IPV4_TUNNEL_INGRESS;
|
|
|
|
tnlInProto = in_dsp ? dpif_sflow_tunnel_proto(in_dsp->tunnel_type) : 0;
|
|
|
|
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);
|
|
|
|
}
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Output tunnel. */
|
|
|
|
if (sflow_actions
|
2018-05-25 17:11:07 -07:00
|
|
|
&& 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);
|
|
|
|
}
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* MPLS output label stack. */
|
|
|
|
if (sflow_actions
|
2018-05-25 17:11:07 -07:00
|
|
|
&& 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);
|
2015-07-17 21:37:02 -07:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|