From ffa652afd8847438af4d0bdc253789a67a4f1da1 Mon Sep 17 00:00:00 2001 From: Juraj Sloboda Date: Sun, 7 Aug 2016 23:43:42 -0700 Subject: [PATCH] VPP-204 Rework and finish IPFIX implementation Rework flow report registration system - add streams Add support for IPv6 and src and dst ports for TCP and UDP protocols Implement binary API for IPFIX classifier module Change-Id: Id05cc0127a7b95ceaeebf9c79a32c6936449bd63 Signed-off-by: Juraj Sloboda --- vnet/vnet/flow/flow_report.c | 159 +++++++++++++++++--- vnet/vnet/flow/flow_report.h | 33 ++++- vnet/vnet/flow/flow_report_classify.c | 264 +++++++++++++++++++++++++--------- vnet/vnet/flow/flow_report_classify.h | 110 +++++++++++++- vpp-api-test/vat/api_format.c | 252 +++++++++++++++++++++++++++++--- vpp/vpp-api/api.c | 221 ++++++++++++++++++++++++++-- vpp/vpp-api/custom_dump.c | 68 +++++++-- vpp/vpp-api/vpe.api | 114 +++++++++++++-- 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 " - "[port ] " +VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = { + .path = "set ipfix exporter", + .short_help = "set ipfix exporter " + "collector [port ] " "src [fib-id ] " "[path-mtu ] " "[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 #include +/* 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 ", + .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 ] [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 ") \ _(classify_table_info, "table_id ") \ _(classify_session_dump, "table_id ") \ -_(ipfix_enable, "collector_address [collector_port ] " \ - "src_address [fib_id ] [path_mtu ] " \ - "[template_interval ]") \ -_(ipfix_dump, "") \ +_(set_ipfix_exporter, "collector_address [collector_port ] " \ + "src_address [vrf_id ] [path_mtu ] " \ + "[template_interval ] [udp_checksum]") \ +_(ipfix_exporter_dump, "") \ +_(set_ipfix_classify_stream, "[domain ] [src_port ]") \ +_(ipfix_classify_stream_dump, "") \ +_(ipfix_classify_table_add_del, "table ip4|ip6 [tcp|udp]")\ +_(ipfix_classify_table_dump, "") \ _(get_next_index, "node-name next-node-name ") \ _(pg_create_interface, "if_id ") \ _(pg_capture, "if_id pcap count [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 #include #include +#include #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,20 +8038,196 @@ 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) { 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 -- 2.16.6