2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 09:58:01 +00:00

ipfix: Export user specified virtual observation ID

In virtual network, users want more info about the virtual point to observe the traffic.
It should be a string to provide clear info, not a simple interger ID.

Introduce "other-config: virtual_obs_id" in IPFIX, which is a string configured by user.
Introduce an enterprise IPFIX entity "virtualObsID"(898) to export the value. The entity is a
variable-length string.

Signed-off-by: Wenyu Zhang <wenyuz@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Wenyu Zhang 2016-06-24 17:10:07 -07:00 committed by Ben Pfaff
parent 7a9d65d294
commit c97320eb7d
6 changed files with 147 additions and 15 deletions

11
NEWS
View File

@ -15,16 +15,19 @@ Post-v2.5.0
now implemented. Only flow mod and port mod messages are supported now implemented. Only flow mod and port mod messages are supported
in bundles. in bundles.
* New OpenFlow extension NXM_NX_MPLS_TTL to provide access to MPLS TTL. * New OpenFlow extension NXM_NX_MPLS_TTL to provide access to MPLS TTL.
* New "sampling_port" option for "sample" action to allow sampling
ingress and egress tunnel metadata with IPFIX.
* New output option, output(port=N,max_len=M), to allow truncating a * New output option, output(port=N,max_len=M), to allow truncating a
packet to size M bytes when outputting to port N. packet to size M bytes when outputting to port N.
- ovs-ofctl: - ovs-ofctl:
* queue-get-config command now allows a queue ID to be specified. * queue-get-config command now allows a queue ID to be specified.
* '--bundle' option can now be used with OpenFlow 1.3. * '--bundle' option can now be used with OpenFlow 1.3.
* New option "--color" to produce colorized output for some commands. * New option "--color" to produce colorized output for some commands.
* New commands "dump-ipfix-bridge" and "dump-ipfix-flow" to dump bridge - IPFIX:
IPFIX statistics and flow based IPFIX statistics. * New "sampling_port" option for "sample" action to allow sampling
ingress and egress tunnel metadata with IPFIX.
* New ovs-ofctl commands "dump-ipfix-bridge" and "dump-ipfix-flow" to
dump bridge IPFIX statistics and flow based IPFIX statistics.
* New setting other-config:virtual_obs_id to add an arbitrary string
to IPFIX records.
- Linux: - Linux:
* New QoS type "linux-noop" that prevents Open vSwitch from trying to * New QoS type "linux-noop" that prevents Open vSwitch from trying to
manage QoS for a given port (useful when other software manages QoS). manage QoS for a given port (useful when other software manages QoS).

View File

@ -12,5 +12,6 @@ IPFIX_ENTERPRISE_ENTITY(TUNNEL_DESTINATION_IPV4_ADDRESS, 894, 4, tunnelDestinati
IPFIX_ENTERPRISE_ENTITY(TUNNEL_PROTOCOL_IDENTIFIER, 895, 1, tunnelProtocolIdentifier, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_PROTOCOL_IDENTIFIER, 895, 1, tunnelProtocolIdentifier, IPFIX_ENTERPRISE_VMWARE)
IPFIX_ENTERPRISE_ENTITY(TUNNEL_SOURCE_TRANSPORT_PORT, 896, 2, tunnelSourceTransportPort, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_SOURCE_TRANSPORT_PORT, 896, 2, tunnelSourceTransportPort, IPFIX_ENTERPRISE_VMWARE)
IPFIX_ENTERPRISE_ENTITY(TUNNEL_DESTINATION_TRANSPORT_PORT, 897, 2, tunnelDestinationTransportPort, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_DESTINATION_TRANSPORT_PORT, 897, 2, tunnelDestinationTransportPort, IPFIX_ENTERPRISE_VMWARE)
IPFIX_ENTERPRISE_ENTITY(VIRTUAL_OBS_ID, 898, 0, virtualObsID, IPFIX_ENTERPRISE_VMWARE)
#undef IPFIX_ENTERPRISE_ENTITY #undef IPFIX_ENTERPRISE_ENTITY

View File

@ -101,6 +101,8 @@ struct dpif_ipfix_exporter {
struct ovs_list cache_flow_start_timestamp_list; /* ipfix_flow_cache_entry. */ struct ovs_list cache_flow_start_timestamp_list; /* ipfix_flow_cache_entry. */
uint32_t cache_active_timeout; /* In seconds. */ uint32_t cache_active_timeout; /* In seconds. */
uint32_t cache_max_flows; uint32_t cache_max_flows;
char *virtual_obs_id;
uint8_t virtual_obs_len;
ofproto_ipfix_stats stats; ofproto_ipfix_stats stats;
}; };
@ -366,6 +368,32 @@ struct ipfix_data_record_aggregated_ip {
}); });
BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_ip) == 32); 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: * support tunnel key for:
* VxLAN: 24-bit VIN, * VxLAN: 24-bit VIN,
@ -435,6 +463,18 @@ static void get_export_time_now(uint64_t *, uint32_t *);
static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool); 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 char *
nullable_xstrdup(const char *s)
{
return s ? xstrdup(s) : NULL;
}
static bool static bool
ofproto_ipfix_bridge_exporter_options_equal( ofproto_ipfix_bridge_exporter_options_equal(
const struct ofproto_ipfix_bridge_exporter_options *a, const struct ofproto_ipfix_bridge_exporter_options *a,
@ -448,7 +488,8 @@ ofproto_ipfix_bridge_exporter_options_equal(
&& a->enable_tunnel_sampling == b->enable_tunnel_sampling && a->enable_tunnel_sampling == b->enable_tunnel_sampling
&& a->enable_input_sampling == b->enable_input_sampling && a->enable_input_sampling == b->enable_input_sampling
&& a->enable_output_sampling == b->enable_output_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 * static struct ofproto_ipfix_bridge_exporter_options *
@ -458,6 +499,7 @@ ofproto_ipfix_bridge_exporter_options_clone(
struct ofproto_ipfix_bridge_exporter_options *new = struct ofproto_ipfix_bridge_exporter_options *new =
xmemdup(old, sizeof *old); xmemdup(old, sizeof *old);
sset_clone(&new->targets, &old->targets); sset_clone(&new->targets, &old->targets);
new->virtual_obs_id = nullable_xstrdup(old->virtual_obs_id);
return new; return new;
} }
@ -467,6 +509,7 @@ ofproto_ipfix_bridge_exporter_options_destroy(
{ {
if (options) { if (options) {
sset_destroy(&options->targets); sset_destroy(&options->targets);
free(options->virtual_obs_id);
free(options); free(options);
} }
} }
@ -480,7 +523,8 @@ ofproto_ipfix_flow_exporter_options_equal(
&& a->cache_active_timeout == b->cache_active_timeout && a->cache_active_timeout == b->cache_active_timeout
&& a->cache_max_flows == b->cache_max_flows && a->cache_max_flows == b->cache_max_flows
&& a->enable_tunnel_sampling == b->enable_tunnel_sampling && a->enable_tunnel_sampling == b->enable_tunnel_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_flow_exporter_options * static struct ofproto_ipfix_flow_exporter_options *
@ -490,6 +534,7 @@ ofproto_ipfix_flow_exporter_options_clone(
struct ofproto_ipfix_flow_exporter_options *new = struct ofproto_ipfix_flow_exporter_options *new =
xmemdup(old, sizeof *old); xmemdup(old, sizeof *old);
sset_clone(&new->targets, &old->targets); sset_clone(&new->targets, &old->targets);
new->virtual_obs_id = nullable_xstrdup(old->virtual_obs_id);
return new; return new;
} }
@ -499,6 +544,7 @@ ofproto_ipfix_flow_exporter_options_destroy(
{ {
if (options) { if (options) {
sset_destroy(&options->targets); sset_destroy(&options->targets);
free(options->virtual_obs_id);
free(options); free(options);
} }
} }
@ -513,6 +559,8 @@ dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
ovs_list_init(&exporter->cache_flow_start_timestamp_list); ovs_list_init(&exporter->cache_flow_start_timestamp_list);
exporter->cache_active_timeout = 0; exporter->cache_active_timeout = 0;
exporter->cache_max_flows = 0; exporter->cache_max_flows = 0;
exporter->virtual_obs_id = NULL;
exporter->virtual_obs_len = 0;
} }
static void static void
@ -527,6 +575,9 @@ dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
exporter->last_template_set_time = 0; exporter->last_template_set_time = 0;
exporter->cache_active_timeout = 0; exporter->cache_active_timeout = 0;
exporter->cache_max_flows = 0; exporter->cache_max_flows = 0;
free(exporter->virtual_obs_id);
exporter->virtual_obs_id = NULL;
exporter->virtual_obs_len = 0;
} }
static void static void
@ -540,8 +591,10 @@ static bool
dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter, dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
const struct sset *targets, const struct sset *targets,
const uint32_t cache_active_timeout, 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_destroy(exporter->collectors);
collectors_create(targets, IPFIX_DEFAULT_COLLECTOR_PORT, collectors_create(targets, IPFIX_DEFAULT_COLLECTOR_PORT,
&exporter->collectors); &exporter->collectors);
@ -553,6 +606,16 @@ dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
} }
exporter->cache_active_timeout = cache_active_timeout; exporter->cache_active_timeout = cache_active_timeout;
exporter->cache_max_flows = cache_max_flows; 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; return true;
} }
@ -707,7 +770,8 @@ dpif_ipfix_bridge_exporter_set_options(
< sset_count(&options->targets)) { < sset_count(&options->targets)) {
if (!dpif_ipfix_exporter_set_options( if (!dpif_ipfix_exporter_set_options(
&exporter->exporter, &options->targets, &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; return;
} }
} }
@ -795,7 +859,8 @@ dpif_ipfix_flow_exporter_set_options(
< sset_count(&options->targets)) { < sset_count(&options->targets)) {
if (!dpif_ipfix_exporter_set_options( if (!dpif_ipfix_exporter_set_options(
&exporter->exporter, &options->targets, &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 false;
} }
} }
@ -1074,6 +1139,7 @@ ipfix_define_template_entity(enum ipfix_entity_id id,
static uint16_t static uint16_t
ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3, ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
enum ipfix_proto_l4 l4, enum ipfix_proto_tunnel tunnel, enum ipfix_proto_l4 l4, enum ipfix_proto_tunnel tunnel,
bool virtual_obs_id_set,
struct dp_packet *msg) struct dp_packet *msg)
{ {
uint16_t count = 0; uint16_t count = 0;
@ -1145,7 +1211,12 @@ ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
DEF(TUNNEL_KEY); 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_START_DELTA_MICROSECONDS);
DEF(FLOW_END_DELTA_MICROSECONDS); DEF(FLOW_END_DELTA_MICROSECONDS);
@ -1159,8 +1230,6 @@ ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
DEF(MINIMUM_IP_TOTAL_LENGTH); DEF(MINIMUM_IP_TOTAL_LENGTH);
DEF(MAXIMUM_IP_TOTAL_LENGTH); DEF(MAXIMUM_IP_TOTAL_LENGTH);
} }
#undef DEF #undef DEF
return count; return count;
@ -1253,8 +1322,9 @@ ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter,
tmpl_hdr = dp_packet_put_zeros(&msg, sizeof *tmpl_hdr); tmpl_hdr = dp_packet_put_zeros(&msg, sizeof *tmpl_hdr);
tmpl_hdr->template_id = htons( tmpl_hdr->template_id = htons(
ipfix_get_template_id(l2, l3, l4, tunnel)); ipfix_get_template_id(l2, l3, l4, tunnel));
field_count = field_count = ipfix_define_template_fields(
ipfix_define_template_fields(l2, l3, l4, tunnel, &msg); l2, l3, l4, tunnel, exporter->virtual_obs_id != NULL,
&msg);
tmpl_hdr = (struct ipfix_template_record_header*) tmpl_hdr = (struct ipfix_template_record_header*)
((uint8_t*)dp_packet_data(&msg) + tmpl_hdr_offset); ((uint8_t*)dp_packet_data(&msg) + tmpl_hdr_offset);
tmpl_hdr->field_count = htons(field_count); tmpl_hdr->field_count = htons(field_count);
@ -1738,6 +1808,8 @@ static void
ipfix_put_data_set(uint32_t export_time_sec, ipfix_put_data_set(uint32_t export_time_sec,
struct ipfix_flow_cache_entry *entry, struct ipfix_flow_cache_entry *entry,
enum ipfix_flow_end_reason flow_end_reason, enum ipfix_flow_end_reason flow_end_reason,
const char *virtual_obs_id,
uint8_t virtual_obs_len,
struct dp_packet *msg) struct dp_packet *msg)
{ {
size_t set_hdr_offset; size_t set_hdr_offset;
@ -1754,6 +1826,12 @@ ipfix_put_data_set(uint32_t export_time_sec,
dp_packet_put(msg, entry->flow_key.flow_key_msg_part, dp_packet_put(msg, entry->flow_key.flow_key_msg_part,
entry->flow_key.flow_key_msg_part_size); 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. */ /* Put the non-key part of the data record. */
{ {
@ -1816,7 +1894,9 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter,
ipfix_init_header(export_time_sec, exporter->seq_number++, ipfix_init_header(export_time_sec, exporter->seq_number++,
entry->flow_key.obs_domain_id, &msg); 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); tx_errors = ipfix_send_msg(exporter->collectors, &msg);
dp_packet_uninit(&msg); dp_packet_uninit(&msg);

View File

@ -82,6 +82,7 @@ struct ofproto_ipfix_bridge_exporter_options {
bool enable_tunnel_sampling; bool enable_tunnel_sampling;
bool enable_input_sampling; bool enable_input_sampling;
bool enable_output_sampling; bool enable_output_sampling;
char *virtual_obs_id;
}; };
struct ofproto_ipfix_flow_exporter_options { struct ofproto_ipfix_flow_exporter_options {
@ -90,6 +91,7 @@ struct ofproto_ipfix_flow_exporter_options {
uint32_t cache_active_timeout; uint32_t cache_active_timeout;
uint32_t cache_max_flows; uint32_t cache_max_flows;
bool enable_tunnel_sampling; bool enable_tunnel_sampling;
char *virtual_obs_id;
}; };
struct ofproto_rstp_status { struct ofproto_rstp_status {

View File

@ -1165,6 +1165,7 @@ bridge_configure_ipfix(struct bridge *br)
struct ofproto_ipfix_bridge_exporter_options be_opts; struct ofproto_ipfix_bridge_exporter_options be_opts;
struct ofproto_ipfix_flow_exporter_options *fe_opts = NULL; struct ofproto_ipfix_flow_exporter_options *fe_opts = NULL;
size_t n_fe_opts = 0; size_t n_fe_opts = 0;
const char *virtual_obs_id;
OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH(fe_cfg, idl) { OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH(fe_cfg, idl) {
if (ovsrec_fscs_is_valid(fe_cfg, br)) { if (ovsrec_fscs_is_valid(fe_cfg, br)) {
@ -1209,6 +1210,11 @@ bridge_configure_ipfix(struct bridge *br)
be_opts.enable_output_sampling = !smap_get_bool(&be_cfg->other_config, be_opts.enable_output_sampling = !smap_get_bool(&be_cfg->other_config,
"enable-output-sampling", false); "enable-output-sampling", false);
virtual_obs_id = smap_get(&be_cfg->other_config, "virtual_obs_id");
be_opts.virtual_obs_id = (virtual_obs_id
? xstrdup(virtual_obs_id)
: NULL);
} }
if (n_fe_opts > 0) { if (n_fe_opts > 0) {
@ -1228,6 +1234,11 @@ bridge_configure_ipfix(struct bridge *br)
opts->enable_tunnel_sampling = smap_get_bool( opts->enable_tunnel_sampling = smap_get_bool(
&fe_cfg->ipfix->other_config, &fe_cfg->ipfix->other_config,
"enable-tunnel-sampling", true); "enable-tunnel-sampling", true);
virtual_obs_id = smap_get(&fe_cfg->ipfix->other_config,
"virtual_obs_id");
opts->virtual_obs_id = (virtual_obs_id
? xstrdup(virtual_obs_id)
: NULL);
opts++; opts++;
} }
} }
@ -1238,6 +1249,7 @@ bridge_configure_ipfix(struct bridge *br)
if (valid_be_cfg) { if (valid_be_cfg) {
sset_destroy(&be_opts.targets); sset_destroy(&be_opts.targets);
free(be_opts.virtual_obs_id);
} }
if (n_fe_opts > 0) { if (n_fe_opts > 0) {
@ -1245,6 +1257,7 @@ bridge_configure_ipfix(struct bridge *br)
size_t i; size_t i;
for (i = 0; i < n_fe_opts; i++) { for (i = 0; i < n_fe_opts; i++) {
sset_destroy(&opts->targets); sset_destroy(&opts->targets);
free(opts->virtual_obs_id);
opts++; opts++;
} }
free(fe_opts); free(fe_opts);

View File

@ -4747,6 +4747,39 @@
</p> </p>
</column> </column>
<column name="other_config" key="virtual_obs_id"
type='{"type": "string"}'>
<p>
A string that accompanies each IPFIX flow record. Its intended use is
for the ``virtual observation ID,'' an identifier of a virtual
observation point that is locally unique in a virtual network. It
describes a location in the virtual network where IP packets can be
observed. The maximum length is 254 bytes. If not specified, the
field is omitted from the IPFIX flow record.
</p>
<p>
The following enterprise entity reports the specified virtual
observation ID:
</p>
<dl>
<dt>virtualObsID:</dt>
<dd>
<p>ID: 898, and enterprise ID 6876 (VMware).</p>
<p>type: variable-length string.</p>
<p>data type semantics: identifier.</p>
<p>description: A virtual observation domain ID that is locally
unique in a virtual network.
</p>
</dd>
</dl>
<p>
This feature was introduced in Open vSwitch 2.5.90.
</p>
</column>
<group title="Per-Bridge Sampling"> <group title="Per-Bridge Sampling">
<p> <p>
These values affect only per-bridge sampling. See above for a These values affect only per-bridge sampling. See above for a