Damjan Marion 8b3191e6d7 feature: convert all feature nodes to new feature infra
Change-Id: I34c527ba910fb282a95458b78d1d684eb337905e
Signed-off-by: Damjan Marion <damarion@cisco.com>
2016-11-11 14:39:00 +00:00

491 lines
15 KiB
C

/*
* flowperpkt.c - per-packet data capture flow report plugin
*
* Copyright (c) <current-year> <your-organization>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* @brief Per-packet IPFIX flow record generator plugin
*
* This file implements vpp plugin registration mechanics,
* debug CLI, and binary API handling.
*/
#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <flowperpkt/flowperpkt.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vlibsocket/api.h>
/* define message IDs */
#include <flowperpkt/flowperpkt_msg_enum.h>
/* define message structures */
#define vl_typedefs
#include <flowperpkt/flowperpkt_all_api_h.h>
#undef vl_typedefs
/* define generated endian-swappers */
#define vl_endianfun
#include <flowperpkt/flowperpkt_all_api_h.h>
#undef vl_endianfun
/* instantiate all the print functions we know about */
#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
#define vl_printfun
#include <flowperpkt/flowperpkt_all_api_h.h>
#undef vl_printfun
flowperpkt_main_t flowperpkt_main;
/* Get the API version number */
#define vl_api_version(n,v) static u32 api_version=(v);
#include <flowperpkt/flowperpkt_all_api_h.h>
#undef vl_api_version
/* Define the per-interface configurable feature */
/* *INDENT-OFF* */
VNET_FEATURE_INIT (flow_perpacket, static) = {
.arc_name = "ip4-output",
.node_name = "flowperpkt",
.runs_before = VNET_FEATURES ("interface-output"),
};
/* *INDENT-ON* */
/*
* A handy macro to set up a message reply.
* Assumes that the following variables are available:
* mp - pointer to request message
* rmp - pointer to reply message type
* rv - return value
*/
#define REPLY_MACRO(t) \
do { \
unix_shared_memory_queue_t * q = \
vl_api_client_index_to_input_queue (mp->client_index); \
if (!q) \
return; \
\
rmp = vl_msg_api_alloc (sizeof (*rmp)); \
rmp->_vl_msg_id = ntohs((t)+fm->msg_id_base); \
rmp->context = mp->context; \
rmp->retval = ntohl(rv); \
\
vl_msg_api_send_shmem (q, (u8 *)&rmp); \
} while(0);
/* Macro to finish up custom dump fns */
#define FINISH \
vec_add1 (s, 0); \
vl_print (handle, (char *)s); \
vec_free (s); \
return handle;
#define VALIDATE_SW_IF_INDEX(mp) \
do { u32 __sw_if_index = ntohl(mp->sw_if_index); \
vnet_main_t *__vnm = vnet_get_main(); \
if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
__sw_if_index)) { \
rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \
goto bad_sw_if_index; \
} \
} while(0);
#define BAD_SW_IF_INDEX_LABEL \
do { \
bad_sw_if_index: \
; \
} while (0);
/**
* @brief Create an IPFIX template packet rewrite string
* @param frm flow_report_main_t *
* @param fr flow_report_t *
* @param collector_address ip4_address_t * the IPFIX collector address
* @param src_address ip4_address_t * the source address we should use
* @param collector_port u16 the collector port we should use, host byte order
* @returns u8 * vector containing the indicated IPFIX template packet
*/
u8 *
flowperpkt_template_rewrite (flow_report_main_t * frm,
flow_report_t * fr,
ip4_address_t * collector_address,
ip4_address_t * src_address, u16 collector_port)
{
ip4_header_t *ip;
udp_header_t *udp;
ipfix_message_header_t *h;
ipfix_set_header_t *s;
ipfix_template_header_t *t;
ipfix_field_specifier_t *f;
ipfix_field_specifier_t *first_field;
u8 *rewrite = 0;
ip4_ipfix_template_packet_t *tp;
u32 field_count = 0;
flow_report_stream_t *stream;
stream = &frm->streams[fr->stream_index];
/*
* Supported Fields:
*
* egressInterface, TLV type 14, u32
* ipClassOfService, TLV type 5, u8
* flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
* Implementation: f64 nanoseconds since VPP started
* warning: wireshark doesn't really understand this TLV
* dataLinkFrameSize, TLV type 312, u16
* warning: wireshark doesn't understand this TLV at all
*/
/* Currently 4 fields */
field_count += 4;
/* allocate rewrite space */
vec_validate_aligned (rewrite,
sizeof (ip4_ipfix_template_packet_t)
+ field_count * sizeof (ipfix_field_specifier_t) - 1,
CLIB_CACHE_LINE_BYTES);
tp = (ip4_ipfix_template_packet_t *) rewrite;
ip = (ip4_header_t *) & tp->ip4;
udp = (udp_header_t *) (ip + 1);
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: message header export_time */
/* FIXUP: message header sequence_number */
h->domain_id = clib_host_to_net_u32 (stream->domain_id);
/* Add TLVs to the template */
f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , egressInterface,
4);
f++;
f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
1);
f++;
f->e_id_length =
ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
8);
f++;
f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
2);
f++;
/* Extend in the obvious way, right here... */
/* 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);
/* set length in octets */
s->set_id_length =
ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
/* 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);
return rewrite;
}
/**
* @brief Flush accumulated data
* @param frm flow_report_main_t *
* @param fr flow_report_t *
* @param f vlib_frame_t *
*
* <em>Notes:</em>
* This function must simply return the incoming frame, or no template packets
* will be sent.
*/
vlib_frame_t *
flowperpkt_data_callback (flow_report_main_t * frm,
flow_report_t * fr,
vlib_frame_t * f, u32 * to_next, u32 node_index)
{
flowperpkt_flush_callback ();
return f;
}
/**
* @brief configure / deconfigure the IPFIX flow-per-packet
* @param fm flowperpkt_main_t * fm
* @param sw_if_index u32 the desired interface
* @param is_add int 1 to enable the feature, 0 to disable it
* @returns 0 if successful, non-zero otherwise
*/
static int flowperpkt_tx_interface_add_del_feature
(flowperpkt_main_t * fm, u32 sw_if_index, int is_add)
{
flow_report_main_t *frm = &flow_report_main;
vnet_flow_report_add_del_args_t _a, *a = &_a;
int rv;
if (!fm->report_created)
{
memset (a, 0, sizeof (*a));
a->rewrite_callback = flowperpkt_template_rewrite;
a->flow_data_callback = flowperpkt_data_callback;
a->is_add = 1;
a->domain_id = 1; /*$$$$ config parameter */
a->src_port = 4739; /*$$$$ config parameter */
fm->report_created = 1;
rv = vnet_flow_report_add_del (frm, a);
if (rv)
{
clib_warning ("vnet_flow_report_add_del returned %d", rv);
return -1;
}
}
vnet_feature_enable_disable ("ip4-output", "flowperpkt", sw_if_index,
is_add, 0, 0);
return 0;
}
/**
* @brief API message handler
* @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
*/
void vl_api_flowperpkt_tx_interface_add_del_t_handler
(vl_api_flowperpkt_tx_interface_add_del_t * mp)
{
flowperpkt_main_t *fm = &flowperpkt_main;
vl_api_flowperpkt_tx_interface_add_del_reply_t *rmp;
u32 sw_if_index = ntohl (mp->sw_if_index);
int rv = 0;
VALIDATE_SW_IF_INDEX (mp);
rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->is_add);
BAD_SW_IF_INDEX_LABEL;
REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
}
/**
* @brief API message custom-dump function
* @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
* @param handle void * print function handle
* @returns u8 * output string
*/
static void *vl_api_flowperpkt_tx_interface_add_del_t_print
(vl_api_flowperpkt_tx_interface_add_del_t * mp, void *handle)
{
u8 *s;
s = format (0, "SCRIPT: flowperpkt_tx_interface_add_del ");
s = format (s, "sw_if_index %d is_add %d is_ipv6 %d ",
clib_host_to_net_u32 (mp->sw_if_index),
(int) mp->is_add, (int) mp->is_ipv6);
FINISH;
}
/* List of message types that this plugin understands */
#define foreach_flowperpkt_plugin_api_msg \
_(FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del)
/**
* @brief plugin-api required function
* @param vm vlib_main_t * vlib main data structure pointer
* @param h vlib_plugin_handoff_t * handoff structure
* @param from_early_init int notused
*
* <em>Notes:</em>
* This routine exists to convince the vlib plugin framework that
* we haven't accidentally copied a random .dll into the plugin directory.
*
* Also collects global variable pointers passed from the vpp engine
*/
clib_error_t *
vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
int from_early_init)
{
flowperpkt_main_t *fm = &flowperpkt_main;
clib_error_t *error = 0;
fm->vlib_main = vm;
fm->vnet_main = h->vnet_main;
return error;
}
static clib_error_t *
flowperpkt_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd)
{
flowperpkt_main_t *fm = &flowperpkt_main;
u32 sw_if_index = ~0;
int is_add = 1;
int rv;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "disable"))
is_add = 0;
else if (unformat (input, "%U", unformat_vnet_sw_interface,
fm->vnet_main, &sw_if_index))
;
else
break;
}
if (sw_if_index == ~0)
return clib_error_return (0, "Please specify an interface...");
rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, is_add);
switch (rv)
{
case 0:
break;
case VNET_API_ERROR_INVALID_SW_IF_INDEX:
return clib_error_return
(0, "Invalid interface, only works on physical ports");
break;
case VNET_API_ERROR_UNIMPLEMENTED:
return clib_error_return (0, "ip6 not supported");
break;
default:
return clib_error_return (0, "flowperpkt_enable_disable returned %d",
rv);
}
return 0;
}
/*?
* '<em>flowperpkt feature add-del</em>' commands to enable/disable
* per-packet IPFIX flow record generation on an interface
*
* @cliexpar
* @parblock
* To enable per-packet IPFIX flow-record generation on an interface:
* @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
*
* To disable per-packet IPFIX flow-record generation on an interface:
* @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
* @cliexend
* @endparblock
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
.path = "flowperpkt feature add-del",
.short_help =
"flowperpkt feature add-del <interface-name> [disable]",
.function = flowperpkt_tx_interface_add_del_feature_command_fn,
};
/* *INDENT-ON* */
/**
* @brief Set up the API message handling tables
* @param vm vlib_main_t * vlib main data structure pointer
* @returns 0 to indicate all is well
*/
static clib_error_t *
flowperpkt_plugin_api_hookup (vlib_main_t * vm)
{
flowperpkt_main_t *fm = &flowperpkt_main;
#define _(N,n) \
vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base), \
#n, \
vl_api_##n##_t_handler, \
vl_noop_handler, \
vl_api_##n##_t_endian, \
vl_api_##n##_t_print, \
sizeof(vl_api_##n##_t), 1);
foreach_flowperpkt_plugin_api_msg;
#undef _
return 0;
}
/**
* @brief Set up the API message handling tables
* @param vm vlib_main_t * vlib main data structure pointer
* @returns 0 to indicate all is well, or a clib_error_t
*/
static clib_error_t *
flowperpkt_init (vlib_main_t * vm)
{
flowperpkt_main_t *fm = &flowperpkt_main;
vlib_thread_main_t *tm = &vlib_thread_main;
clib_error_t *error = 0;
u32 num_threads;
u8 *name;
/* Construct the API name */
name = format (0, "flowperpkt_%08x%c", api_version, 0);
/* Ask for a correctly-sized block of API message decode slots */
fm->msg_id_base = vl_msg_api_get_msg_ids
((char *) name, VL_MSG_FIRST_AVAILABLE);
/* Hook up message handlers */
error = flowperpkt_plugin_api_hookup (vm);
vec_free (name);
/* Decide how many worker threads we have */
num_threads = 1 /* main thread */ + tm->n_eal_threads;
/* Allocate per worker thread vectors */
vec_validate (fm->buffers_per_worker, num_threads - 1);
vec_validate (fm->frames_per_worker, num_threads - 1);
vec_validate (fm->next_record_offset_per_worker, num_threads - 1);
/* Set up time reference pair */
fm->vlib_time_0 = vlib_time_now (vm);
fm->nanosecond_time_0 = unix_time_now_nsec ();
return error;
}
VLIB_INIT_FUNCTION (flowperpkt_init);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/