aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vnet/vnet/flow/flow_report.c159
-rw-r--r--vnet/vnet/flow/flow_report.h33
-rw-r--r--vnet/vnet/flow/flow_report_classify.c264
-rw-r--r--vnet/vnet/flow/flow_report_classify.h110
-rw-r--r--vpp-api-test/vat/api_format.c252
-rw-r--r--vpp/vpp-api/api.c221
-rw-r--r--vpp/vpp-api/custom_dump.c68
-rw-r--r--vpp/vpp-api/vpe.api114
8 files changed, 1072 insertions, 149 deletions
diff --git a/vnet/vnet/flow/flow_report.c b/vnet/vnet/flow/flow_report.c
index 38ad6136ef6..932613d338d 100644
--- a/vnet/vnet/flow/flow_report.c
+++ b/vnet/vnet/flow/flow_report.c
@@ -20,6 +20,52 @@
flow_report_main_t flow_report_main;
+static_always_inline u8 stream_index_valid (u32 index)
+{
+ flow_report_main_t * frm = &flow_report_main;
+ return index < vec_len(frm->streams) &&
+ frm->streams[index].domain_id != ~0;
+}
+
+static_always_inline flow_report_stream_t * add_stream (void)
+{
+ flow_report_main_t * frm = &flow_report_main;
+ u32 i;
+ for (i = 0; i < vec_len(frm->streams); i++)
+ if (!stream_index_valid(i))
+ return &frm->streams[i];
+ u32 index = vec_len(frm->streams);
+ vec_validate(frm->streams, index);
+ return &frm->streams[index];
+}
+
+static_always_inline void delete_stream (u32 index)
+{
+ flow_report_main_t * frm = &flow_report_main;
+ ASSERT (index < vec_len(frm->streams));
+ ASSERT (frm->streams[index].domain_id != ~0);
+ frm->streams[index].domain_id = ~0;
+}
+
+static i32 find_stream (u32 domain_id, u16 src_port)
+{
+ flow_report_main_t * frm = &flow_report_main;
+ flow_report_stream_t * stream;
+ u32 i;
+ for (i = 0; i < vec_len(frm->streams); i++)
+ if (stream_index_valid(i)) {
+ stream = &frm->streams[i];
+ if (domain_id == stream->domain_id) {
+ if (src_port != stream->src_port)
+ return -2;
+ return i;
+ } else if (src_port == stream->src_port) {
+ return -2;
+ }
+ }
+ return -1;
+}
+
int send_template_packet (flow_report_main_t *frm,
flow_report_t *fr,
u32 * buffer_indexp)
@@ -31,6 +77,7 @@ int send_template_packet (flow_report_main_t *frm,
ip4_header_t * ip;
udp_header_t * udp;
vlib_main_t * vm = frm->vlib_main;
+ flow_report_stream_t * stream;
ASSERT (buffer_indexp);
@@ -82,12 +129,22 @@ int send_template_packet (flow_report_main_t *frm,
(vlib_time_now(frm->vlib_main) - frm->vlib_time_0));
h->export_time = clib_host_to_net_u32(h->export_time);
+ stream = &frm->streams[fr->stream_index];
+
/* FIXUP: message header sequence_number. Templates do not increase it */
- h->sequence_number = clib_host_to_net_u32(fr->sequence_number);
+ h->sequence_number = clib_host_to_net_u32(stream->sequence_number);
/* FIXUP: udp length */
udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
+ if (frm->udp_checksum)
+ {
+ /* RFC 7011 section 10.3.2. */
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+ }
+
*buffer_indexp = bi0;
fr->last_template_sent = vlib_time_now (vm);
@@ -178,11 +235,19 @@ int vnet_flow_report_add_del (flow_report_main_t *frm,
int i;
int found_index = ~0;
flow_report_t *fr;
+ flow_report_stream_t * stream;
+ u32 si;
+ si = find_stream(a->domain_id, a->src_port);
+ if (si == -2)
+ return VNET_API_ERROR_INVALID_VALUE;
+ if (si == -1 && a->is_add == 0)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
for (i = 0; i < vec_len(frm->reports); i++)
{
fr = vec_elt_at_index (frm->reports, i);
- if (fr->opaque == a->opaque
+ if (fr->opaque.as_uword == a->opaque.as_uword
&& fr->rewrite_callback == a->rewrite_callback
&& fr->flow_data_callback == a->flow_data_callback)
{
@@ -196,16 +261,37 @@ int vnet_flow_report_add_del (flow_report_main_t *frm,
if (found_index != ~0)
{
vec_delete (frm->reports, 1, found_index);
+ stream = &frm->streams[si];
+ stream->n_reports--;
+ if (stream->n_reports == 0)
+ delete_stream(si);
return 0;
}
return VNET_API_ERROR_NO_SUCH_ENTRY;
}
+ if (found_index != ~0)
+ return VNET_API_ERROR_VALUE_EXIST;
+
+ if (si == -1)
+ {
+ stream = add_stream();
+ stream->domain_id = a->domain_id;
+ stream->src_port = a->src_port;
+ stream->sequence_number = 0;
+ stream->n_reports = 0;
+ si = stream - frm->streams;
+ }
+ else
+ stream = &frm->streams[si];
+
+ stream->n_reports++;
+
vec_add2 (frm->reports, fr, 1);
- fr->sequence_number = 0;
- fr->domain_id = a->domain_id;
- fr->src_port = a->src_port;
+ fr->stream_index = si;
+ fr->template_id = 256 + stream->next_template_no;
+ stream->next_template_no = (stream->next_template_no + 1) % (65536 - 256);
fr->update_rewrite = 1;
fr->opaque = a->opaque;
fr->rewrite_callback = a->rewrite_callback;
@@ -217,18 +303,51 @@ int vnet_flow_report_add_del (flow_report_main_t *frm,
void vnet_flow_reports_reset (flow_report_main_t * frm)
{
flow_report_t *fr;
+ u32 i;
+
+ for (i = 0; i < vec_len(frm->streams); i++)
+ if (stream_index_valid(i))
+ frm->streams[i].sequence_number = 0;
+
vec_foreach (fr, frm->reports)
{
- fr->sequence_number = 0;
fr->update_rewrite = 1;
fr->last_template_sent = 0;
}
}
+void vnet_stream_reset (flow_report_main_t * frm, u32 stream_index)
+{
+ flow_report_t *fr;
+
+ frm->streams[stream_index].sequence_number = 0;
+
+ vec_foreach (fr, frm->reports)
+ if (frm->reports->stream_index == stream_index) {
+ fr->update_rewrite = 1;
+ fr->last_template_sent = 0;
+ }
+}
+
+int vnet_stream_change (flow_report_main_t * frm,
+ u32 old_domain_id, u16 old_src_port,
+ u32 new_domain_id, u16 new_src_port)
+{
+ i32 stream_index = find_stream (old_domain_id, old_src_port);
+ if (stream_index < 0)
+ return 1;
+ flow_report_stream_t * stream = &frm->streams[stream_index];
+ stream->domain_id = new_domain_id;
+ stream->src_port = new_src_port;
+ if (old_domain_id != new_domain_id || old_src_port != new_src_port)
+ vnet_stream_reset (frm, stream_index);
+ return 0;
+}
+
static clib_error_t *
-set_ipfix_command_fn (vlib_main_t * vm,
- unformat_input_t * input,
- vlib_cli_command_t * cmd)
+set_ipfix_exporter_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
{
flow_report_main_t * frm = &flow_report_main;
ip4_address_t collector, src;
@@ -240,6 +359,7 @@ set_ipfix_command_fn (vlib_main_t * vm,
src.as_u32 = 0;
u32 path_mtu = 512; // RFC 7011 section 10.3.3.
u32 template_interval = 20;
+ u8 udp_checksum = 0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
if (unformat (input, "collector %U", unformat_ip4_address, &collector))
@@ -261,6 +381,8 @@ set_ipfix_command_fn (vlib_main_t * vm,
;
else if (unformat (input, "template-interval %u", &template_interval))
;
+ else if (unformat (input, "udp-checksum"))
+ udp_checksum = 1;
else
break;
}
@@ -289,13 +411,16 @@ set_ipfix_command_fn (vlib_main_t * vm,
frm->fib_index = fib_index;
frm->path_mtu = path_mtu;
frm->template_interval = template_interval;
+ frm->udp_checksum = udp_checksum;
vlib_cli_output (vm, "Collector %U, src address %U, "
"fib index %d, path MTU %u, "
- "template resend interval %us",
+ "template resend interval %us, "
+ "udp checksum %s",
format_ip4_address, &frm->ipfix_collector,
format_ip4_address, &frm->src_address,
- fib_index, path_mtu, template_interval);
+ fib_index, path_mtu, template_interval,
+ udp_checksum ? "enabled" : "disabled");
/* Turn on the flow reporting process */
vlib_process_signal_event (vm, flow_report_process_node.index,
@@ -303,14 +428,15 @@ set_ipfix_command_fn (vlib_main_t * vm,
return 0;
}
-VLIB_CLI_COMMAND (set_ipfix_command, static) = {
- .path = "set ipfix",
- .short_help = "set ipfix collector <ip4-address> "
- "[port <port>] "
+VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = {
+ .path = "set ipfix exporter",
+ .short_help = "set ipfix exporter "
+ "collector <ip4-address> [port <port>] "
"src <ip4-address> [fib-id <fib-id>] "
"[path-mtu <path-mtu>] "
"[template-interval <template-interval>]",
- .function = set_ipfix_command_fn,
+ "[udp-checksum]",
+ .function = set_ipfix_exporter_command_fn,
};
static clib_error_t *
@@ -322,6 +448,7 @@ flow_report_init (vlib_main_t *vm)
frm->vnet_main = vnet_get_main();
frm->unix_time_0 = time(0);
frm->vlib_time_0 = vlib_time_now(frm->vlib_main);
+ frm->fib_index = ~0;
return 0;
}
diff --git a/vnet/vnet/flow/flow_report.h b/vnet/vnet/flow/flow_report.h
index 27aa81ae3ca..7b9fc7b0fc7 100644
--- a/vnet/vnet/flow/flow_report.h
+++ b/vnet/vnet/flow/flow_report.h
@@ -51,12 +51,25 @@ typedef vlib_frame_t * (vnet_flow_data_callback_t) (struct flow_report_main *,
struct flow_report *,
vlib_frame_t *, u32 *,
u32);
+
+typedef union {
+ void * as_ptr;
+ uword as_uword;
+} opaque_t;
+
+typedef struct {
+ u32 domain_id;
+ u32 sequence_number;
+ u16 src_port;
+ u16 n_reports;
+ u16 next_template_no;
+} flow_report_stream_t;
+
typedef struct flow_report {
/* ipfix rewrite, set by callback */
u8 * rewrite;
- u32 sequence_number;
- u32 domain_id;
- u16 src_port;
+ u16 template_id;
+ u32 stream_index;
f64 last_template_sent;
int update_rewrite;
@@ -64,7 +77,7 @@ typedef struct flow_report {
uword * fields_to_send;
/* Opaque data */
- void * opaque;
+ opaque_t opaque;
/* build-the-rewrite callback */
vnet_flow_rewrite_callback_t *rewrite_callback;
@@ -75,6 +88,7 @@ typedef struct flow_report {
typedef struct flow_report_main {
flow_report_t * reports;
+ flow_report_stream_t * streams;
/* ipfix collector ip address, port, our ip address, fib index */
ip4_address_t ipfix_collector;
@@ -88,6 +102,9 @@ typedef struct flow_report_main {
/* time interval in seconds after which to resend templates */
u32 template_interval;
+ /* UDP checksum calculation enable flag */
+ u8 udp_checksum;
+
/* time scale transform. Joy. */
u32 unix_time_0;
f64 vlib_time_0;
@@ -106,7 +123,7 @@ int vnet_flow_report_enable_disable (u32 sw_if_index, u32 table_index,
typedef struct {
vnet_flow_data_callback_t *flow_data_callback;
vnet_flow_rewrite_callback_t *rewrite_callback;
- void * opaque;
+ opaque_t opaque;
int is_add;
u32 domain_id;
u16 src_port;
@@ -117,4 +134,10 @@ int vnet_flow_report_add_del (flow_report_main_t *frm,
void vnet_flow_reports_reset (flow_report_main_t * frm);
+void vnet_stream_reset (flow_report_main_t * frm, u32 stream_index);
+
+int vnet_stream_change (flow_report_main_t * frm,
+ u32 old_domain_id, u16 old_src_port,
+ u32 new_domain_id, u16 new_src_port);
+
#endif /* __included_vnet_flow_report_h__ */
diff --git a/vnet/vnet/flow/flow_report_classify.c b/vnet/vnet/flow/flow_report_classify.c
index f13f4fe8b87..95403793cfe 100644
--- a/vnet/vnet/flow/flow_report_classify.c
+++ b/vnet/vnet/flow/flow_report_classify.c
@@ -16,23 +16,28 @@
#include <vnet/flow/flow_report_classify.h>
#include <vnet/api_errno.h>
+/* Common prefix of tcp and udp headers
+ * containing only source and destination port fields */
typedef struct {
- u32 classify_table_index;
-} flow_report_classify_main_t;
+ u16 src_port, dst_port;
+} tcpudp_header_t;
flow_report_classify_main_t flow_report_classify_main;
-static u8 * template_rewrite (flow_report_main_t * frm,
- flow_report_t * fr,
- ip4_address_t * collector_address,
- ip4_address_t * src_address,
- u16 collector_port)
+u8 * ipfix_classify_template_rewrite (flow_report_main_t * frm,
+ flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address,
+ u16 collector_port)
{
+ flow_report_classify_main_t * fcm = &flow_report_classify_main;
vnet_classify_table_t * tblp;
vnet_classify_main_t * vcm = &vnet_classify_main;
- flow_report_classify_main_t *fcm =
- (flow_report_classify_main_t *) fr->opaque;
+ u32 flow_table_index = fr->opaque.as_uword;
+ u8 * ip_start;
ip4_header_t * ip;
+ ip6_header_t * ip6;
+ tcpudp_header_t * tcpudp;
udp_header_t * udp;
ipfix_message_header_t * h;
ipfix_set_header_t * s;
@@ -44,20 +49,28 @@ static u8 * template_rewrite (flow_report_main_t * frm,
i32 l3_offset = -2; /* sizeof (ethernet_header_t) - sizeof (u32x4) */
u32 field_count = 0;
u32 field_index = 0;
-
- tblp = pool_elt_at_index (vcm->tables, fcm->classify_table_index);
+ flow_report_stream_t * stream;
+ u8 ip_version;
+ u8 transport_protocol;
+
+ stream = &frm->streams[fr->stream_index];
+
+ ipfix_classify_table_t * table = &fcm->tables[flow_table_index];
+
+ ip_version = table->ip_version;
+ transport_protocol = table->transport_protocol;
+
+ tblp = pool_elt_at_index (vcm->tables, table->classify_table_index);
/*
* Mumble, assumes that we're not classifying on L2 or first 2 octets
* of L3..
*/
- ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
- udp = (udp_header_t *)(ip+1);
-
/* Determine field count */
+ ip_start = ((u8 *)(tblp->mask)) + l3_offset;
#define _(field,mask,item,length) \
- if ((field) == (mask)) \
+ if (memcmp(&field, &mask, length) == 0) \
{ \
field_count++; \
\
@@ -65,9 +78,9 @@ static u8 * template_rewrite (flow_report_main_t * frm,
field_index, 1); \
} \
field_index++;
-
foreach_ipfix_field;
#undef _
+
/* Add packetTotalCount manually */
field_count += 1;
@@ -92,19 +105,18 @@ static u8 * template_rewrite (flow_report_main_t * frm,
ip->protocol = IP_PROTOCOL_UDP;
ip->src_address.as_u32 = src_address->as_u32;
ip->dst_address.as_u32 = collector_address->as_u32;
- udp->src_port = clib_host_to_net_u16 (fr->src_port);
+ udp->src_port = clib_host_to_net_u16 (stream->src_port);
udp->dst_port = clib_host_to_net_u16 (collector_port);
udp->length = clib_host_to_net_u16 (vec_len(rewrite) - sizeof (*ip));
/* FIXUP: message header export_time */
/* FIXUP: message header sequence_number */
- h->domain_id = clib_host_to_net_u32 (fr->domain_id);
+ h->domain_id = clib_host_to_net_u32 (stream->domain_id);
/* Take another trip through the mask and build the template */
- ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
- udp = (udp_header_t *)(ip+1);
+ ip_start = ((u8 *)(tblp->mask)) + l3_offset;
#define _(field,mask,item,length) \
- if ((field) == (mask)) \
+ if (memcmp(&field, &mask, length) == 0) \
{ \
f->e_id_length = ipfix_e_id_length (0 /* enterprise */, \
item, length); \
@@ -123,7 +135,7 @@ static u8 * template_rewrite (flow_report_main_t * frm,
ASSERT (f - first_field);
/* Field count in this template */
- t->id_count = ipfix_id_count (256 /* template_id */, f - first_field);
+ t->id_count = ipfix_id_count (fr->template_id, f - first_field);
/* set length in octets*/
s->set_id_length = ipfix_set_id_length (2 /* set_id */, (u8 *) f - (u8 *)s);
@@ -137,16 +149,16 @@ static u8 * template_rewrite (flow_report_main_t * frm,
return rewrite;
}
-static vlib_frame_t * send_flows (flow_report_main_t * frm,
- flow_report_t * fr,
- vlib_frame_t * f, u32 * to_next,
- u32 node_index)
+vlib_frame_t * ipfix_classify_send_flows (flow_report_main_t * frm,
+ flow_report_t * fr,
+ vlib_frame_t * f,
+ u32 * to_next,
+ u32 node_index)
{
+ flow_report_classify_main_t * fcm = &flow_report_classify_main;
vnet_classify_main_t * vcm = &vnet_classify_main;
- flow_report_classify_main_t * fcm =
- (flow_report_classify_main_t *) fr->opaque;
- vnet_classify_table_t * t =
- pool_elt_at_index (vcm->tables, fcm->classify_table_index);
+ u32 flow_table_index = fr->opaque.as_uword;
+ vnet_classify_table_t * t;
vnet_classify_bucket_t * b;
vnet_classify_entry_t * v, * save_v;
vlib_buffer_t *b0 = 0;
@@ -157,14 +169,28 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
ip4_ipfix_template_packet_t * tp;
ipfix_message_header_t * h = 0;
ipfix_set_header_t * s = 0;
+ u8 * ip_start;
ip4_header_t * ip;
+ ip6_header_t * ip6;
+ tcpudp_header_t * tcpudp;
udp_header_t * udp;
int field_index;
- ip4_header_t * match;
u32 records_this_buffer;
u16 new_l0, old_l0;
ip_csum_t sum0;
vlib_main_t * vm = frm->vlib_main;
+ flow_report_stream_t * stream;
+ u8 ip_version;
+ u8 transport_protocol;
+
+ stream = &frm->streams[fr->stream_index];
+
+ ipfix_classify_table_t * table = &fcm->tables[flow_table_index];
+
+ ip_version = table->ip_version;
+ transport_protocol = table->transport_protocol;
+
+ t = pool_elt_at_index (vcm->tables, table->classify_table_index);
while (__sync_lock_test_and_set (t->writer_lock, 1))
;
@@ -216,19 +242,16 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
h->export_time = clib_host_to_net_u32(h->export_time);
/* FIXUP: message header sequence_number */
- h->sequence_number = fr->sequence_number;
+ h->sequence_number = stream->sequence_number;
h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
next_offset = (u32) (((u8 *)(s+1)) - (u8 *)tp);
record_offset = next_offset;
records_this_buffer = 0;
}
-
+
field_index = 0;
- match = (ip4_header_t *) (((u8 *)v->key) - 2);
- ip = match;
- udp = (udp_header_t * )(ip+1);
-
+ ip_start = ((u8 *)v->key) - 2;
#define _(field,mask,item,length) \
if (clib_bitmap_get (fr->fields_to_send, field_index)) \
{ \
@@ -239,7 +262,7 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
field_index++;
foreach_ipfix_field;
#undef _
-
+
/* Add packetTotalCount manually */
{
u64 packets = clib_host_to_net_u64 (v->hits);
@@ -247,7 +270,7 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
next_offset += sizeof (packets);
}
records_this_buffer++;
- fr->sequence_number++;
+ stream->sequence_number++;
/* Next record will have the same size as this record */
u32 next_record_size = next_offset - record_offset;
@@ -255,7 +278,7 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
if (next_offset + next_record_size > frm->path_mtu)
{
- s->set_id_length = ipfix_set_id_length (256 /* template ID*/,
+ s->set_id_length = ipfix_set_id_length (fr->template_id,
next_offset -
(sizeof (*ip) + sizeof (*udp) +
sizeof (*h)));
@@ -280,7 +303,15 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
ip->length = new_l0;
udp->length =
clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
-
+
+ if (frm->udp_checksum)
+ {
+ /* RFC 7011 section 10.3.2. */
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+ }
+
ASSERT (ip->checksum == ip4_header_checksum (ip));
to_next[0] = bi0;
@@ -304,7 +335,7 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
flush:
if (b0)
{
- s->set_id_length = ipfix_set_id_length (256 /* template ID*/,
+ s->set_id_length = ipfix_set_id_length (fr->template_id,
next_offset -
(sizeof (*ip) + sizeof (*udp) +
sizeof (*h)));
@@ -328,6 +359,14 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
ip->length = new_l0;
udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
+ if (frm->udp_checksum)
+ {
+ /* RFC 7011 section 10.3.2. */
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+ }
+
ASSERT (ip->checksum == ip4_header_checksum (ip));
to_next[0] = bi0;
@@ -341,47 +380,81 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
return f;
}
-
static clib_error_t *
-flow_classify_command_fn (vlib_main_t * vm,
- unformat_input_t * input,
- vlib_cli_command_t * cmd)
+ipfix_classify_table_add_del_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
{
flow_report_classify_main_t *fcm = &flow_report_classify_main;
flow_report_main_t *frm = &flow_report_main;
vnet_flow_report_add_del_args_t args;
+ ipfix_classify_table_t * table;
int rv;
- int is_add = 1;
- u32 domain_id = 0;
- u32 src_port = UDP_DST_PORT_ipfix;
+ int is_add = -1;
+ u32 classify_table_index;
+ u8 ip_version = 0;
+ u8 transport_protocol = 255;
+
+ if (fcm->src_port == 0)
+ clib_error_return (0, "call 'set ipfix classify stream' first");
- domain_id = 0;
- fcm->classify_table_index = ~0;
memset (&args, 0, sizeof (args));
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
- if (unformat (input, "table %d", &fcm->classify_table_index))
- ;
- else if (unformat (input, "domain %d", &domain_id))
- ;
- else if (unformat (input, "src-port %d", &src_port))
- ;
+ if (unformat (input, "add"))
+ is_add = 1;
else if (unformat (input, "del"))
is_add = 0;
+ else if (unformat (input, "%d", &classify_table_index))
+ ;
+ else if (unformat (input, "ip4"))
+ ip_version = 4;
+ else if (unformat (input, "ip6"))
+ ip_version = 6;
+ else if (unformat (input, "tcp"))
+ transport_protocol = 6;
+ else if (unformat (input, "udp"))
+ transport_protocol = 17;
else
return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input);
}
- if (fcm->classify_table_index == ~0)
+ if (is_add == -1)
+ return clib_error_return (0, "expecting: add|del");
+ if (classify_table_index == ~0)
return clib_error_return (0, "classifier table not specified");
+ if (ip_version == 0)
+ return clib_error_return (0, "IP version not specified");
+
+ table = 0;
+ int i;
+ for (i = 0; i < vec_len(fcm->tables); i++)
+ if (ipfix_classify_table_index_valid(i))
+ if (fcm->tables[i].classify_table_index == classify_table_index) {
+ table = &fcm->tables[i];
+ break;
+ }
+
+ if (is_add) {
+ if (table)
+ return clib_error_return (0, "Specified classifier table already used");
+ table = ipfix_classify_add_table();
+ table->classify_table_index = classify_table_index;
+ } else {
+ if (!table)
+ return clib_error_return (0, "Specified classifier table not registered");
+ }
+
+ table->ip_version = ip_version;
+ table->transport_protocol = transport_protocol;
- args.opaque = (void *) fcm;
- args.rewrite_callback = template_rewrite;
- args.flow_data_callback = send_flows;
+ args.opaque.as_uword = table - fcm->tables;
+ args.rewrite_callback = ipfix_classify_template_rewrite;
+ args.flow_data_callback = ipfix_classify_send_flows;
args.is_add = is_add;
- args.domain_id = domain_id;
- args.src_port = (u16)src_port;
+ args.domain_id = fcm->domain_id;
+ args.src_port = fcm->src_port;
rv = vnet_flow_report_add_del (frm, &args);
@@ -390,18 +463,73 @@ flow_classify_command_fn (vlib_main_t * vm,
case 0:
break;
case VNET_API_ERROR_NO_SUCH_ENTRY:
- return clib_error_return (0, "registration not found...");
+ return clib_error_return (0, "Flow report not found");
+ case VNET_API_ERROR_VALUE_EXIST:
+ return clib_error_return (0, "Flow report already exists");
+ case VNET_API_ERROR_INVALID_VALUE:
+ return clib_error_return (0, "Expecting either still unused values "
+ "for both domain_id and src_port "
+ "or already used values for both fields");
default:
return clib_error_return (0, "vnet_flow_report_add_del returned %d", rv);
}
+ if (is_add) {
+ if (rv != 0)
+ ipfix_classify_delete_table(table - fcm->tables);
+ } else {
+ if (rv == 0)
+ ipfix_classify_delete_table(table - fcm->tables);
+ }
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (ipfix_classify_table_add_del_command, static) = {
+ .path = "ipfix classify table",
+ .short_help = "ipfix classify table add|del <table-index>",
+ .function = ipfix_classify_table_add_del_command_fn,
+};
+
+static clib_error_t *
+set_ipfix_classify_stream_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ flow_report_classify_main_t *fcm = &flow_report_classify_main;
+ flow_report_main_t *frm = &flow_report_main;
+ u32 domain_id = 1;
+ u32 src_port = UDP_DST_PORT_ipfix;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
+ if (unformat (input, "domain %d", &domain_id))
+ ;
+ else if (unformat (input, "src-port %d", &src_port))
+ ;
+ else
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
+
+ if (fcm->src_port != 0 &&
+ (fcm->domain_id != domain_id ||
+ fcm->src_port != (u16)src_port)) {
+ int rv = vnet_stream_change (frm, fcm->domain_id, fcm->src_port,
+ domain_id, (u16)src_port);
+ ASSERT (rv == 0);
+ }
+
+ fcm->domain_id = domain_id;
+ fcm->src_port = (u16)src_port;
+
return 0;
}
-VLIB_CLI_COMMAND (flow_classify_command, static) = {
- .path = "flow classify",
- .short_help = "flow classify",
- .function = flow_classify_command_fn,
+VLIB_CLI_COMMAND (set_ipfix_classify_stream_command, static) = {
+ .path = "set ipfix classify stream",
+ .short_help = "set ipfix classify stream"
+ "[domain <domain-id>] [src-port <src-port>]",
+ .function = set_ipfix_classify_stream_command_fn,
};
static clib_error_t *
diff --git a/vnet/vnet/flow/flow_report_classify.h b/vnet/vnet/flow/flow_report_classify.h
index c72ef0eb301..77d98b586ca 100644
--- a/vnet/vnet/flow/flow_report_classify.h
+++ b/vnet/vnet/flow/flow_report_classify.h
@@ -15,12 +15,108 @@
#ifndef __included_flow_report_classify_h__
#define __included_flow_report_classify_h__
-/* Note: add +2 to udp (src,dst) port enum values to get TCP values */
-#define foreach_ipfix_field \
-_(ip->src_address.as_u32, 0xffffffff, sourceIPv4Address, 4) \
-_(ip->dst_address.as_u32, 0xffffffff, destinationIPv4Address, 4) \
-_(ip->protocol, 0xFF, protocolIdentifier, 1) \
-_(udp->src_port, 0xFFFF, udpSourcePort, 2) \
-_(udp->dst_port, 0xFFFF, udpDestinationPort, 2)
+#define foreach_ipfix_ip4_field \
+_(ip->src_address.as_u32, ((u32[]){0xFFFFFFFF}), sourceIPv4Address, 4) \
+_(ip->dst_address.as_u32, ((u32[]){0xFFFFFFFF}), destinationIPv4Address, 4) \
+_(ip->protocol, ((u8[]){0xFF}), protocolIdentifier, 1)
+
+#define foreach_ipfix_ip6_field \
+_(ip6->src_address.as_u8, \
+ ((u32[]){0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}), \
+ sourceIPv6Address, 16) \
+_(ip6->dst_address.as_u8, \
+ ((u32[]){0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}), \
+ destinationIPv6Address, 16) \
+_(ip6->protocol, ((u8[]){0xFF}), protocolIdentifier, 1)
+
+#define foreach_ipfix_tcpudp_field \
+_(tcpudp->src_port, ((u16[]){0xFFFF}), sourceTransportPort, 2) \
+_(tcpudp->dst_port, ((u16[]){0xFFFF}), destinationTransportPort, 2)
+
+#define foreach_ipfix_tcp_field \
+_(tcpudp->src_port, ((u16[]){0xFFFF}), tcpSourcePort, 2) \
+_(tcpudp->dst_port, ((u16[]){0xFFFF}), tcpDestinationPort, 2)
+
+#define foreach_ipfix_udp_field \
+_(tcpudp->src_port, ((u16[]){0xFFFF}), udpSourcePort, 2) \
+_(tcpudp->dst_port, ((u16[]){0xFFFF}), udpDestinationPort, 2)
+
+#define foreach_ipfix_transport_protocol_field \
+ switch (transport_protocol) { \
+ case 255: \
+ foreach_ipfix_tcpudp_field; \
+ break; \
+ case 6: \
+ foreach_ipfix_tcp_field; \
+ break; \
+ case 17: \
+ foreach_ipfix_udp_field; \
+ break; \
+ }
+
+#define foreach_ipfix_field \
+ if (ip_version == 4) { \
+ ip = (ip4_header_t *)ip_start; \
+ tcpudp = (tcpudp_header_t *)(ip+1); \
+ foreach_ipfix_ip4_field; \
+ } else { \
+ ip6 = (ip6_header_t *)ip_start; \
+ tcpudp = (tcpudp_header_t *)(ip6+1); \
+ foreach_ipfix_ip6_field; \
+ } \
+ foreach_ipfix_transport_protocol_field
+
+typedef struct {
+ u32 classify_table_index;
+ u8 ip_version;
+ u8 transport_protocol;
+} ipfix_classify_table_t;
+
+typedef struct {
+ u32 domain_id;
+ u16 src_port;
+ ipfix_classify_table_t * tables;
+} flow_report_classify_main_t;
+
+extern flow_report_classify_main_t flow_report_classify_main;
+
+static_always_inline u8 ipfix_classify_table_index_valid (u32 index)
+{
+ flow_report_classify_main_t * fcm = &flow_report_classify_main;
+ return index < vec_len(fcm->tables) &&
+ fcm->tables[index].classify_table_index != ~0;
+}
+
+static_always_inline ipfix_classify_table_t * ipfix_classify_add_table (void)
+{
+ flow_report_classify_main_t * fcm = &flow_report_classify_main;
+ u32 i;
+ for (i = 0; i < vec_len(fcm->tables); i++)
+ if (!ipfix_classify_table_index_valid(i))
+ return &fcm->tables[i];
+ u32 index = vec_len(fcm->tables);
+ vec_validate(fcm->tables, index);
+ return &fcm->tables[index];
+}
+
+static_always_inline void ipfix_classify_delete_table (u32 index)
+{
+ flow_report_classify_main_t * fcm = &flow_report_classify_main;
+ ASSERT (index < vec_len(fcm->tables));
+ ASSERT (fcm->tables[index].classify_table_index != ~0);
+ fcm->tables[index].classify_table_index = ~0;
+}
+
+u8 * ipfix_classify_template_rewrite (flow_report_main_t * frm,
+ flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address,
+ u16 collector_port);
+
+vlib_frame_t * ipfix_classify_send_flows (flow_report_main_t * frm,
+ flow_report_t * fr,
+ vlib_frame_t * f,
+ u32 * to_next,
+ u32 node_index);
#endif /* __included_flow_report_classify_h__ */
diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c
index 3b93c985791..3c65d78db8b 100644
--- a/vpp-api-test/vat/api_format.c
+++ b/vpp-api-test/vat/api_format.c
@@ -3426,7 +3426,9 @@ _(af_packet_delete_reply) \
_(policer_classify_set_interface_reply) \
_(netmap_create_reply) \
_(netmap_delete_reply) \
-_(ipfix_enable_reply) \
+_(set_ipfix_exporter_reply) \
+_(set_ipfix_classify_stream_reply) \
+_(ipfix_classify_table_add_del_reply) \
_(pg_capture_reply) \
_(pg_enable_disable_reply) \
_(ip_source_and_port_range_check_add_del_reply) \
@@ -3641,8 +3643,12 @@ _(CLASSIFY_TABLE_IDS_REPLY, classify_table_ids_reply) \
_(CLASSIFY_TABLE_BY_INTERFACE_REPLY, classify_table_by_interface_reply) \
_(CLASSIFY_TABLE_INFO_REPLY, classify_table_info_reply) \
_(CLASSIFY_SESSION_DETAILS, classify_session_details) \
-_(IPFIX_ENABLE_REPLY, ipfix_enable_reply) \
-_(IPFIX_DETAILS, ipfix_details) \
+_(SET_IPFIX_EXPORTER_REPLY, set_ipfix_exporter_reply) \
+_(IPFIX_EXPORTER_DETAILS, ipfix_exporter_details) \
+_(SET_IPFIX_CLASSIFY_STREAM_REPLY, set_ipfix_classify_stream_reply) \
+_(IPFIX_CLASSIFY_STREAM_DETAILS, ipfix_classify_stream_details) \
+_(IPFIX_CLASSIFY_TABLE_ADD_DEL_REPLY, ipfix_classify_table_add_del_reply) \
+_(IPFIX_CLASSIFY_TABLE_DETAILS, ipfix_classify_table_details) \
_(GET_NEXT_INDEX_REPLY, get_next_index_reply) \
_(PG_CREATE_INTERFACE_REPLY, pg_create_interface_reply) \
_(PG_CAPTURE_REPLY, pg_capture_reply) \
@@ -8618,10 +8624,10 @@ api_classify_set_interface_l2_tables (vat_main_t * vam)
}
static int
-api_ipfix_enable (vat_main_t * vam)
+api_set_ipfix_exporter (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
- vl_api_ipfix_enable_t *mp;
+ vl_api_set_ipfix_exporter_t *mp;
ip4_address_t collector_address;
u8 collector_address_set = 0;
u32 collector_port = ~0;
@@ -8630,6 +8636,7 @@ api_ipfix_enable (vat_main_t * vam)
u32 vrf_id = ~0;
u32 path_mtu = ~0;
u32 template_interval = ~0;
+ u8 udp_checksum = 0;
f64 timeout;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
@@ -8648,6 +8655,8 @@ api_ipfix_enable (vat_main_t * vam)
;
else if (unformat (i, "template_interval %d", &template_interval))
;
+ else if (unformat (i, "udp_checksum"))
+ udp_checksum = 1;
else
break;
}
@@ -8664,7 +8673,7 @@ api_ipfix_enable (vat_main_t * vam)
return -99;
}
- M (IPFIX_ENABLE, ipfix_enable);
+ M (SET_IPFIX_EXPORTER, set_ipfix_exporter);
memcpy (mp->collector_address, collector_address.data,
sizeof (collector_address.data));
@@ -8673,6 +8682,101 @@ api_ipfix_enable (vat_main_t * vam)
mp->vrf_id = htonl (vrf_id);
mp->path_mtu = htonl (path_mtu);
mp->template_interval = htonl (template_interval);
+ mp->udp_checksum = udp_checksum;
+
+ S;
+ W;
+ /* NOTREACHED */
+}
+
+static int
+api_set_ipfix_classify_stream (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_set_ipfix_classify_stream_t *mp;
+ u32 domain_id = 0;
+ u32 src_port = UDP_DST_PORT_ipfix;
+ f64 timeout;
+
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "domain %d", &domain_id))
+ ;
+ else if (unformat (i, "src_port %d", &src_port))
+ ;
+ else
+ {
+ errmsg ("unknown input `%U'", format_unformat_error, i);
+ return -99;
+ }
+ }
+
+ M (SET_IPFIX_CLASSIFY_STREAM, set_ipfix_classify_stream);
+
+ mp->domain_id = htonl (domain_id);
+ mp->src_port = htons ((u16) src_port);
+
+ S;
+ W;
+ /* NOTREACHED */
+}
+
+static int
+api_ipfix_classify_table_add_del (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_ipfix_classify_table_add_del_t *mp;
+ int is_add = -1;
+ u32 classify_table_index;
+ u8 ip_version = 0;
+ u8 transport_protocol = 255;
+ f64 timeout;
+
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "add"))
+ is_add = 1;
+ else if (unformat (i, "del"))
+ is_add = 0;
+ else if (unformat (i, "table %d", &classify_table_index))
+ ;
+ else if (unformat (i, "ip4"))
+ ip_version = 4;
+ else if (unformat (i, "ip6"))
+ ip_version = 6;
+ else if (unformat (i, "tcp"))
+ transport_protocol = 6;
+ else if (unformat (i, "udp"))
+ transport_protocol = 17;
+ else
+ {
+ errmsg ("unknown input `%U'", format_unformat_error, i);
+ return -99;
+ }
+ }
+
+ if (is_add == -1)
+ {
+ errmsg ("expecting: add|del");
+ return -99;
+ }
+ if (classify_table_index == ~0)
+ {
+ errmsg ("classifier table not specified");
+ return -99;
+ }
+ if (ip_version == 0)
+ {
+ errmsg ("IP version not specified");
+ return -99;
+ }
+
+ M (IPFIX_CLASSIFY_TABLE_ADD_DEL, ipfix_classify_table_add_del);
+
+ mp->is_add = is_add;
+ mp->table_id = htonl (classify_table_index);
+ mp->ip_version = ip_version;
+ mp->transport_protocol = transport_protocol;
S;
W;
@@ -14743,25 +14847,26 @@ api_classify_session_dump (vat_main_t * vam)
}
static void
-vl_api_ipfix_details_t_handler (vl_api_ipfix_details_t * mp)
+vl_api_ipfix_exporter_details_t_handler (vl_api_ipfix_exporter_details_t * mp)
{
vat_main_t *vam = &vat_main;
fformat (vam->ofp, "collector_address %U, collector_port %d, "
- "src_address %U, fib_index %u, path_mtu %u, "
- "template_interval %u\n",
+ "src_address %U, vrf_id %d, path_mtu %u, "
+ "template_interval %u, udp_checksum %d\n",
format_ip4_address, mp->collector_address,
ntohs (mp->collector_port),
format_ip4_address, mp->src_address,
- ntohl (mp->fib_index),
- ntohl (mp->path_mtu), ntohl (mp->template_interval));
+ ntohl (mp->vrf_id), ntohl (mp->path_mtu),
+ ntohl (mp->template_interval), mp->udp_checksum);
vam->retval = 0;
vam->result_ready = 1;
}
static void
-vl_api_ipfix_details_t_handler_json (vl_api_ipfix_details_t * mp)
+ vl_api_ipfix_exporter_details_t_handler_json
+ (vl_api_ipfix_exporter_details_t * mp)
{
vat_main_t *vam = &vat_main;
vat_json_node_t node;
@@ -14776,10 +14881,11 @@ vl_api_ipfix_details_t_handler_json (vl_api_ipfix_details_t * mp)
ntohs (mp->collector_port));
clib_memcpy (&src_address, &mp->src_address, sizeof (src_address));
vat_json_object_add_ip4 (&node, "src_address", src_address);
- vat_json_object_add_uint (&node, "fib_index", ntohl (mp->fib_index));
+ vat_json_object_add_int (&node, "vrf_id", ntohl (mp->vrf_id));
vat_json_object_add_uint (&node, "path_mtu", ntohl (mp->path_mtu));
vat_json_object_add_uint (&node, "template_interval",
ntohl (mp->template_interval));
+ vat_json_object_add_int (&node, "udp_checksum", mp->udp_checksum);
vat_json_print (vam->ofp, &node);
vat_json_free (&node);
@@ -14788,13 +14894,13 @@ vl_api_ipfix_details_t_handler_json (vl_api_ipfix_details_t * mp)
}
int
-api_ipfix_dump (vat_main_t * vam)
+api_ipfix_exporter_dump (vat_main_t * vam)
{
- vl_api_ipfix_dump_t *mp;
+ vl_api_ipfix_exporter_dump_t *mp;
f64 timeout;
/* Construct the API message */
- M (IPFIX_DUMP, ipfix_dump);
+ M (IPFIX_EXPORTER_DUMP, ipfix_exporter_dump);
mp->context = 0;
S;
@@ -14803,6 +14909,108 @@ api_ipfix_dump (vat_main_t * vam)
return 0;
}
+static int
+api_ipfix_classify_stream_dump (vat_main_t * vam)
+{
+ vl_api_ipfix_classify_stream_dump_t *mp;
+ f64 timeout;
+
+ /* Construct the API message */
+ M (IPFIX_CLASSIFY_STREAM_DUMP, ipfix_classify_stream_dump);
+ mp->context = 0;
+
+ S;
+ W;
+ /* NOTREACHED */
+ return 0;
+}
+
+static void
+ vl_api_ipfix_classify_stream_details_t_handler
+ (vl_api_ipfix_classify_stream_details_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ fformat (vam->ofp, "domain_id %d, src_port %d\n",
+ ntohl (mp->domain_id), ntohs (mp->src_port));
+ vam->retval = 0;
+ vam->result_ready = 1;
+}
+
+static void
+ vl_api_ipfix_classify_stream_details_t_handler_json
+ (vl_api_ipfix_classify_stream_details_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ vat_json_node_t node;
+
+ vat_json_init_object (&node);
+ vat_json_object_add_uint (&node, "domain_id", ntohl (mp->domain_id));
+ vat_json_object_add_uint (&node, "src_port", ntohs (mp->src_port));
+
+ vat_json_print (vam->ofp, &node);
+ vat_json_free (&node);
+ vam->retval = 0;
+ vam->result_ready = 1;
+}
+
+static int
+api_ipfix_classify_table_dump (vat_main_t * vam)
+{
+ vl_api_ipfix_classify_table_dump_t *mp;
+ f64 timeout;
+
+ if (!vam->json_output)
+ {
+ fformat (vam->ofp, "%15s%15s%20s\n", "table_id", "ip_version",
+ "transport_protocol");
+ }
+
+ /* Construct the API message */
+ M (IPFIX_CLASSIFY_TABLE_DUMP, ipfix_classify_table_dump);
+
+ /* send it... */
+ S;
+
+ /* Use a control ping for synchronization */
+ {
+ vl_api_control_ping_t *mp;
+ M (CONTROL_PING, control_ping);
+ S;
+ }
+ W;
+}
+
+static void
+ vl_api_ipfix_classify_table_details_t_handler
+ (vl_api_ipfix_classify_table_details_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ fformat (vam->ofp, "%15d%15d%20d\n", ntohl (mp->table_id), mp->ip_version,
+ mp->transport_protocol);
+}
+
+static void
+ vl_api_ipfix_classify_table_details_t_handler_json
+ (vl_api_ipfix_classify_table_details_t * mp)
+{
+ vat_json_node_t *node = NULL;
+ vat_main_t *vam = &vat_main;
+
+ if (VAT_JSON_ARRAY != vam->json_tree.type)
+ {
+ ASSERT (VAT_JSON_NONE == vam->json_tree.type);
+ vat_json_init_array (&vam->json_tree);
+ }
+
+ node = vat_json_array_add (&vam->json_tree);
+ vat_json_init_object (node);
+
+ vat_json_object_add_uint (node, "table_id", ntohl (mp->table_id));
+ vat_json_object_add_uint (node, "ip_version", mp->ip_version);
+ vat_json_object_add_uint (node, "transport_protocol",
+ mp->transport_protocol);
+}
+
int
api_pg_create_interface (vat_main_t * vam)
{
@@ -15862,10 +16070,14 @@ _(classify_table_ids, "") \
_(classify_table_by_interface, "sw_if_index <sw_if_index>") \
_(classify_table_info, "table_id <nn>") \
_(classify_session_dump, "table_id <nn>") \
-_(ipfix_enable, "collector_address <ip4> [collector_port <nn>] " \
- "src_address <ip4> [fib_id <nn>] [path_mtu <nn>] " \
- "[template_interval <nn>]") \
-_(ipfix_dump, "") \
+_(set_ipfix_exporter, "collector_address <ip4> [collector_port <nn>] " \
+ "src_address <ip4> [vrf_id <nn>] [path_mtu <nn>] " \
+ "[template_interval <nn>] [udp_checksum]") \
+_(ipfix_exporter_dump, "") \
+_(set_ipfix_classify_stream, "[domain <domain-id>] [src_port <src-port>]") \
+_(ipfix_classify_stream_dump, "") \
+_(ipfix_classify_table_add_del, "table <table-index> ip4|ip6 [tcp|udp]")\
+_(ipfix_classify_table_dump, "") \
_(get_next_index, "node-name <node-name> next-node-name <node-name>") \
_(pg_create_interface, "if_id <nn>") \
_(pg_capture, "if_id <nnn> pcap <file_name> count <nnn> [disable]") \
diff --git a/vpp/vpp-api/api.c b/vpp/vpp-api/api.c
index c339cb44c60..36532434df2 100644
--- a/vpp/vpp-api/api.c
+++ b/vpp/vpp-api/api.c
@@ -84,6 +84,7 @@
#include <vnet/devices/netmap/netmap.h>
#include <vnet/flow/flow_report.h>
#include <vnet/ipsec-gre/ipsec_gre.h>
+#include <vnet/flow/flow_report_classify.h>
#undef BIHASH_TYPE
#undef __included_bihash_template_h__
@@ -391,8 +392,12 @@ _(CLASSIFY_TABLE_BY_INTERFACE, classify_table_by_interface) \
_(CLASSIFY_TABLE_INFO,classify_table_info) \
_(CLASSIFY_SESSION_DUMP,classify_session_dump) \
_(CLASSIFY_SESSION_DETAILS,classify_session_details) \
-_(IPFIX_ENABLE,ipfix_enable) \
-_(IPFIX_DUMP,ipfix_dump) \
+_(SET_IPFIX_EXPORTER, set_ipfix_exporter) \
+_(IPFIX_EXPORTER_DUMP, ipfix_exporter_dump) \
+_(SET_IPFIX_CLASSIFY_STREAM, set_ipfix_classify_stream) \
+_(IPFIX_CLASSIFY_STREAM_DUMP, ipfix_classify_stream_dump) \
+_(IPFIX_CLASSIFY_TABLE_ADD_DEL, ipfix_classify_table_add_del) \
+_(IPFIX_CLASSIFY_TABLE_DUMP, ipfix_classify_table_dump) \
_(GET_NEXT_INDEX, get_next_index) \
_(PG_CREATE_INTERFACE, pg_create_interface) \
_(PG_CAPTURE, pg_capture) \
@@ -7928,15 +7933,16 @@ vl_api_classify_session_dump_t_handler (vl_api_classify_session_dump_t * mp)
}
static void
-vl_api_ipfix_enable_t_handler (vl_api_ipfix_enable_t * mp)
+vl_api_set_ipfix_exporter_t_handler (vl_api_set_ipfix_exporter_t * mp)
{
vlib_main_t *vm = vlib_get_main ();
flow_report_main_t *frm = &flow_report_main;
- vl_api_ipfix_enable_reply_t *rmp;
+ vl_api_set_ipfix_exporter_reply_t *rmp;
ip4_address_t collector, src;
u16 collector_port = UDP_DST_PORT_ipfix;
u32 path_mtu;
u32 template_interval;
+ u8 udp_checksum;
u32 fib_id;
u32 fib_index = ~0;
int rv = 0;
@@ -7949,13 +7955,20 @@ vl_api_ipfix_enable_t_handler (vl_api_ipfix_enable_t * mp)
fib_id = ntohl (mp->vrf_id);
ip4_main_t *im = &ip4_main;
- uword *p = hash_get (im->fib_index_by_table_id, fib_id);
- if (!p)
+ if (fib_id == ~0)
{
- rv = VNET_API_ERROR_NO_SUCH_FIB;
- goto out;
+ fib_index = ~0;
+ }
+ else
+ {
+ uword *p = hash_get (im->fib_index_by_table_id, fib_id);
+ if (!p)
+ {
+ rv = VNET_API_ERROR_NO_SUCH_FIB;
+ goto out;
+ }
+ fib_index = p[0];
}
- fib_index = p[0];
path_mtu = ntohl (mp->path_mtu);
if (path_mtu == ~0)
@@ -7963,6 +7976,7 @@ vl_api_ipfix_enable_t_handler (vl_api_ipfix_enable_t * mp)
template_interval = ntohl (mp->template_interval);
if (template_interval == ~0)
template_interval = 20;
+ udp_checksum = mp->udp_checksum;
if (collector.as_u32 == 0)
{
@@ -8000,20 +8014,23 @@ vl_api_ipfix_enable_t_handler (vl_api_ipfix_enable_t * mp)
frm->fib_index = fib_index;
frm->path_mtu = path_mtu;
frm->template_interval = template_interval;
+ frm->udp_checksum = udp_checksum;
/* Turn on the flow reporting process */
vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
out:
- REPLY_MACRO (VL_API_IPFIX_ENABLE_REPLY);
+ REPLY_MACRO (VL_API_SET_IPFIX_EXPORTER_REPLY);
}
static void
-vl_api_ipfix_dump_t_handler (vl_api_ipfix_dump_t * mp)
+vl_api_ipfix_exporter_dump_t_handler (vl_api_ipfix_exporter_dump_t * mp)
{
flow_report_main_t *frm = &flow_report_main;
unix_shared_memory_queue_t *q;
- vl_api_ipfix_details_t *rmp;
+ vl_api_ipfix_exporter_details_t *rmp;
+ ip4_main_t *im = &ip4_main;
+ u32 vrf_id;
q = vl_api_client_index_to_input_queue (mp->client_index);
if (!q)
@@ -8021,21 +8038,197 @@ vl_api_ipfix_dump_t_handler (vl_api_ipfix_dump_t * mp)
rmp = vl_msg_api_alloc (sizeof (*rmp));
memset (rmp, 0, sizeof (*rmp));
- rmp->_vl_msg_id = ntohs (VL_API_IPFIX_DETAILS);
+ rmp->_vl_msg_id = ntohs (VL_API_IPFIX_EXPORTER_DETAILS);
rmp->context = mp->context;
memcpy (rmp->collector_address, frm->ipfix_collector.data,
sizeof (frm->ipfix_collector.data));
rmp->collector_port = htons (frm->collector_port);
memcpy (rmp->src_address, frm->src_address.data,
sizeof (frm->src_address.data));
- rmp->fib_index = htonl (frm->fib_index);
+ if (frm->fib_index == ~0)
+ vrf_id = ~0;
+ else
+ vrf_id = im->fibs[frm->fib_index].table_id;
+ rmp->vrf_id = htonl (vrf_id);
rmp->path_mtu = htonl (frm->path_mtu);
rmp->template_interval = htonl (frm->template_interval);
+ rmp->udp_checksum = (frm->udp_checksum != 0);
+
+ vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+ vl_api_set_ipfix_classify_stream_t_handler
+ (vl_api_set_ipfix_classify_stream_t * mp)
+{
+ vl_api_set_ipfix_classify_stream_reply_t *rmp;
+ flow_report_classify_main_t *fcm = &flow_report_classify_main;
+ flow_report_main_t *frm = &flow_report_main;
+ u32 domain_id = 0;
+ u32 src_port = UDP_DST_PORT_ipfix;
+ int rv = 0;
+
+ domain_id = ntohl (mp->domain_id);
+ src_port = ntohs (mp->src_port);
+
+ if (fcm->src_port != 0 &&
+ (fcm->domain_id != domain_id || fcm->src_port != (u16) src_port))
+ {
+ int rv = vnet_stream_change (frm, fcm->domain_id, fcm->src_port,
+ domain_id, (u16) src_port);
+ ASSERT (rv == 0);
+ }
+
+ fcm->domain_id = domain_id;
+ fcm->src_port = (u16) src_port;
+
+ REPLY_MACRO (VL_API_SET_IPFIX_CLASSIFY_STREAM_REPLY);
+}
+
+static void
+ vl_api_ipfix_classify_stream_dump_t_handler
+ (vl_api_ipfix_classify_stream_dump_t * mp)
+{
+ flow_report_classify_main_t *fcm = &flow_report_classify_main;
+ unix_shared_memory_queue_t *q;
+ vl_api_ipfix_classify_stream_details_t *rmp;
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ memset (rmp, 0, sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (VL_API_IPFIX_CLASSIFY_STREAM_DETAILS);
+ rmp->context = mp->context;
+ rmp->domain_id = htonl (fcm->domain_id);
+ rmp->src_port = htons (fcm->src_port);
vl_msg_api_send_shmem (q, (u8 *) & rmp);
}
static void
+ vl_api_ipfix_classify_table_add_del_t_handler
+ (vl_api_ipfix_classify_table_add_del_t * mp)
+{
+ vl_api_ipfix_classify_table_add_del_reply_t *rmp;
+ flow_report_classify_main_t *fcm = &flow_report_classify_main;
+ flow_report_main_t *frm = &flow_report_main;
+ vnet_flow_report_add_del_args_t args;
+ ipfix_classify_table_t *table;
+ int is_add;
+ u32 classify_table_index;
+ u8 ip_version;
+ u8 transport_protocol;
+ int rv = 0;
+
+ classify_table_index = ntohl (mp->table_id);
+ ip_version = mp->ip_version;
+ transport_protocol = mp->transport_protocol;
+ is_add = mp->is_add;
+
+ if (fcm->src_port == 0)
+ {
+ /* call set_ipfix_classify_stream first */
+ rv = VNET_API_ERROR_UNSPECIFIED;
+ goto out;
+ }
+
+ memset (&args, 0, sizeof (args));
+
+ table = 0;
+ int i;
+ for (i = 0; i < vec_len (fcm->tables); i++)
+ if (ipfix_classify_table_index_valid (i))
+ if (fcm->tables[i].classify_table_index == classify_table_index)
+ {
+ table = &fcm->tables[i];
+ break;
+ }
+
+ if (is_add)
+ {
+ if (table)
+ {
+ rv = VNET_API_ERROR_VALUE_EXIST;
+ goto out;
+ }
+ table = ipfix_classify_add_table ();
+ table->classify_table_index = classify_table_index;
+ }
+ else
+ {
+ if (!table)
+ {
+ rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+ goto out;
+ }
+ }
+
+ table->ip_version = ip_version;
+ table->transport_protocol = transport_protocol;
+
+ args.opaque.as_uword = table - fcm->tables;
+ args.rewrite_callback = ipfix_classify_template_rewrite;
+ args.flow_data_callback = ipfix_classify_send_flows;
+ args.is_add = is_add;
+ args.domain_id = fcm->domain_id;
+ args.src_port = fcm->src_port;
+
+ rv = vnet_flow_report_add_del (frm, &args);
+ if (rv != 0)
+ goto out;
+
+ if (is_add)
+ {
+ if (rv != 0)
+ ipfix_classify_delete_table (table - fcm->tables);
+ }
+ else
+ {
+ if (rv == 0)
+ ipfix_classify_delete_table (table - fcm->tables);
+ }
+
+out:
+ REPLY_MACRO (VL_API_SET_IPFIX_CLASSIFY_STREAM_REPLY);
+}
+
+static void
+send_ipfix_classify_table_details (u32 table_index,
+ unix_shared_memory_queue_t * q,
+ u32 context)
+{
+ flow_report_classify_main_t *fcm = &flow_report_classify_main;
+ vl_api_ipfix_classify_table_details_t *mp;
+
+ ipfix_classify_table_t *table = &fcm->tables[table_index];
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_IPFIX_CLASSIFY_TABLE_DETAILS);
+ mp->context = context;
+ mp->table_id = htonl (table->classify_table_index);
+ mp->ip_version = table->ip_version;
+ mp->transport_protocol = table->transport_protocol;
+
+ vl_msg_api_send_shmem (q, (u8 *) & mp);
+}
+
+static void
+ vl_api_ipfix_classify_table_dump_t_handler
+ (vl_api_ipfix_classify_table_dump_t * mp)
+{
+ flow_report_classify_main_t *fcm = &flow_report_classify_main;
+ unix_shared_memory_queue_t *q;
+ u32 i;
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+
+ for (i = 0; i < vec_len (fcm->tables); i++)
+ if (ipfix_classify_table_index_valid (i))
+ send_ipfix_classify_table_details (i, q, mp->context);
+}
+
+static void
vl_api_pg_create_interface_t_handler (vl_api_pg_create_interface_t * mp)
{
vl_api_pg_create_interface_reply_t *rmp;
diff --git a/vpp/vpp-api/custom_dump.c b/vpp/vpp-api/custom_dump.c
index 5ca7ccc9299..e8ee5516ea3 100644
--- a/vpp/vpp-api/custom_dump.c
+++ b/vpp/vpp-api/custom_dump.c
@@ -2093,12 +2093,12 @@ static void *vl_api_classify_session_dump_t_print
FINISH;
}
-static void *vl_api_ipfix_enable_t_print
- (vl_api_ipfix_enable_t * mp, void *handle)
+static void *vl_api_set_ipfix_exporter_t_print
+ (vl_api_set_ipfix_exporter_t * mp, void *handle)
{
u8 *s;
- s = format (0, "SCRIPT: ipfix_enable ");
+ s = format (0, "SCRIPT: set_ipfix_exporter ");
s = format (s, "collector-address %U ", format_ip4_address,
(ip4_address_t *) mp->collector_address);
@@ -2108,16 +2108,64 @@ static void *vl_api_ipfix_enable_t_print
s = format (s, "vrf-id %d ", ntohl (mp->vrf_id));
s = format (s, "path-mtu %d ", ntohl (mp->path_mtu));
s = format (s, "template-interval %d ", ntohl (mp->template_interval));
+ s = format (s, "udp-checksum %d ", mp->udp_checksum);
FINISH;
}
-static void *vl_api_ipfix_dump_t_print
- (vl_api_ipfix_dump_t * mp, void *handle)
+static void *vl_api_ipfix_exporter_dump_t_print
+ (vl_api_ipfix_exporter_dump_t * mp, void *handle)
{
u8 *s;
- s = format (0, "SCRIPT: ipfix_dump ");
+ s = format (0, "SCRIPT: ipfix_exporter_dump ");
+
+ FINISH;
+}
+
+static void *vl_api_set_ipfix_classify_stream_t_print
+ (vl_api_set_ipfix_classify_stream_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: set_ipfix_classify_stream ");
+
+ s = format (s, "domain-id %d ", ntohl (mp->domain_id));
+ s = format (s, "src-port %d ", ntohs (mp->src_port));
+
+ FINISH;
+}
+
+static void *vl_api_ipfix_classify_stream_dump_t_print
+ (vl_api_ipfix_classify_stream_dump_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: ipfix_classify_stream_dump ");
+
+ FINISH;
+}
+
+static void *vl_api_ipfix_classify_table_add_del_t_print
+ (vl_api_ipfix_classify_table_add_del_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: ipfix_classify_table_add_del ");
+
+ s = format (s, "table-id %d ", ntohl (mp->table_id));
+ s = format (s, "ip-version %d ", mp->ip_version);
+ s = format (s, "transport-protocol %d ", mp->transport_protocol);
+
+ FINISH;
+}
+
+static void *vl_api_ipfix_classify_table_dump_t_print
+ (vl_api_ipfix_classify_table_dump_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: ipfix_classify_table_dump ");
FINISH;
}
@@ -2733,8 +2781,12 @@ _(CLASSIFY_TABLE_IDS,classify_table_ids) \
_(CLASSIFY_TABLE_BY_INTERFACE, classify_table_by_interface) \
_(CLASSIFY_TABLE_INFO,classify_table_info) \
_(CLASSIFY_SESSION_DUMP,classify_session_dump) \
-_(IPFIX_ENABLE,ipfix_enable) \
-_(IPFIX_DUMP,ipfix_dump) \
+_(SET_IPFIX_EXPORTER, set_ipfix_exporter) \
+_(IPFIX_EXPORTER_DUMP, ipfix_exporter_dump) \
+_(SET_IPFIX_CLASSIFY_STREAM, set_ipfix_classify_stream) \
+_(IPFIX_CLASSIFY_STREAM_DUMP, ipfix_classify_stream_dump) \
+_(IPFIX_CLASSIFY_TABLE_ADD_DEL, ipfix_classify_table_add_del) \
+_(IPFIX_CLASSIFY_TABLE_DUMP, ipfix_classify_table_dump) \
_(GET_NEXT_INDEX, get_next_index) \
_(PG_CREATE_INTERFACE,pg_create_interface) \
_(PG_CAPTURE, pg_capture) \
diff --git a/vpp/vpp-api/vpe.api b/vpp/vpp-api/vpe.api
index 5c1502a890a..11bb30c1cc3 100644
--- a/vpp/vpp-api/vpe.api
+++ b/vpp/vpp-api/vpe.api
@@ -4570,17 +4570,18 @@ define classify_session_details
u8 match[match_length];
};
-/** \brief Enable and configure IPFIX exporter process request
+/** \brief Configure IPFIX exporter process request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param collector_address - address of IPFIX collector
- @param collector_port - port of IPFIX IPFIX collector
+ @param collector_port - port of IPFIX collector
@param src_address - address of IPFIX exporter
@param vrf_id - VRF / fib table ID
@param path_mtu - Path MTU between exporter and collector
@param template_interval - number of seconds after which to resend template
+ @param udp_checksum - UDP checksum calculation enable flag
*/
-define ipfix_enable
+define set_ipfix_exporter
{
u32 client_index;
u32 context;
@@ -4590,45 +4591,136 @@ define ipfix_enable
u32 vrf_id;
u32 path_mtu;
u32 template_interval;
+ u8 udp_checksum;
};
-/** \brief Reply to IPFIX enable and configure request
+/** \brief Reply to IPFIX exporter configure request
@param context - sender context which was passed in the request
*/
-define ipfix_enable_reply
+define set_ipfix_exporter_reply
{
u32 context;
i32 retval;
};
-/** \brief IPFIX dump request
+/** \brief IPFIX exporter dump request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
*/
-define ipfix_dump
+define ipfix_exporter_dump
{
u32 client_index;
u32 context;
};
-/** \brief Reply to IPFIX dump request
+/** \brief Reply to IPFIX exporter dump request
@param context - sender context which was passed in the request
@param collector_address - address of IPFIX collector
- @param collector_port - port of IPFIX IPFIX collector
+ @param collector_port - port of IPFIX collector
@param src_address - address of IPFIX exporter
@param fib_index - fib table index
@param path_mtu - Path MTU between exporter and collector
@param template_interval - number of seconds after which to resend template
+ @param udp_checksum - UDP checksum calculation enable flag
*/
-define ipfix_details
+define ipfix_exporter_details
{
u32 context;
u8 collector_address[16];
u16 collector_port;
u8 src_address[16];
- u32 fib_index;
+ u32 vrf_id;
u32 path_mtu;
u32 template_interval;
+ u8 udp_checksum;
+};
+
+/** \brief IPFIX classify stream configure request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param domain_id - domain ID reported in IPFIX messages for classify stream
+ @param src_port - source port of UDP session for classify stream
+*/
+define set_ipfix_classify_stream {
+ u32 client_index;
+ u32 context;
+ u32 domain_id;
+ u16 src_port;
+};
+
+/** \brief IPFIX classify stream configure response
+ @param context - sender context, to match reply w/ request
+ @param retval - return value for request
+*/
+define set_ipfix_classify_stream_reply {
+ u32 context;
+ i32 retval;
+};
+
+/** \brief IPFIX classify stream dump request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define ipfix_classify_stream_dump {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Reply to IPFIX classify stream dump request
+ @param context - sender context, to match reply w/ request
+ @param domain_id - domain ID reported in IPFIX messages for classify stream
+ @param src_port - source port of UDP session for classify stream
+*/
+define ipfix_classify_stream_details {
+ u32 context;
+ u32 domain_id;
+ u16 src_port;
+};
+
+/** \brief IPFIX add or delete classifier table request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param table_id - classifier table ID
+ @param ip_version - version of IP used in the classifier table
+ @param transport_protocol - transport protocol used in the classifier table or 255 for unspecified
+*/
+define ipfix_classify_table_add_del {
+ u32 client_index;
+ u32 context;
+ u32 table_id;
+ u8 ip_version;
+ u8 transport_protocol;
+ u8 is_add;
+};
+
+/** \brief IPFIX add classifier table response
+ @param context - sender context which was passed in the request
+*/
+define ipfix_classify_table_add_del_reply {
+ u32 context;
+ i32 retval;
+};
+
+/** \brief IPFIX classify tables dump request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define ipfix_classify_table_dump {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Reply to IPFIX classify tables dump request
+ @param context - sender context, to match reply w/ request
+ @param table_id - classifier table ID
+ @param ip_version - version of IP used in the classifier table
+ @param transport_protocol - transport protocol used in the classifier table or 255 for unspecified
+*/
+define ipfix_classify_table_details {
+ u32 context;
+ u32 table_id;
+ u8 ip_version;
+ u8 transport_protocol;
};
/** \brief Query relative index via node names