/*
- * Copyright (c) 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "collectors.h"
#include "flow.h"
#include "hash.h"
-#include "hmap.h"
+#include "openvswitch/hmap.h"
#include "netdev.h"
#include "openvswitch/list.h"
#include "openvswitch/ofpbuf.h"
struct ovs_list cache_flow_start_timestamp_list; /* ipfix_flow_cache_entry. */
uint32_t cache_active_timeout; /* In seconds. */
uint32_t cache_max_flows;
+ char *virtual_obs_id;
+ uint8_t virtual_obs_len;
ofproto_ipfix_stats stats;
};
});
BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_ip) == 32);
+/*
+ * Refer to RFC 7011, the length of Variable length element is 0~65535:
+ * In most case, it should be less than 255 octets:
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Length (< 255)| Information Element |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ... continuing as needed |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * When it is greater than or equeal to 255 octets:
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 255 | Length (0 to 65535) | IE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ... continuing as needed |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * Now, only the virtual_obs_id whose length < 255 is implemented.
+ */
+
+#define IPFIX_VIRTUAL_OBS_MAX_LEN 254
+
/*
* support tunnel key for:
* VxLAN: 24-bit VIN,
static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool);
+static bool
+nullable_string_is_equal(const char *a, const char *b)
+{
+ return a ? b && !strcmp(a, b) : !b;
+}
+
static bool
ofproto_ipfix_bridge_exporter_options_equal(
const struct ofproto_ipfix_bridge_exporter_options *a,
&& a->enable_tunnel_sampling == b->enable_tunnel_sampling
&& a->enable_input_sampling == b->enable_input_sampling
&& a->enable_output_sampling == b->enable_output_sampling
- && sset_equals(&a->targets, &b->targets));
+ && sset_equals(&a->targets, &b->targets)
+ && nullable_string_is_equal(a->virtual_obs_id, b->virtual_obs_id));
}
static struct ofproto_ipfix_bridge_exporter_options *
struct ofproto_ipfix_bridge_exporter_options *new =
xmemdup(old, sizeof *old);
sset_clone(&new->targets, &old->targets);
+ new->virtual_obs_id = nullable_xstrdup(old->virtual_obs_id);
return new;
}
{
if (options) {
sset_destroy(&options->targets);
+ free(options->virtual_obs_id);
free(options);
}
}
return (a->collector_set_id == b->collector_set_id
&& a->cache_active_timeout == b->cache_active_timeout
&& a->cache_max_flows == b->cache_max_flows
- && sset_equals(&a->targets, &b->targets));
+ && a->enable_tunnel_sampling == b->enable_tunnel_sampling
+ && sset_equals(&a->targets, &b->targets)
+ && nullable_string_is_equal(a->virtual_obs_id, b->virtual_obs_id));
}
static struct ofproto_ipfix_flow_exporter_options *
struct ofproto_ipfix_flow_exporter_options *new =
xmemdup(old, sizeof *old);
sset_clone(&new->targets, &old->targets);
+ new->virtual_obs_id = nullable_xstrdup(old->virtual_obs_id);
return new;
}
{
if (options) {
sset_destroy(&options->targets);
+ free(options->virtual_obs_id);
free(options);
}
}
{
exporter->collectors = NULL;
exporter->seq_number = 1;
- exporter->last_template_set_time = TIME_MIN;
+ exporter->last_template_set_time = 0;
hmap_init(&exporter->cache_flow_key_map);
ovs_list_init(&exporter->cache_flow_start_timestamp_list);
exporter->cache_active_timeout = 0;
exporter->cache_max_flows = 0;
+ exporter->virtual_obs_id = NULL;
+ exporter->virtual_obs_len = 0;
}
static void
collectors_destroy(exporter->collectors);
exporter->collectors = NULL;
exporter->seq_number = 1;
- exporter->last_template_set_time = TIME_MIN;
+ exporter->last_template_set_time = 0;
exporter->cache_active_timeout = 0;
exporter->cache_max_flows = 0;
+ free(exporter->virtual_obs_id);
+ exporter->virtual_obs_id = NULL;
+ exporter->virtual_obs_len = 0;
}
static void
dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
const struct sset *targets,
const uint32_t cache_active_timeout,
- const uint32_t cache_max_flows)
+ const uint32_t cache_max_flows,
+ const char *virtual_obs_id)
{
+ size_t virtual_obs_len;
collectors_destroy(exporter->collectors);
collectors_create(targets, IPFIX_DEFAULT_COLLECTOR_PORT,
&exporter->collectors);
}
exporter->cache_active_timeout = cache_active_timeout;
exporter->cache_max_flows = cache_max_flows;
+ virtual_obs_len = virtual_obs_id ? strlen(virtual_obs_id) : 0;
+ if (virtual_obs_len > IPFIX_VIRTUAL_OBS_MAX_LEN) {
+ VLOG_WARN_RL(&rl, "Virtual obsevation ID too long (%d bytes), "
+ "should not be longer than %d bytes.",
+ exporter->virtual_obs_len, IPFIX_VIRTUAL_OBS_MAX_LEN);
+ dpif_ipfix_exporter_clear(exporter);
+ return false;
+ }
+ exporter->virtual_obs_len = virtual_obs_len;
+ exporter->virtual_obs_id = nullable_xstrdup(virtual_obs_id);
return true;
}
< sset_count(&options->targets)) {
if (!dpif_ipfix_exporter_set_options(
&exporter->exporter, &options->targets,
- options->cache_active_timeout, options->cache_max_flows)) {
+ options->cache_active_timeout, options->cache_max_flows,
+ options->virtual_obs_id)) {
return;
}
}
< sset_count(&options->targets)) {
if (!dpif_ipfix_exporter_set_options(
&exporter->exporter, &options->targets,
- options->cache_active_timeout, options->cache_max_flows)) {
+ options->cache_active_timeout, options->cache_max_flows,
+ options->virtual_obs_id)) {
return false;
}
}
return ret;
}
+bool
+dpif_ipfix_get_flow_exporter_tunnel_sampling(const struct dpif_ipfix *di,
+ const uint32_t collector_set_id)
+ OVS_EXCLUDED(mutex)
+{
+ ovs_mutex_lock(&mutex);
+ struct dpif_ipfix_flow_exporter_map_node *node
+ = dpif_ipfix_find_flow_exporter_map_node(di, collector_set_id);
+ bool ret = (node
+ && node->exporter.options
+ && node->exporter.options->enable_tunnel_sampling);
+ ovs_mutex_unlock(&mutex);
+
+ return ret;
+}
+
static void
dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex)
{
static uint16_t
ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
enum ipfix_proto_l4 l4, enum ipfix_proto_tunnel tunnel,
+ bool virtual_obs_id_set,
struct dp_packet *msg)
{
uint16_t count = 0;
DEF(TUNNEL_KEY);
}
- /* 2. Flow aggregated data. */
+ /* 2. Virtual observation ID, which is not a part of flow key. */
+ if (virtual_obs_id_set) {
+ DEF(VIRTUAL_OBS_ID);
+ }
+
+ /* 3. Flow aggregated data. */
DEF(FLOW_START_DELTA_MICROSECONDS);
DEF(FLOW_END_DELTA_MICROSECONDS);
DEF(MINIMUM_IP_TOTAL_LENGTH);
DEF(MAXIMUM_IP_TOTAL_LENGTH);
}
-
-
#undef DEF
return count;
tmpl_hdr = dp_packet_put_zeros(&msg, sizeof *tmpl_hdr);
tmpl_hdr->template_id = htons(
ipfix_get_template_id(l2, l3, l4, tunnel));
- field_count =
- ipfix_define_template_fields(l2, l3, l4, tunnel, &msg);
+ field_count = ipfix_define_template_fields(
+ l2, l3, l4, tunnel, exporter->virtual_obs_id != NULL,
+ &msg);
tmpl_hdr = (struct ipfix_template_record_header*)
((uint8_t*)dp_packet_data(&msg) + tmpl_hdr_offset);
tmpl_hdr->field_count = htons(field_count);
ipfix_put_data_set(uint32_t export_time_sec,
struct ipfix_flow_cache_entry *entry,
enum ipfix_flow_end_reason flow_end_reason,
+ const char *virtual_obs_id,
+ uint8_t virtual_obs_len,
struct dp_packet *msg)
{
size_t set_hdr_offset;
dp_packet_put(msg, entry->flow_key.flow_key_msg_part,
entry->flow_key.flow_key_msg_part_size);
+ /* Export virtual observation ID. */
+ if (virtual_obs_id) {
+ dp_packet_put(msg, &virtual_obs_len, sizeof(virtual_obs_len));
+ dp_packet_put(msg, virtual_obs_id, virtual_obs_len);
+ }
+
/* Put the non-key part of the data record. */
{
ipfix_init_header(export_time_sec, exporter->seq_number++,
entry->flow_key.obs_domain_id, &msg);
- ipfix_put_data_set(export_time_sec, entry, flow_end_reason, &msg);
+ ipfix_put_data_set(export_time_sec, entry, flow_end_reason,
+ exporter->virtual_obs_id, exporter->virtual_obs_len,
+ &msg);
tx_errors = ipfix_send_msg(exporter->collectors, &msg);
dp_packet_uninit(&msg);
void
dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
- const struct flow *flow, uint32_t collector_set_id,
- uint16_t probability, uint32_t obs_domain_id,
- uint32_t obs_point_id) OVS_EXCLUDED(mutex)
+ const struct flow *flow,
+ const union user_action_cookie *cookie,
+ odp_port_t input_odp_port,
+ const struct flow_tnl *output_tunnel_key)
+ OVS_EXCLUDED(mutex)
{
struct dpif_ipfix_flow_exporter_map_node *node;
+ const struct flow_tnl *tunnel_key = NULL;
+ struct dpif_ipfix_port * tunnel_port = NULL;
+ odp_port_t output_odp_port = cookie->flow_sample.output_odp_port;
+ uint32_t collector_set_id = cookie->flow_sample.collector_set_id;
+ uint16_t probability = cookie->flow_sample.probability;
+
/* Use the sampling probability as an approximation of the number
* of matched packets. */
uint64_t packet_delta_count = USHRT_MAX / probability;
ovs_mutex_lock(&mutex);
node = dpif_ipfix_find_flow_exporter_map_node(di, collector_set_id);
if (node) {
+ if (node->exporter.options->enable_tunnel_sampling) {
+ if (output_odp_port == ODPP_NONE && flow->tunnel.ip_dst) {
+ /* Input tunnel. */
+ tunnel_key = &flow->tunnel;
+ tunnel_port = dpif_ipfix_find_port(di, input_odp_port);
+ }
+ if (output_odp_port != ODPP_NONE && output_tunnel_key) {
+ /* Output tunnel, output_tunnel_key must be valid. */
+ tunnel_key = output_tunnel_key;
+ tunnel_port = dpif_ipfix_find_port(di, output_odp_port);
+ }
+ }
+
dpif_ipfix_sample(&node->exporter.exporter, packet, flow,
- packet_delta_count, obs_domain_id, obs_point_id,
- ODPP_NONE, NULL, NULL);
+ packet_delta_count,
+ cookie->flow_sample.obs_domain_id,
+ cookie->flow_sample.obs_point_id,
+ output_odp_port, tunnel_port, tunnel_key);
}
ovs_mutex_unlock(&mutex);
}