diff options
Diffstat (limited to 'src/vnet/ipfix-export')
-rw-r--r-- | src/vnet/ipfix-export/flow_api.c | 257 | ||||
-rw-r--r-- | src/vnet/ipfix-export/flow_report.c | 651 | ||||
-rw-r--r-- | src/vnet/ipfix-export/flow_report.h | 154 | ||||
-rw-r--r-- | src/vnet/ipfix-export/flow_report_classify.c | 46 | ||||
-rw-r--r-- | src/vnet/ipfix-export/flow_report_classify.h | 17 | ||||
-rw-r--r-- | src/vnet/ipfix-export/ipfix_export.api | 74 |
6 files changed, 888 insertions, 311 deletions
diff --git a/src/vnet/ipfix-export/flow_api.c b/src/vnet/ipfix-export/flow_api.c index 75a656468db..0b287335bbf 100644 --- a/src/vnet/ipfix-export/flow_api.c +++ b/src/vnet/ipfix-export/flow_api.c @@ -36,39 +36,96 @@ #define REPLY_MSG_ID_BASE frm->msg_id_base #include <vlibapi/api_helper_macros.h> -static void -vl_api_set_ipfix_exporter_t_handler (vl_api_set_ipfix_exporter_t * mp) +ipfix_exporter_t * +vnet_ipfix_exporter_lookup (const ip_address_t *ipfix_collector) +{ + flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp; + + pool_foreach (exp, frm->exporters) + { + if (ip_address_cmp (&exp->ipfix_collector, ipfix_collector) == 0) + return exp; + } + + return NULL; +} + +/* + * For backwards compatibility reasons index 0 in the set of exporters + * is alwyas used for the exporter created via the set_ipfix_exporter + * API. + */ +#define USE_INDEX_0 true +#define USE_ANY_INDEX false + +static int +vl_api_set_ipfix_exporter_t_internal ( + u32 client_index, vl_api_address_t *mp_collector_address, + u16 mp_collector_port, vl_api_address_t *mp_src_address, u32 mp_vrf_id, + u32 mp_path_mtu, u32 mp_template_interval, bool mp_udp_checksum, + bool use_index_0, bool is_create) { vlib_main_t *vm = vlib_get_main (); flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp; vl_api_registration_t *reg; - vl_api_set_ipfix_exporter_reply_t *rmp; - ip4_address_t collector, src; + ip_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; + u32 ip_header_size; - reg = vl_api_client_index_to_registration (mp->client_index); + reg = vl_api_client_index_to_registration (client_index); if (!reg) - return; + return VNET_API_ERROR_UNIMPLEMENTED; - if (mp->src_address.af == ADDRESS_IP6 - || mp->collector_address.af == ADDRESS_IP6) + if (use_index_0) { - rv = VNET_API_ERROR_UNIMPLEMENTED; - goto out; + /* + * In this case we update the existing exporter. There is no delete + * for exp[0] + */ + exp = &frm->exporters[0]; + + /* Collector address must be IPv4 for exp[0] */ + collector.version = AF_IP4; + ip4_address_decode (mp_collector_address->un.ip4, &collector.ip.ip4); + } + else + { + ip_address_decode2 (mp_collector_address, &collector); + if (is_create) + { + exp = vnet_ipfix_exporter_lookup (&collector); + if (!exp) + { + /* Create a new exporter instead of updating an existing one */ + if (pool_elts (frm->exporters) >= IPFIX_EXPORTERS_MAX) + return VNET_API_ERROR_INVALID_VALUE; + pool_get (frm->exporters, exp); + } + } + else + { + /* Delete the exporter */ + exp = vnet_ipfix_exporter_lookup (&collector); + if (!exp) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + pool_put (frm->exporters, exp); + return 0; + } } - ip4_address_decode (mp->collector_address.un.ip4, &collector); - collector_port = ntohs (mp->collector_port); + collector_port = ntohs (mp_collector_port); if (collector_port == (u16) ~ 0) collector_port = UDP_DST_PORT_ipfix; - ip4_address_decode (mp->src_address.un.ip4, &src); - fib_id = ntohl (mp->vrf_id); + ip_address_decode2 (mp_src_address, &src); + fib_id = ntohl (mp_vrf_id); ip4_main_t *im = &ip4_main; if (fib_id == ~0) @@ -79,69 +136,97 @@ vl_api_set_ipfix_exporter_t_handler (vl_api_set_ipfix_exporter_t * mp) { uword *p = hash_get (im->fib_index_by_table_id, fib_id); if (!p) - { - rv = VNET_API_ERROR_NO_SUCH_FIB; - goto out; - } + return VNET_API_ERROR_NO_SUCH_FIB; fib_index = p[0]; } - path_mtu = ntohl (mp->path_mtu); + path_mtu = ntohl (mp_path_mtu); if (path_mtu == ~0) path_mtu = 512; // RFC 7011 section 10.3.3. - template_interval = ntohl (mp->template_interval); + template_interval = ntohl (mp_template_interval); if (template_interval == ~0) template_interval = 20; - udp_checksum = mp->udp_checksum; + udp_checksum = mp_udp_checksum; - if (collector.as_u32 != 0 && src.as_u32 == 0) - { - rv = VNET_API_ERROR_INVALID_VALUE; - goto out; - } + /* + * If the collector address is set then the src must be too. + * Collector address can be set to 0 to disable exporter + */ + if (!ip_address_is_zero (&collector) && ip_address_is_zero (&src)) + return VNET_API_ERROR_INVALID_VALUE; + if (collector.version != src.version) + return VNET_API_ERROR_INVALID_VALUE; if (path_mtu > 1450 /* vpp does not support fragmentation */ ) - { - rv = VNET_API_ERROR_INVALID_VALUE; - goto out; - } + return VNET_API_ERROR_INVALID_VALUE; if (path_mtu < 68) - { - rv = VNET_API_ERROR_INVALID_VALUE; - goto out; - } + return VNET_API_ERROR_INVALID_VALUE; + + /* Calculate how much header data we need. */ + if (collector.version == AF_IP4) + ip_header_size = sizeof (ip4_header_t); + else + ip_header_size = sizeof (ip6_header_t); + exp->all_headers_size = ip_header_size + sizeof (udp_header_t) + + sizeof (ipfix_message_header_t) + + sizeof (ipfix_set_header_t); /* Reset report streams if we are reconfiguring IP addresses */ - if (frm->ipfix_collector.as_u32 != collector.as_u32 || - frm->src_address.as_u32 != src.as_u32 || - frm->collector_port != collector_port) - vnet_flow_reports_reset (frm); - - frm->ipfix_collector.as_u32 = collector.as_u32; - frm->collector_port = collector_port; - frm->src_address.as_u32 = src.as_u32; - frm->fib_index = fib_index; - frm->path_mtu = path_mtu; - frm->template_interval = template_interval; - frm->udp_checksum = udp_checksum; + if (ip_address_cmp (&exp->ipfix_collector, &collector) || + ip_address_cmp (&exp->src_address, &src) || + exp->collector_port != collector_port) + vnet_flow_reports_reset (exp); + + exp->ipfix_collector = collector; + exp->collector_port = collector_port; + exp->src_address = src; + exp->fib_index = fib_index; + exp->path_mtu = path_mtu; + exp->template_interval = template_interval; + exp->udp_checksum = udp_checksum; /* Turn on the flow reporting process */ vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0); -out: + return 0; +} + +static void +vl_api_set_ipfix_exporter_t_handler (vl_api_set_ipfix_exporter_t *mp) +{ + vl_api_set_ipfix_exporter_reply_t *rmp; + flow_report_main_t *frm = &flow_report_main; + int rv = vl_api_set_ipfix_exporter_t_internal ( + mp->client_index, &mp->collector_address, mp->collector_port, + &mp->src_address, mp->vrf_id, mp->path_mtu, mp->template_interval, + mp->udp_checksum, USE_INDEX_0, 0); + REPLY_MACRO (VL_API_SET_IPFIX_EXPORTER_REPLY); } static void +vl_api_ipfix_exporter_create_delete_t_handler ( + vl_api_ipfix_exporter_create_delete_t *mp) +{ + vl_api_ipfix_exporter_create_delete_reply_t *rmp; + flow_report_main_t *frm = &flow_report_main; + int rv = vl_api_set_ipfix_exporter_t_internal ( + mp->client_index, &mp->collector_address, mp->collector_port, + &mp->src_address, mp->vrf_id, mp->path_mtu, mp->template_interval, + mp->udp_checksum, USE_ANY_INDEX, mp->is_create); + + REPLY_MACRO (VL_API_IPFIX_EXPORTER_CREATE_DELETE_REPLY); +} + +static void vl_api_ipfix_exporter_dump_t_handler (vl_api_ipfix_exporter_dump_t * mp) { flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp = pool_elt_at_index (flow_report_main.exporters, 0); vl_api_registration_t *reg; vl_api_ipfix_exporter_details_t *rmp; ip4_main_t *im = &ip4_main; - ip46_address_t collector = {.as_u64[0] = 0,.as_u64[1] = 0 }; - ip46_address_t src = {.as_u64[0] = 0,.as_u64[1] = 0 }; u32 vrf_id; reg = vl_api_client_index_to_registration (mp->client_index); @@ -150,27 +235,69 @@ vl_api_ipfix_exporter_dump_t_handler (vl_api_ipfix_exporter_dump_t * mp) rmp = vl_msg_api_alloc (sizeof (*rmp)); clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_IPFIX_EXPORTER_DETAILS); + rmp->_vl_msg_id = + ntohs ((REPLY_MSG_ID_BASE) + VL_API_IPFIX_EXPORTER_DETAILS); rmp->context = mp->context; - memcpy (&collector.ip4, &frm->ipfix_collector, sizeof (ip4_address_t)); - ip_address_encode (&collector, IP46_TYPE_IP4, &rmp->collector_address); + ip_address_encode2 (&exp->ipfix_collector, &rmp->collector_address); + rmp->collector_port = htons (exp->collector_port); + ip_address_encode2 (&exp->src_address, &rmp->src_address); - rmp->collector_port = htons (frm->collector_port); + if (exp->fib_index == ~0) + vrf_id = ~0; + else + vrf_id = im->fibs[exp->fib_index].ft_table_id; + rmp->vrf_id = htonl (vrf_id); + rmp->path_mtu = htonl (exp->path_mtu); + rmp->template_interval = htonl (exp->template_interval); + rmp->udp_checksum = (exp->udp_checksum != 0); - memcpy (&src.ip4, &frm->src_address, sizeof (ip4_address_t)); - ip_address_encode (&src, IP46_TYPE_IP4, &rmp->src_address); + vl_api_send_msg (reg, (u8 *) rmp); +} - if (frm->fib_index == ~0) +static void +ipfix_all_fill_details (vl_api_ipfix_all_exporter_details_t *rmp, + ipfix_exporter_t *exp) +{ + ip4_main_t *im = &ip4_main; + u32 vrf_id; + + ip_address_encode2 (&exp->ipfix_collector, &rmp->collector_address); + rmp->collector_port = htons (exp->collector_port); + ip_address_encode2 (&exp->src_address, &rmp->src_address); + + if (exp->fib_index == ~0) vrf_id = ~0; else - vrf_id = im->fibs[frm->fib_index].ft_table_id; + vrf_id = im->fibs[exp->fib_index].ft_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); + rmp->path_mtu = htonl (exp->path_mtu); + rmp->template_interval = htonl (exp->template_interval); + rmp->udp_checksum = (exp->udp_checksum != 0); +} - vl_api_send_msg (reg, (u8 *) rmp); +static void +ipfix_all_exporter_details (flow_report_main_t *frm, u32 index, + vl_api_registration_t *rp, u32 context) +{ + ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, index); + + vl_api_ipfix_all_exporter_details_t *rmp; + + REPLY_MACRO_DETAILS4 (VL_API_IPFIX_ALL_EXPORTER_DETAILS, rp, context, + ({ ipfix_all_fill_details (rmp, exp); })); +} + +static void +vl_api_ipfix_all_exporter_get_t_handler (vl_api_ipfix_all_exporter_get_t *mp) +{ + flow_report_main_t *frm = &flow_report_main; + vl_api_ipfix_all_exporter_get_reply_t *rmp; + int rv = 0; + + REPLY_AND_DETAILS_MACRO ( + VL_API_IPFIX_ALL_EXPORTER_GET_REPLY, frm->exporters, + ({ ipfix_all_exporter_details (frm, cursor, rp, mp->context); })); } static void @@ -180,6 +307,7 @@ static void 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; + ipfix_exporter_t *exp = &frm->exporters[0]; u32 domain_id = 0; u32 src_port = UDP_DST_PORT_ipfix; int rv = 0; @@ -190,7 +318,7 @@ static void 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, + int rv = vnet_stream_change (exp, fcm->domain_id, fcm->src_port, domain_id, (u16) src_port); ASSERT (rv == 0); } @@ -231,6 +359,7 @@ static void vl_api_registration_t *reg; flow_report_classify_main_t *fcm = &flow_report_classify_main; flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp = &frm->exporters[0]; vnet_flow_report_add_del_args_t args; ipfix_classify_table_t *table; int is_add; @@ -296,7 +425,7 @@ static void args.domain_id = fcm->domain_id; args.src_port = fcm->src_port; - rv = vnet_flow_report_add_del (frm, &args, NULL); + rv = vnet_flow_report_add_del (exp, &args, NULL); /* If deleting, or add failed */ if (is_add == 0 || (rv && is_add)) diff --git a/src/vnet/ipfix-export/flow_report.c b/src/vnet/ipfix-export/flow_report.c index 760de5f8c66..4eb93520ed8 100644 --- a/src/vnet/ipfix-export/flow_report.c +++ b/src/vnet/ipfix-export/flow_report.c @@ -15,6 +15,7 @@ /* * flow_report.c */ +#include <vppinfra/atomics.h> #include <vnet/ipfix-export/flow_report.h> #include <vnet/api_errno.h> #include <vnet/udp/udp.h> @@ -22,45 +23,40 @@ flow_report_main_t flow_report_main; static_always_inline u8 -stream_index_valid (u32 index) +stream_index_valid (ipfix_exporter_t *exp, u32 index) { - flow_report_main_t *frm = &flow_report_main; - return index < vec_len (frm->streams) && - frm->streams[index].domain_id != ~0; + return index < vec_len (exp->streams) && exp->streams[index].domain_id != ~0; } static_always_inline flow_report_stream_t * -add_stream (void) +add_stream (ipfix_exporter_t *exp) { - 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]; + for (i = 0; i < vec_len (exp->streams); i++) + if (!stream_index_valid (exp, i)) + return &exp->streams[i]; + u32 index = vec_len (exp->streams); + vec_validate (exp->streams, index); + return &exp->streams[index]; } static_always_inline void -delete_stream (u32 index) +delete_stream (ipfix_exporter_t *exp, 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; + ASSERT (index < vec_len (exp->streams)); + ASSERT (exp->streams[index].domain_id != ~0); + exp->streams[index].domain_id = ~0; } static i32 -find_stream (u32 domain_id, u16 src_port) +find_stream (ipfix_exporter_t *exp, 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)) + for (i = 0; i < vec_len (exp->streams); i++) + if (stream_index_valid (exp, i)) { - stream = &frm->streams[i]; + stream = &exp->streams[i]; if (domain_id == stream->domain_id) { if (src_port != stream->src_port) @@ -76,14 +72,17 @@ find_stream (u32 domain_id, u16 src_port) } int -send_template_packet (flow_report_main_t * frm, - flow_report_t * fr, u32 * buffer_indexp) +send_template_packet (flow_report_main_t *frm, ipfix_exporter_t *exp, + flow_report_t *fr, u32 *buffer_indexp) { u32 bi0; vlib_buffer_t *b0; - ip4_ipfix_template_packet_t *tp; + ip4_ipfix_template_packet_t *tp4; + ip6_ipfix_template_packet_t *tp6; ipfix_message_header_t *h; - ip4_header_t *ip; + ip4_header_t *ip4; + ip6_header_t *ip6; + void *ip; udp_header_t *udp; vlib_main_t *vm = frm->vlib_main; flow_report_stream_t *stream; @@ -92,7 +91,8 @@ send_template_packet (flow_report_main_t * frm, if (fr->update_rewrite || fr->rewrite == 0) { - if (frm->ipfix_collector.as_u32 == 0 || frm->src_address.as_u32 == 0) + if (ip_address_is_zero (&exp->ipfix_collector) || + ip_address_is_zero (&exp->src_address)) { vlib_node_set_state (frm->vlib_main, flow_report_process_node.index, VLIB_NODE_STATE_DISABLED); @@ -104,13 +104,9 @@ send_template_packet (flow_report_main_t * frm, if (fr->update_rewrite) { - fr->rewrite = fr->rewrite_callback (frm, fr, - &frm->ipfix_collector, - &frm->src_address, - frm->collector_port, - fr->report_elements, - fr->n_report_elements, - fr->stream_indexp); + fr->rewrite = fr->rewrite_callback ( + exp, fr, exp->collector_port, fr->report_elements, + fr->n_report_elements, fr->stream_indexp); fr->update_rewrite = 0; } @@ -126,11 +122,22 @@ send_template_packet (flow_report_main_t * frm, b0->current_length = vec_len (fr->rewrite); b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT); vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = exp->fib_index; - tp = vlib_buffer_get_current (b0); - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + { + tp4 = vlib_buffer_get_current (b0); + ip4 = (ip4_header_t *) &tp4->ip4; + ip = ip4; + udp = (udp_header_t *) (ip4 + 1); + } + else + { + tp6 = vlib_buffer_get_current (b0); + ip6 = (ip6_header_t *) &tp6->ip6; + ip = ip6; + udp = (udp_header_t *) (ip6 + 1); + } h = (ipfix_message_header_t *) (udp + 1); /* FIXUP: message header export_time */ @@ -139,18 +146,30 @@ 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]; + stream = &exp->streams[fr->stream_index]; /* FIXUP: message header sequence_number. Templates do not increase it */ 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 (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip4)); + else + udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip6)); - if (frm->udp_checksum) + if (exp->udp_checksum || ip_addr_version (&exp->ipfix_collector) == AF_IP6) { /* RFC 7011 section 10.3.2. */ - udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); + + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); + else + { + int bogus = 0; + udp->checksum = + ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip, &bogus); + } + if (udp->checksum == 0) udp->checksum = 0xffff; } @@ -162,16 +181,58 @@ send_template_packet (flow_report_main_t * frm, return 0; } +u32 always_inline +ipfix_write_headers (ipfix_exporter_t *exp, void *data, void **ip, + udp_header_t **udp, u32 len) +{ + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + { + ip4_ipfix_template_packet_t *tp4; + ip4_header_t *ip4; + + tp4 = (ip4_ipfix_template_packet_t *) data; + ip4 = (ip4_header_t *) &tp4->ip4; + ip4->ip_version_and_header_length = 0x45; + ip4->ttl = 254; + ip4->protocol = IP_PROTOCOL_UDP; + ip4->flags_and_fragment_offset = 0; + ip4->src_address.as_u32 = exp->src_address.ip.ip4.as_u32; + ip4->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32; + *ip = ip4; + *udp = (udp_header_t *) (ip4 + 1); + + (*udp)->length = clib_host_to_net_u16 (len - sizeof (*ip4)); + return sizeof (*ip4); + } + else + { + ip6_ipfix_template_packet_t *tp6; + ip6_header_t *ip6; + + tp6 = (ip6_ipfix_template_packet_t *) data; + ip6 = (ip6_header_t *) &tp6->ip6; + ip6->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (6 << 28); + ip6->hop_limit = 254; + ip6->protocol = IP_PROTOCOL_UDP; + ip6->src_address = exp->src_address.ip.ip6; + ip6->dst_address = exp->ipfix_collector.ip.ip6; + *ip = ip6; + *udp = (udp_header_t *) (ip6 + 1); + (*udp)->length = clib_host_to_net_u16 (len - sizeof (*ip6)); + return sizeof (*ip6); + } +} + u8 * -vnet_flow_rewrite_generic_callback (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, +vnet_flow_rewrite_generic_callback (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, - ipfix_report_element_t * report_elts, - u32 n_elts, u32 * stream_indexp) + ipfix_report_element_t *report_elts, + u32 n_elts, u32 *stream_indexp) { - ip4_header_t *ip; + ip4_header_t *ip4; + ip6_header_t *ip6; + void *ip; udp_header_t *udp; ipfix_message_header_t *h; ipfix_set_header_t *s; @@ -179,41 +240,36 @@ vnet_flow_rewrite_generic_callback (flow_report_main_t * frm, ipfix_field_specifier_t *f; ipfix_field_specifier_t *first_field; u8 *rewrite = 0; - ip4_ipfix_template_packet_t *tp; flow_report_stream_t *stream; int i; ipfix_report_element_t *ep; + u32 size; ASSERT (stream_indexp); ASSERT (n_elts); ASSERT (report_elts); - stream = &frm->streams[fr->stream_index]; + stream = &exp->streams[fr->stream_index]; *stream_indexp = fr->stream_index; + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + size = sizeof (ip4_ipfix_template_packet_t); + else + size = sizeof (ip6_ipfix_template_packet_t); /* allocate rewrite space */ vec_validate_aligned (rewrite, - sizeof (ip4_ipfix_template_packet_t) - + n_elts * sizeof (ipfix_field_specifier_t) - 1, + size + n_elts * sizeof (ipfix_field_specifier_t) - 1, CLIB_CACHE_LINE_BYTES); /* create the packet rewrite string */ - tp = (ip4_ipfix_template_packet_t *) rewrite; - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); + ipfix_write_headers (exp, rewrite, &ip, &udp, vec_len (rewrite)); + h = (ipfix_message_header_t *) (udp + 1); s = (ipfix_set_header_t *) (h + 1); t = (ipfix_template_header_t *) (s + 1); first_field = f = (ipfix_field_specifier_t *) (t + 1); - - ip->ip_version_and_header_length = 0x45; - ip->ttl = 254; - 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 (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 LATER: message header export_time */ h->domain_id = clib_host_to_net_u32 (stream->domain_id); @@ -227,10 +283,6 @@ vnet_flow_rewrite_generic_callback (flow_report_main_t * frm, ep++; } - /* Back to the template packet... */ - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - ASSERT (f - first_field); /* Field count in this template */ t->id_count = ipfix_id_count (fr->template_id, f - first_field); @@ -242,12 +294,201 @@ vnet_flow_rewrite_generic_callback (flow_report_main_t * frm, /* message length in octets */ h->version_length = version_length ((u8 *) f - (u8 *) h); - ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip); - ip->checksum = ip4_header_checksum (ip); + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + { + ip4 = (ip4_header_t *) ip; + ip4->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip4); + ip4->checksum = ip4_header_checksum (ip4); + } + else + { + ip6 = (ip6_header_t *) ip; + /* IPv6 payload length does not include the IPv6 header */ + ip6->payload_length = clib_host_to_net_u16 ((u8 *) f - (u8 *) udp); + } return rewrite; } +vlib_buffer_t * +vnet_ipfix_exp_get_buffer (vlib_main_t *vm, ipfix_exporter_t *exp, + flow_report_t *fr, u32 thread_index) +{ + u32 bi0; + vlib_buffer_t *b0; + + if (fr->per_thread_data[thread_index].buffer) + return fr->per_thread_data[thread_index].buffer; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + return NULL; + + /* Initialize the buffer */ + b0 = fr->per_thread_data[thread_index].buffer = vlib_get_buffer (vm, bi0); + + b0->current_data = 0; + b0->current_length = exp->all_headers_size; + b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT); + vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = exp->fib_index; + fr->per_thread_data[thread_index].next_data_offset = b0->current_length; + + return b0; +} + +/* + * Send a buffer that is mostly populated. Has flow records but needs some + * header fields updated. + */ +void +vnet_ipfix_exp_send_buffer (vlib_main_t *vm, ipfix_exporter_t *exp, + flow_report_t *fr, flow_report_stream_t *stream, + u32 thread_index, vlib_buffer_t *b0) +{ + flow_report_main_t *frm = &flow_report_main; + vlib_frame_t *f; + ipfix_set_header_t *s; + ipfix_message_header_t *h; + ip4_header_t *ip4 = 0; + ip6_header_t *ip6 = 0; + void *ip; + udp_header_t *udp; + int ip_len; + + /* nothing to send */ + if (fr->per_thread_data[thread_index].next_data_offset <= + exp->all_headers_size) + return; + + ip_len = ipfix_write_headers (exp, (void *) vlib_buffer_get_current (b0), + &ip, &udp, b0->current_length); + + h = (ipfix_message_header_t *) (udp + 1); + s = (ipfix_set_header_t *) (h + 1); + + udp->src_port = clib_host_to_net_u16 (stream->src_port); + udp->dst_port = clib_host_to_net_u16 (exp->collector_port); + udp->checksum = 0; + + /* FIXUP: message header export_time */ + h->export_time = + (u32) (((f64) frm->unix_time_0) + (vlib_time_now (vm) - frm->vlib_time_0)); + h->export_time = clib_host_to_net_u32 (h->export_time); + h->domain_id = clib_host_to_net_u32 (stream->domain_id); + + /* + * RFC 7011: Section 3.2 + * + * Incremental sequence counter modulo 2^32 of all IPFIX Data Records + * sent in the current stream from the current Observation Domain by + * the Exporting Process + */ + h->sequence_number = + clib_atomic_fetch_add (&stream->sequence_number, + fr->per_thread_data[thread_index].n_data_records); + h->sequence_number = clib_host_to_net_u32 (h->sequence_number); + + /* + * For data records we use the template ID as the set ID. + * RFC 7011: 3.4.3 + */ + s->set_id_length = ipfix_set_id_length ( + fr->template_id, + b0->current_length - (ip_len + sizeof (*udp) + sizeof (*h))); + h->version_length = + version_length (b0->current_length - (ip_len + sizeof (*udp))); + + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + { + ip4 = (ip4_header_t *) ip; + ip4->length = clib_host_to_net_u16 (b0->current_length); + ip4->checksum = ip4_header_checksum (ip4); + udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip4)); + ASSERT (ip4_header_checksum_is_valid (ip4)); + } + else + { + ip6 = (ip6_header_t *) ip; + /* Ipv6 payload length does not include the IPv6 header */ + ip6->payload_length = + clib_host_to_net_u16 (b0->current_length - sizeof (*ip6)); + udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip6)); + } + + if (exp->udp_checksum || ip_addr_version (&exp->ipfix_collector) == AF_IP6) + { + /* RFC 7011 section 10.3.2. */ + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip4); + else + { + int bogus = 0; + udp->checksum = + ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip6, &bogus); + } + if (udp->checksum == 0) + udp->checksum = 0xffff; + } + + /* Find or allocate a frame */ + f = fr->per_thread_data[thread_index].frame; + if (PREDICT_FALSE (f == 0)) + { + u32 *to_next; + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + else + f = vlib_get_frame_to_node (vm, ip6_lookup_node.index); + fr->per_thread_data[thread_index].frame = f; + u32 bi0 = vlib_get_buffer_index (vm, b0); + + /* Enqueue the buffer */ + to_next = vlib_frame_vector_args (f); + to_next[0] = bi0; + f->n_vectors = 1; + } + + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); + else + vlib_put_frame_to_node (vm, ip6_lookup_node.index, f); + + fr->per_thread_data[thread_index].frame = NULL; + fr->per_thread_data[thread_index].buffer = NULL; + fr->per_thread_data[thread_index].next_data_offset = 0; +} + +static void +flow_report_process_send (vlib_main_t *vm, flow_report_main_t *frm, + ipfix_exporter_t *exp, flow_report_t *fr, + u32 next_node, u32 template_bi) +{ + vlib_frame_t *nf = 0; + u32 *to_next; + + nf = vlib_get_frame_to_node (vm, next_node); + nf->n_vectors = 0; + to_next = vlib_frame_vector_args (nf); + + if (template_bi != ~0) + { + to_next[0] = template_bi; + to_next++; + nf->n_vectors++; + } + + nf = fr->flow_data_callback (frm, exp, fr, nf, to_next, next_node); + if (nf) + { + if (nf->n_vectors) + vlib_put_frame_to_node (vm, next_node, nf); + else + { + vlib_frame_free (vm, nf); + } + } +} + static uword flow_report_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) @@ -256,9 +497,9 @@ flow_report_process (vlib_main_t * vm, flow_report_t *fr; u32 ip4_lookup_node_index; vlib_node_t *ip4_lookup_node; - vlib_frame_t *nf = 0; + u32 ip6_lookup_node_index; + vlib_node_t *ip6_lookup_node; u32 template_bi; - u32 *to_next; int send_template; f64 now, wait_time; f64 def_wait_time = 5.0; @@ -277,6 +518,10 @@ flow_report_process (vlib_main_t * vm, ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup"); ip4_lookup_node_index = ip4_lookup_node->index; + /* Enqueue pkts to ip6-lookup */ + ip6_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup"); + ip6_lookup_node_index = ip6_lookup_node->index; + wait_time = def_wait_time; while (1) @@ -284,82 +529,85 @@ flow_report_process (vlib_main_t * vm, vlib_process_wait_for_event_or_clock (vm, wait_time); event_type = vlib_process_get_events (vm, &event_data); vec_reset_length (event_data); + ipfix_exporter_t *exp; + pool_foreach (exp, frm->exporters) + { - /* 5s delay by default, possibly reduced by template intervals */ - wait_time = def_wait_time; - - vec_foreach (fr, frm->reports) - { - f64 next_template; - now = vlib_time_now (vm); - - /* Need to send a template packet? */ - send_template = - now > (fr->last_template_sent + frm->template_interval); - send_template += fr->last_template_sent == 0; - template_bi = ~0; - rv = 0; - - if (send_template) - rv = send_template_packet (frm, fr, &template_bi); - - if (rv < 0) - continue; - - /* decide if template should be sent sooner than current wait time */ - next_template = - (fr->last_template_sent + frm->template_interval) - now; - wait_time = clib_min (wait_time, next_template); - - nf = vlib_get_frame_to_node (vm, ip4_lookup_node_index); - nf->n_vectors = 0; - to_next = vlib_frame_vector_args (nf); - - if (template_bi != ~0) - { - to_next[0] = template_bi; - to_next++; - nf->n_vectors++; - } - - nf = fr->flow_data_callback (frm, fr, - nf, to_next, ip4_lookup_node_index); - if (nf) - vlib_put_frame_to_node (vm, ip4_lookup_node_index, nf); - } + /* 5s delay by default, possibly reduced by template intervals */ + wait_time = def_wait_time; + + vec_foreach (fr, exp->reports) + { + f64 next_template; + now = vlib_time_now (vm); + + /* Need to send a template packet? */ + send_template = + now > (fr->last_template_sent + exp->template_interval); + send_template += fr->last_template_sent == 0; + template_bi = ~0; + rv = 0; + + if (send_template) + rv = send_template_packet (frm, exp, fr, &template_bi); + + if (rv < 0) + continue; + + /* + * decide if template should be sent sooner than current wait + * time + */ + next_template = + (fr->last_template_sent + exp->template_interval) - now; + wait_time = clib_min (wait_time, next_template); + + if (ip_addr_version (&exp->ipfix_collector) == AF_IP4) + { + flow_report_process_send ( + vm, frm, exp, fr, ip4_lookup_node_index, template_bi); + } + else + { + flow_report_process_send ( + vm, frm, exp, fr, ip6_lookup_node_index, template_bi); + } + } + } } return 0; /* not so much */ } -/* *INDENT-OFF* */ VLIB_REGISTER_NODE (flow_report_process_node) = { .function = flow_report_process, .type = VLIB_NODE_TYPE_PROCESS, .name = "flow-report-process", }; -/* *INDENT-ON* */ int -vnet_flow_report_add_del (flow_report_main_t * frm, - vnet_flow_report_add_del_args_t * a, - u16 * template_id) +vnet_flow_report_add_del (ipfix_exporter_t *exp, + vnet_flow_report_add_del_args_t *a, u16 *template_id) { int i; int found_index = ~0; flow_report_t *fr; flow_report_stream_t *stream; u32 si; + vlib_thread_main_t *tm = &vlib_thread_main; + flow_report_main_t *frm = &flow_report_main; + vlib_main_t *vm = frm->vlib_main; + int size; - si = find_stream (a->domain_id, a->src_port); + si = find_stream (exp, 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++) + for (i = 0; i < vec_len (exp->reports); i++) { - fr = vec_elt_at_index (frm->reports, i); + fr = vec_elt_at_index (exp->reports, i); if (fr->opaque.as_uword == a->opaque.as_uword && fr->rewrite_callback == a->rewrite_callback && fr->flow_data_callback == a->flow_data_callback) @@ -375,11 +623,24 @@ 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]; + for (int i = 0; + i < vec_len (exp->reports[found_index].per_thread_data); i++) + { + u32 bi; + if (exp->reports[found_index].per_thread_data[i].buffer) + { + bi = vlib_get_buffer_index ( + vm, exp->reports[found_index].per_thread_data[i].buffer); + vlib_buffer_free (vm, &bi, 1); + } + } + vec_free (exp->reports[found_index].per_thread_data); + + vec_delete (exp->reports, 1, found_index); + stream = &exp->streams[si]; stream->n_reports--; if (stream->n_reports == 0) - delete_stream (si); + delete_stream (exp, si); return 0; } return VNET_API_ERROR_NO_SUCH_ENTRY; @@ -390,19 +651,19 @@ vnet_flow_report_add_del (flow_report_main_t * frm, if (si == -1) { - stream = add_stream (); + stream = add_stream (exp); stream->domain_id = a->domain_id; stream->src_port = a->src_port; stream->sequence_number = 0; stream->n_reports = 0; - si = stream - frm->streams; + si = stream - exp->streams; } else - stream = &frm->streams[si]; + stream = &exp->streams[si]; stream->n_reports++; - vec_add2 (frm->reports, fr, 1); + vec_add2 (exp->reports, fr, 1); fr->stream_index = si; fr->template_id = 256 + stream->next_template_no; @@ -414,6 +675,14 @@ vnet_flow_report_add_del (flow_report_main_t * frm, fr->report_elements = a->report_elements; fr->n_report_elements = a->n_report_elements; fr->stream_indexp = a->stream_indexp; + vec_validate (fr->per_thread_data, tm->n_threads); + /* Store the flow_report index back in the args struct */ + a->flow_report_index = fr - exp->reports; + + size = 0; + for (int i = 0; i < fr->n_report_elements; i++) + size += fr->report_elements[i].size; + fr->data_record_size = size; if (template_id) *template_id = fr->template_id; @@ -442,50 +711,50 @@ flow_report_add_del_error_to_clib_error (int error) } void -vnet_flow_reports_reset (flow_report_main_t * frm) +vnet_flow_reports_reset (ipfix_exporter_t *exp) { 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; + for (i = 0; i < vec_len (exp->streams); i++) + if (stream_index_valid (exp, i)) + exp->streams[i].sequence_number = 0; - vec_foreach (fr, frm->reports) - { - fr->update_rewrite = 1; - fr->last_template_sent = 0; - } + vec_foreach (fr, exp->reports) + { + fr->update_rewrite = 1; + fr->last_template_sent = 0; + } } void -vnet_stream_reset (flow_report_main_t * frm, u32 stream_index) +vnet_stream_reset (ipfix_exporter_t *exp, u32 stream_index) { flow_report_t *fr; - frm->streams[stream_index].sequence_number = 0; + exp->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; - } + vec_foreach (fr, exp->reports) + if (exp->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, +vnet_stream_change (ipfix_exporter_t *exp, 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); + i32 stream_index = find_stream (exp, old_domain_id, old_src_port); + if (stream_index < 0) return 1; - flow_report_stream_t *stream = &frm->streams[stream_index]; + flow_report_stream_t *stream = &exp->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); + vnet_stream_reset (exp, stream_index); return 0; } @@ -495,25 +764,26 @@ set_ipfix_exporter_command_fn (vlib_main_t * vm, vlib_cli_command_t * cmd) { flow_report_main_t *frm = &flow_report_main; - ip4_address_t collector, src; + ip_address_t collector = IP_ADDRESS_V4_ALL_0S, src = IP_ADDRESS_V4_ALL_0S; u16 collector_port = UDP_DST_PORT_ipfix; u32 fib_id; u32 fib_index = ~0; - collector.as_u32 = 0; - src.as_u32 = 0; u32 path_mtu = 512; // RFC 7011 section 10.3.3. u32 template_interval = 20; u8 udp_checksum = 0; + ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, 0); + u32 ip_header_size; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { - if (unformat (input, "collector %U", unformat_ip4_address, &collector)) + if (unformat (input, "collector %U", unformat_ip4_address, + &collector.ip.ip4)) ; else if (unformat (input, "port %U", unformat_udp_port, &collector_port)) ; - else if (unformat (input, "src %U", unformat_ip4_address, &src)) + else if (unformat (input, "src %U", unformat_ip4_address, &src.ip.ip4)) ; else if (unformat (input, "fib-id %u", &fib_id)) { @@ -533,8 +803,15 @@ set_ipfix_exporter_command_fn (vlib_main_t * vm, break; } - if (collector.as_u32 != 0 && src.as_u32 == 0) + /* + * If the collector address is set then the src must be too. + * Collector address can be set to 0 to disable exporter + */ + if (!ip_address_is_zero (&collector) && ip_address_is_zero (&src)) return clib_error_return (0, "src address required"); + if (collector.version != src.version) + return clib_error_return ( + 0, "src address and dest address must use same IP version"); if (path_mtu > 1450 /* vpp does not support fragmentation */ ) return clib_error_return (0, "too big path-mtu value, maximum is 1450"); @@ -542,28 +819,38 @@ set_ipfix_exporter_command_fn (vlib_main_t * vm, if (path_mtu < 68) return clib_error_return (0, "too small path-mtu value, minimum is 68"); + /* Calculate how much header data we need. */ + if (collector.version == AF_IP4) + ip_header_size = sizeof (ip4_header_t); + else + ip_header_size = sizeof (ip6_header_t); + exp->all_headers_size = ip_header_size + sizeof (udp_header_t) + + sizeof (ipfix_message_header_t) + + sizeof (ipfix_set_header_t); + /* Reset report streams if we are reconfiguring IP addresses */ - if (frm->ipfix_collector.as_u32 != collector.as_u32 || - frm->src_address.as_u32 != src.as_u32 || - frm->collector_port != collector_port) - vnet_flow_reports_reset (frm); - - frm->ipfix_collector.as_u32 = collector.as_u32; - frm->collector_port = collector_port; - frm->src_address.as_u32 = src.as_u32; - frm->fib_index = fib_index; - frm->path_mtu = path_mtu; - frm->template_interval = template_interval; - frm->udp_checksum = udp_checksum; - - if (collector.as_u32) - vlib_cli_output (vm, "Collector %U, src address %U, " + if (ip_address_cmp (&exp->ipfix_collector, &collector) || + ip_address_cmp (&exp->src_address, &src) || + exp->collector_port != collector_port) + vnet_flow_reports_reset (exp); + + exp->ipfix_collector = collector; + exp->collector_port = collector_port; + exp->src_address = src; + exp->fib_index = fib_index; + exp->path_mtu = path_mtu; + exp->template_interval = template_interval; + exp->udp_checksum = udp_checksum; + + if (collector.ip.ip4.as_u32) + vlib_cli_output (vm, + "Collector %U, src address %U, " "fib index %d, path MTU %u, " "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, + format_ip4_address, &exp->ipfix_collector.ip.ip4, + format_ip4_address, &exp->src_address.ip.ip4, fib_index, + path_mtu, template_interval, udp_checksum ? "enabled" : "disabled"); else vlib_cli_output (vm, "IPFIX Collector is disabled"); @@ -573,7 +860,6 @@ set_ipfix_exporter_command_fn (vlib_main_t * vm, return 0; } -/* *INDENT-OFF* */ VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = { .path = "set ipfix exporter", .short_help = "set ipfix exporter " @@ -584,7 +870,6 @@ VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = { "[udp-checksum]", .function = set_ipfix_exporter_command_fn, }; -/* *INDENT-ON* */ static clib_error_t * @@ -596,25 +881,31 @@ ipfix_flush_command_fn (vlib_main_t * vm, return 0; } -/* *INDENT-OFF* */ VLIB_CLI_COMMAND (ipfix_flush_command, static) = { .path = "ipfix flush", .short_help = "flush the current ipfix data [for make test]", .function = ipfix_flush_command_fn, }; -/* *INDENT-ON* */ static clib_error_t * flow_report_init (vlib_main_t * vm) { flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp; frm->vlib_main = 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; - + /* + * Make sure that we can always access the first exporter for + * backwards compatibility reasons. + */ + pool_alloc (frm->exporters, IPFIX_EXPORTERS_MAX); + pool_get (frm->exporters, exp); + /* Verify that this is at index 0 */ + ASSERT (frm->exporters == exp); + exp->fib_index = ~0; return 0; } diff --git a/src/vnet/ipfix-export/flow_report.h b/src/vnet/ipfix-export/flow_report.h index f40015879c4..cd0cafb6158 100644 --- a/src/vnet/ipfix-export/flow_report.h +++ b/src/vnet/ipfix-export/flow_report.h @@ -20,6 +20,7 @@ #include <vnet/ethernet/ethernet.h> #include <vnet/ethernet/packet.h> #include <vnet/ip/ip_packet.h> +#include <vnet/ip/ip_types.h> #include <vnet/ip/ip4_packet.h> #include <vnet/ip/ip6_packet.h> #include <vnet/udp/udp_packet.h> @@ -45,27 +46,31 @@ typedef struct ipfix_template_packet_t ipfix; } ip4_ipfix_template_packet_t; +/* Used to build the rewrite */ +typedef struct +{ + ip6_header_t ip6; + udp_header_t udp; + ipfix_template_packet_t ipfix; +} ip6_ipfix_template_packet_t; + struct flow_report_main; struct flow_report; +struct ipfix_exporter; -typedef vlib_frame_t *(vnet_flow_data_callback_t) (struct flow_report_main *, - struct flow_report *, - vlib_frame_t *, u32 *, - u32); +typedef vlib_frame_t *(vnet_flow_data_callback_t) ( + struct flow_report_main *frm, struct ipfix_exporter *exp, + struct flow_report *, vlib_frame_t *, u32 *, u32); -typedef u8 *(vnet_flow_rewrite_callback_t) (struct flow_report_main *, +typedef u8 *(vnet_flow_rewrite_callback_t) (struct ipfix_exporter *exp, struct flow_report *, - ip4_address_t *, - ip4_address_t *, u16, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index); - -u8 *vnet_flow_rewrite_generic_callback (struct flow_report_main *, - struct flow_report *, - ip4_address_t *, - ip4_address_t *, u16, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index); + u16, ipfix_report_element_t *elts, + u32 n_elts, u32 *stream_index); + +u8 *vnet_flow_rewrite_generic_callback (struct ipfix_exporter *exp, + struct flow_report *, u16, + ipfix_report_element_t *elts, + u32 n_elts, u32 *stream_index); typedef union { @@ -73,6 +78,16 @@ typedef union uword as_uword; } opaque_t; +/* + * A stream represents an IPFIX session to a destination. We can have + * multiple streams to the same destination, but each one has its own + * domain and source port. A stream has a sequence number for that + * session. A stream may contain multiple templates (i.e multiple for + * reports) and each stream also has its own template space. + * + * A stream has per thread state so that data packets can be built + * and send on multiple threads at the same time. + */ typedef struct { u32 domain_id; @@ -82,11 +97,37 @@ typedef struct u16 next_template_no; } flow_report_stream_t; +/* + * For each flow_report we want to be able to build buffers/frames per thread. + */ +typedef struct +{ + vlib_buffer_t *buffer; + vlib_frame_t *frame; + u16 next_data_offset; + /* + * We need this per stream as the IPFIX sequence number is the count of + * data record sent, not the count of packets with data records sent. + * See RFC 7011, Sec 3.1 + */ + u8 n_data_records; +} flow_report_per_thread_t; + +/* + * A flow report represents a group of fields that are to be exported. + * Each flow_report has an associated template that is generated when + * the flow_report is added. Each flow_report is associated with a + * stream, and multiple flow_reports can use the same stream. When + * adding a flow_report the keys for the stream are the domain_id + * and the source_port. + */ typedef struct flow_report { /* ipfix rewrite, set by callback */ u8 *rewrite; u16 template_id; + int data_record_size; + flow_report_per_thread_t *per_thread_data; u32 stream_index; f64 last_template_sent; int update_rewrite; @@ -107,15 +148,24 @@ typedef struct flow_report vnet_flow_data_callback_t *flow_data_callback; } flow_report_t; -typedef struct flow_report_main +/* + * The maximum number of ipfix exporters we can have at once + */ +#define IPFIX_EXPORTERS_MAX 5 + +/* + * We support multiple exporters. Each one has its own configured + * destination, and its own set of reports and streams. + */ +typedef struct ipfix_exporter { flow_report_t *reports; flow_report_stream_t *streams; /* ipfix collector ip address, port, our ip address, fib index */ - ip4_address_t ipfix_collector; + ip_address_t ipfix_collector; u16 collector_port; - ip4_address_t src_address; + ip_address_t src_address; u32 fib_index; /* Path MTU */ @@ -127,6 +177,23 @@ typedef struct flow_report_main /* UDP checksum calculation enable flag */ u8 udp_checksum; + /* + * The amount of data needed for all the headers, prior to the first + * flowset (template or data or ...) This is mostly dependent on the + * L3 and L4 protocols in use. + */ + u32 all_headers_size; +} ipfix_exporter_t; + +typedef struct flow_report_main +{ + /* + * A pool of the exporters. Entry 0 is always there for backwards + * compatability reasons. Entries 1 and above have to be created by + * the users. + */ + ipfix_exporter_t *exporters; + /* time scale transform. Joy. */ u32 unix_time_0; f64 vlib_time_0; @@ -142,8 +209,6 @@ extern flow_report_main_t flow_report_main; extern vlib_node_registration_t flow_report_process_node; -int vnet_flow_report_enable_disable (u32 sw_if_index, u32 table_index, - int enable_disable); typedef struct { vnet_flow_data_callback_t *flow_data_callback; @@ -155,21 +220,52 @@ typedef struct u32 domain_id; u16 src_port; u32 *stream_indexp; + /* + * When adding a flow report, the index of the flow report is stored + * here on success. + */ + u32 flow_report_index; } vnet_flow_report_add_del_args_t; -int vnet_flow_report_add_del (flow_report_main_t * frm, - vnet_flow_report_add_del_args_t * a, - u16 * template_id); +int vnet_flow_report_add_del (ipfix_exporter_t *exp, + vnet_flow_report_add_del_args_t *a, + u16 *template_id); clib_error_t *flow_report_add_del_error_to_clib_error (int error); -void vnet_flow_reports_reset (flow_report_main_t * frm); +void vnet_flow_reports_reset (ipfix_exporter_t *exp); -void vnet_stream_reset (flow_report_main_t * frm, u32 stream_index); +void vnet_stream_reset (ipfix_exporter_t *exp, 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); +int vnet_stream_change (ipfix_exporter_t *exp, u32 old_domain_id, + u16 old_src_port, u32 new_domain_id, u16 new_src_port); + +/* + * Search all the exporters for one that has a matching destination address. + */ +ipfix_exporter_t * +vnet_ipfix_exporter_lookup (const ip_address_t *ipfix_collector); + +/* + * Get the currently in use buffer for the given stream on the given core. + * If there is no current buffer then allocate a new one and return that. + * This is the buffer that data records should be written into. The offset + * currently in use is stored in the per-thread data for the stream and + * should be updated as new records are written in. + */ +vlib_buffer_t *vnet_ipfix_exp_get_buffer (vlib_main_t *vm, + ipfix_exporter_t *exp, + flow_report_t *fr, u32 thread_index); + +/* + * Send the provided buffer. At this stage the buffer should be populated + * with data records, with the offset in use stored in the stream per thread + * data. This func will fix up all the headers and then send the buffer. + */ +void vnet_ipfix_exp_send_buffer (vlib_main_t *vm, ipfix_exporter_t *exp, + flow_report_t *fr, + flow_report_stream_t *stream, + u32 thread_index, vlib_buffer_t *b0); #endif /* __included_vnet_flow_report_h__ */ diff --git a/src/vnet/ipfix-export/flow_report_classify.c b/src/vnet/ipfix-export/flow_report_classify.c index 21b6411a292..9e1b99f252d 100644 --- a/src/vnet/ipfix-export/flow_report_classify.c +++ b/src/vnet/ipfix-export/flow_report_classify.c @@ -29,13 +29,10 @@ typedef struct flow_report_classify_main_t flow_report_classify_main; u8 * -ipfix_classify_template_rewrite (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, +ipfix_classify_template_rewrite (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index) + ipfix_report_element_t *elts, u32 n_elts, + u32 *stream_index) { flow_report_classify_main_t *fcm = &flow_report_classify_main; vnet_classify_table_t *tblp; @@ -61,7 +58,7 @@ ipfix_classify_template_rewrite (flow_report_main_t * frm, u8 *virt_mask; u8 *real_mask; - stream = &frm->streams[fr->stream_index]; + stream = &exp->streams[fr->stream_index]; ipfix_classify_table_t *table = &fcm->tables[flow_table_index]; @@ -109,8 +106,8 @@ ipfix_classify_template_rewrite (flow_report_main_t * frm, ip->ip_version_and_header_length = 0x45; ip->ttl = 254; ip->protocol = IP_PROTOCOL_UDP; - ip->src_address.as_u32 = src_address->as_u32; - ip->dst_address.as_u32 = collector_address->as_u32; + ip->src_address.as_u32 = exp->src_address.ip.ip4.as_u32; + ip->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32; 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)); @@ -158,9 +155,9 @@ ipfix_classify_template_rewrite (flow_report_main_t * frm, } 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) +ipfix_classify_send_flows (flow_report_main_t *frm, ipfix_exporter_t *exp, + 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; @@ -182,7 +179,6 @@ ipfix_classify_send_flows (flow_report_main_t * frm, tcpudp_header_t *tcpudp; udp_header_t *udp; int field_index; - u32 records_this_buffer; u16 new_l0, old_l0; ip_csum_t sum0; vlib_main_t *vm = frm->vlib_main; @@ -191,7 +187,7 @@ ipfix_classify_send_flows (flow_report_main_t * frm, u8 transport_protocol; u8 *virt_key; - stream = &frm->streams[fr->stream_index]; + stream = &exp->streams[fr->stream_index]; ipfix_classify_table_t *table = &fcm->tables[flow_table_index]; @@ -233,7 +229,7 @@ ipfix_classify_send_flows (flow_report_main_t * frm, b0->current_length = copy_len; b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID; vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = exp->fib_index; tp = vlib_buffer_get_current (b0); ip = (ip4_header_t *) & tp->ip4; @@ -254,7 +250,6 @@ ipfix_classify_send_flows (flow_report_main_t * frm, next_offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp); record_offset = next_offset; - records_this_buffer = 0; } field_index = 0; @@ -278,14 +273,13 @@ ipfix_classify_send_flows (flow_report_main_t * frm, sizeof (packets)); next_offset += sizeof (packets); } - records_this_buffer++; stream->sequence_number++; /* Next record will have the same size as this record */ u32 next_record_size = next_offset - record_offset; record_offset = next_offset; - if (next_offset + next_record_size > frm->path_mtu) + if (next_offset + next_record_size > exp->path_mtu) { s->set_id_length = ipfix_set_id_length (fr->template_id, next_offset - @@ -314,7 +308,7 @@ ipfix_classify_send_flows (flow_report_main_t * frm, udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip)); - if (frm->udp_checksum) + if (exp->udp_checksum) { /* RFC 7011 section 10.3.2. */ udp->checksum = @@ -370,7 +364,7 @@ flush: ip->length = new_l0; udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip)); - if (frm->udp_checksum) + if (exp->udp_checksum) { /* RFC 7011 section 10.3.2. */ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); @@ -397,7 +391,7 @@ ipfix_classify_table_add_del_command_fn (vlib_main_t * vm, vlib_cli_command_t * cmd) { flow_report_classify_main_t *fcm = &flow_report_classify_main; - flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp = &flow_report_main.exporters[0]; vnet_flow_report_add_del_args_t args; ipfix_classify_table_t *table; int rv; @@ -475,7 +469,7 @@ ipfix_classify_table_add_del_command_fn (vlib_main_t * vm, args.domain_id = fcm->domain_id; args.src_port = fcm->src_port; - rv = vnet_flow_report_add_del (frm, &args, NULL); + rv = vnet_flow_report_add_del (exp, &args, NULL); error = flow_report_add_del_error_to_clib_error (rv); @@ -486,13 +480,11 @@ ipfix_classify_table_add_del_command_fn (vlib_main_t * vm, return error; } -/* *INDENT-OFF* */ 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, }; -/* *INDENT-ON* */ static clib_error_t * set_ipfix_classify_stream_command_fn (vlib_main_t * vm, @@ -500,7 +492,7 @@ set_ipfix_classify_stream_command_fn (vlib_main_t * vm, vlib_cli_command_t * cmd) { flow_report_classify_main_t *fcm = &flow_report_classify_main; - flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp = &flow_report_main.exporters[0]; u32 domain_id = 1; u32 src_port = UDP_DST_PORT_ipfix; @@ -518,7 +510,7 @@ set_ipfix_classify_stream_command_fn (vlib_main_t * vm, 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, + int rv = vnet_stream_change (exp, fcm->domain_id, fcm->src_port, domain_id, (u16) src_port); ASSERT (rv == 0); } @@ -529,14 +521,12 @@ set_ipfix_classify_stream_command_fn (vlib_main_t * vm, return 0; } -/* *INDENT-OFF* */ 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, }; -/* *INDENT-ON* */ static clib_error_t * flow_report_classify_init (vlib_main_t * vm) diff --git a/src/vnet/ipfix-export/flow_report_classify.h b/src/vnet/ipfix-export/flow_report_classify.h index a923f36714a..8ca40688599 100644 --- a/src/vnet/ipfix-export/flow_report_classify.h +++ b/src/vnet/ipfix-export/flow_report_classify.h @@ -112,18 +112,15 @@ ipfix_classify_delete_table (u32 index) 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, +u8 *ipfix_classify_template_rewrite (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index); + ipfix_report_element_t *elts, u32 n_elts, + u32 *stream_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); +vlib_frame_t *ipfix_classify_send_flows (flow_report_main_t *frm, + ipfix_exporter_t *exp, + flow_report_t *fr, vlib_frame_t *f, + u32 *to_next, u32 node_index); #endif /* __included_flow_report_classify_h__ */ diff --git a/src/vnet/ipfix-export/ipfix_export.api b/src/vnet/ipfix-export/ipfix_export.api index a70b72bee39..8a9d5b13124 100644 --- a/src/vnet/ipfix-export/ipfix_export.api +++ b/src/vnet/ipfix-export/ipfix_export.api @@ -73,6 +73,80 @@ define ipfix_exporter_details bool udp_checksum; }; +/** Configure IPFIX exporter within the exporting process. + The exporting process can contain multiple independent exporters, + each of which have their own state. The collector_address is the key + field that identifies a unique exporter. The already existing API + 'set_ipfix_exporter' is used to modify a single exporter (which will + always have stat index 0). If more than one exporter is required then + they can be created and deleted using this API. + + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_create - True for create, False for delete + @param collector_address - address of 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_exporter_create_delete { + u32 client_index; + u32 context; + bool is_create; + vl_api_address_t collector_address; + u16 collector_port; + vl_api_address_t src_address; + u32 vrf_id; + u32 path_mtu; + u32 template_interval; + bool udp_checksum; +}; + +define ipfix_exporter_create_delete_reply { + u32 context; + i32 retval; + u32 stat_index; +}; + +service { + rpc ipfix_all_exporter_get returns ipfix_all_exporter_get_reply + stream ipfix_all_exporter_details; +}; + +define ipfix_all_exporter_get +{ + u32 client_index; + u32 context; + u32 cursor; +}; + +define ipfix_all_exporter_get_reply +{ + u32 context; + i32 retval; + u32 cursor; +}; + +/** \brief Ipfix meter details in response to the get_meters command + @param context - sender context, to match reply w/ request + @param name The name of the ipfix meter +*/ +define ipfix_all_exporter_details +{ + u32 context; + vl_api_address_t collector_address; + u16 collector_port; + vl_api_address_t src_address; + u32 vrf_id; + u32 path_mtu; + u32 template_interval; + bool 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 |