summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2017-01-13 04:15:54 -0800
committerDamjan Marion <dmarion.lists@gmail.com>2017-01-14 09:44:18 +0000
commiteea28d78a3173341727aafee4c414bcb01001339 (patch)
treefe657b651619b62b7b33cbfabe999e770728f650 /src/plugins
parent2d0b6e380be3acec05f4acea9e4f1b9bb730ee88 (diff)
SNAT: IPFIX logging (VPP-445)
Change-Id: I8450217dd43a1cd9f510e40dfb22274ffc33a4c6 Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/snat.am3
-rw-r--r--src/plugins/snat/in2out.c17
-rw-r--r--src/plugins/snat/out2in.c10
-rw-r--r--src/plugins/snat/snat.api24
-rw-r--r--src/plugins/snat/snat.c112
-rw-r--r--src/plugins/snat/snat_ipfix_logging.c653
-rw-r--r--src/plugins/snat/snat_ipfix_logging.h68
-rw-r--r--src/plugins/snat/snat_test.c48
8 files changed, 925 insertions, 10 deletions
diff --git a/src/plugins/snat.am b/src/plugins/snat.am
index 7ff2386ec83..8611d15c740 100644
--- a/src/plugins/snat.am
+++ b/src/plugins/snat.am
@@ -18,7 +18,8 @@ vppplugins_LTLIBRARIES += snat_plugin.la
snat_plugin_la_SOURCES = snat/snat.c \
snat/in2out.c \
snat/out2in.c \
- snat/snat_plugin.api.h
+ snat/snat_plugin.api.h \
+ snat/snat_ipfix_logging.c
API_FILES += snat/snat.api
diff --git a/src/plugins/snat/in2out.c b/src/plugins/snat/in2out.c
index cd8f1271808..76a6a12cec5 100644
--- a/src/plugins/snat/in2out.c
+++ b/src/plugins/snat/in2out.c
@@ -22,6 +22,7 @@
#include <vnet/ethernet/ethernet.h>
#include <vnet/fib/ip4_fib.h>
#include <snat/snat.h>
+#include <snat/snat_ipfix_logging.h>
#include <vppinfra/hash.h>
#include <vppinfra/error.h>
@@ -213,6 +214,14 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 0 /* is_add */))
clib_warning ("out2in key delete failed");
+ /* log NAT event */
+ snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
+ s->out2in.addr.as_u32,
+ s->in2out.protocol,
+ s->in2out.port,
+ s->out2in.port,
+ s->in2out.fib_index);
+
snat_free_outside_address_and_port
(sm, &s->out2in, s->outside_address_index);
s->outside_address_index = ~0;
@@ -302,6 +311,14 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
kv0.key = worker_by_out_key.as_u64;
kv0.value = cpu_index;
clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1);
+
+ /* log NAT event */
+ snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32,
+ s->out2in.addr.as_u32,
+ s->in2out.protocol,
+ s->in2out.port,
+ s->out2in.port,
+ s->in2out.fib_index);
return next0;
}
diff --git a/src/plugins/snat/out2in.c b/src/plugins/snat/out2in.c
index 0c9c9cd12b6..f1329733ecf 100644
--- a/src/plugins/snat/out2in.c
+++ b/src/plugins/snat/out2in.c
@@ -22,6 +22,7 @@
#include <vnet/ethernet/ethernet.h>
#include <vnet/fib/ip4_fib.h>
#include <snat/snat.h>
+#include <snat/snat_ipfix_logging.h>
#include <vppinfra/hash.h>
#include <vppinfra/error.h>
@@ -210,7 +211,14 @@ create_session_for_static_mapping (snat_main_t *sm,
if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */))
clib_warning ("out2in key add failed");
- return s;
+ /* log NAT event */
+ snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32,
+ s->out2in.addr.as_u32,
+ s->in2out.protocol,
+ s->in2out.port,
+ s->out2in.port,
+ s->in2out.fib_index);
+ return s;
}
static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
diff --git a/src/plugins/snat/snat.api b/src/plugins/snat/snat.api
index f046a9657e2..ff1d9bc1b8a 100644
--- a/src/plugins/snat/snat.api
+++ b/src/plugins/snat/snat.api
@@ -318,3 +318,27 @@ define snat_interface_addr_details {
u32 context;
u32 sw_if_index;
};
+
+/** \brief Enable/disable S-NAT IPFIX logging
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param domain_id - observation domain ID
+ @param src_port - source port number
+ @param enable - 1 if enable, 0 if disable
+*/
+define snat_ipfix_enable_disable {
+ u32 client_index;
+ u32 context;
+ u32 domain_id;
+ u16 src_port;
+ u8 enable;
+};
+
+/** \brief Enable/disable S-NAT IPFIX logging reply
+ @param context - sender context, to match reply w/ request
+ @param retval - return code
+*/
+define snat_ipfix_enable_disable_reply {
+ u32 context;
+ i32 retval;
+};
diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c
index a1236cf74f4..9edb0d5699b 100644
--- a/src/plugins/snat/snat.c
+++ b/src/plugins/snat/snat.c
@@ -21,6 +21,7 @@
#include <vnet/plugin/plugin.h>
#include <vlibapi/api.h>
#include <snat/snat.h>
+#include <snat/snat_ipfix_logging.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
@@ -278,6 +279,13 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr)
pool_foreach (ses, tsm->sessions, ({
if (ses->out2in.addr.as_u32 == addr.as_u32)
{
+ /* log NAT event */
+ snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32,
+ ses->out2in.addr.as_u32,
+ ses->in2out.protocol,
+ ses->in2out.port,
+ ses->out2in.port,
+ ses->in2out.fib_index);
vec_add1 (ses_to_be_removed, ses - tsm->sessions);
kv.key = ses->in2out.as_u64;
clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0);
@@ -550,6 +558,14 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
continue;
}
+ /* log NAT event */
+ snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
+ s->out2in.addr.as_u32,
+ s->in2out.protocol,
+ s->in2out.port,
+ s->out2in.port,
+ s->in2out.fib_index);
+
value.key = s->in2out.as_u64;
clib_bihash_add_del_8_8 (&sm->in2out, &value, 0);
value.key = s->out2in.as_u64;
@@ -1172,6 +1188,37 @@ static void *vl_api_snat_interface_addr_dump_t_print
FINISH;
}
+static void
+vl_api_snat_ipfix_enable_disable_t_handler
+(vl_api_snat_ipfix_enable_disable_t * mp)
+{
+ snat_main_t * sm = &snat_main;
+ vl_api_snat_ipfix_enable_disable_reply_t * rmp;
+ int rv = 0;
+
+ rv = snat_ipfix_logging_enable_disable(mp->enable,
+ clib_host_to_net_u32 (mp->domain_id),
+ clib_host_to_net_u16 (mp->src_port));
+
+ REPLY_MACRO (VL_API_SNAT_IPFIX_ENABLE_DISABLE_REPLY);
+}
+
+static void *vl_api_snat_ipfix_enable_disable_t_print
+(vl_api_snat_ipfix_enable_disable_t *mp, void * handle)
+{
+ u8 * s;
+
+ s = format (0, "SCRIPT: snat_ipfix_enable_disable ");
+ if (mp->domain_id)
+ s = format (s, "domain %d ", clib_net_to_host_u32 (mp->domain_id));
+ if (mp->src_port)
+ s = format (s, "src_port %d ", clib_net_to_host_u16 (mp->src_port));
+ if (!mp->enable)
+ s = format (s, "disable ");
+
+ FINISH;
+}
+
/* List of message types that this plugin understands */
#define foreach_snat_plugin_api_msg \
_(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range) \
@@ -1185,7 +1232,8 @@ _(SNAT_INTERFACE_DUMP, snat_interface_dump) \
_(SNAT_SET_WORKERS, snat_set_workers) \
_(SNAT_WORKER_DUMP, snat_worker_dump) \
_(SNAT_ADD_DEL_INTERFACE_ADDR, snat_add_del_interface_addr) \
-_(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump)
+_(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump) \
+_(SNAT_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable)
/* Set up the API message handling tables */
static clib_error_t *
@@ -1303,6 +1351,9 @@ static clib_error_t * snat_init (vlib_main_t * vm)
vec_add1 (im->add_del_interface_address_callbacks, cb4);
+ /* Init IPFIX logging */
+ snat_ipfix_logging_init(vm);
+
return error;
}
@@ -1420,6 +1471,7 @@ int snat_alloc_outside_address_and_port (snat_main_t * sm,
}
}
/* Totally out of translations to use... */
+ snat_ipfix_logging_addresses_exhausted(0);
return 1;
}
@@ -1720,6 +1772,58 @@ VLIB_CLI_COMMAND (set_workers_command, static) = {
};
static clib_error_t *
+snat_ipfix_logging_enable_disable_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u32 domain_id = 0;
+ u32 src_port = 0;
+ u8 enable = 1;
+ int rv = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "domain %d", &domain_id))
+ ;
+ else if (unformat (line_input, "src-port %d", &src_port))
+ ;
+ else if (unformat (line_input, "disable"))
+ enable = 0;
+ else
+ return clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, input);
+ }
+ unformat_free (line_input);
+
+ rv = snat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port);
+
+ if (rv)
+ return clib_error_return (0, "ipfix logging enable failed");
+
+ return 0;
+}
+
+/*?
+ * @cliexpar
+ * @cliexstart{snat ipfix logging}
+ * To enable SNAT IPFIX logging use:
+ * vpp# snat ipfix logging
+ * To set IPFIX exporter use:
+ * vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (snat_ipfix_logging_enable_disable_command, static) = {
+ .path = "snat ipfix logging",
+ .function = snat_ipfix_logging_enable_disable_command_fn,
+ .short_help = "snat ipfix logging [domain <domain-id>] [src-port <port>] [disable]",
+};
+
+static clib_error_t *
snat_config (vlib_main_t * vm, unformat_input_t * input)
{
snat_main_t * sm = &snat_main;
@@ -1968,7 +2072,7 @@ show_snat_command_fn (vlib_main_t * vm,
({
s = format (s, " %d", j);
}));
- vlib_cli_output (vm, " %d busy ports:%v", ap->busy_ports, s);
+ vlib_cli_output (vm, " %d busy ports:%s", ap->busy_ports, s);
}
}
@@ -1981,7 +2085,7 @@ show_snat_command_fn (vlib_main_t * vm,
{
vlib_worker_thread_t *w =
vlib_worker_threads + *worker + sm->first_worker_index;
- vlib_cli_output (vm, " %v", w->name);
+ vlib_cli_output (vm, " %s", w->name);
}
}
}
@@ -2032,7 +2136,7 @@ show_snat_command_fn (vlib_main_t * vm,
continue;
vlib_worker_thread_t *w = vlib_worker_threads + j;
- vlib_cli_output (vm, "Thread %d (%v at lcore %u):", j, w->name,
+ vlib_cli_output (vm, "Thread %d (%s at lcore %u):", j, w->name,
w->lcore_id);
vlib_cli_output (vm, " %d list pool elements",
pool_elts (tsm->list_pool));
diff --git a/src/plugins/snat/snat_ipfix_logging.c b/src/plugins/snat/snat_ipfix_logging.c
new file mode 100644
index 00000000000..d72eb226675
--- /dev/null
+++ b/src/plugins/snat/snat_ipfix_logging.c
@@ -0,0 +1,653 @@
+/*
+ * snat_ipfix_logging.c - NAT Events IPFIX logging
+ *
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * 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.
+ */
+
+#include <vnet/flow/flow_report.h>
+#include <vlibmemory/api.h>
+#include <snat/snat.h>
+#include <snat/snat_ipfix_logging.h>
+
+snat_ipfix_logging_main_t snat_ipfix_logging_main;
+
+#define NAT44_SESSION_CREATE_LEN 26
+#define NAT_ADDRESSES_EXHAUTED_LEN 13
+
+#define NAT44_SESSION_CREATE_FIELD_COUNT 8
+#define NAT_ADDRESSES_EXHAUTED_FIELD_COUNT 3
+
+typedef struct {
+ u8 nat_event;
+ u32 src_ip;
+ u32 nat_src_ip;
+ snat_protocol_t snat_proto;
+ u16 src_port;
+ u16 nat_src_port;
+ u32 vrf_id;
+} snat_ipfix_logging_nat44_ses_args_t;
+
+typedef struct {
+ u32 pool_id;
+} snat_ipfix_logging_addr_exhausted_args_t;
+
+/**
+ * @brief Create an IPFIX template packet rewrite string
+ *
+ * @param frm flow report main
+ * @param fr flow report
+ * @param collector_address collector address
+ * @param src_address source address
+ * @param collector_port collector
+ * @param event NAT event ID
+ *
+ * @returns template packet
+ */
+static inline u8 *
+snat_template_rewrite (flow_report_main_t * frm,
+ flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address,
+ u16 collector_port,
+ nat_event_t event)
+{
+ snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main;
+ 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];
+ silm->stream_index = fr->stream_index;
+
+ if (event == NAT_ADDRESSES_EXHAUTED)
+ {
+ field_count = NAT_ADDRESSES_EXHAUTED_FIELD_COUNT;
+ silm->addr_exhausted_template_id = fr->template_id;
+ }
+ else if (event == NAT44_SESSION_CREATE)
+ {
+ field_count = NAT44_SESSION_CREATE_FIELD_COUNT;
+ silm->nat44_session_template_id = fr->template_id;
+ }
+
+ /* 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 */
+ h->domain_id = clib_host_to_net_u32 (stream->domain_id);
+
+ /* Add TLVs to the template */
+ if (event == NAT_ADDRESSES_EXHAUTED)
+ {
+ f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, 8);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, natEvent, 1);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, natPoolId, 4);
+ f++;
+ }
+ else if (event == NAT44_SESSION_CREATE)
+ {
+ f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, 8);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, natEvent, 1);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, sourceIPv4Address, 4);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, postNATSourceIPv4Address, 4);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, protocolIdentifier, 1);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, sourceTransportPort, 2);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, postNAPTSourceTransportPort, 2);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0, ingressVRFID, 4);
+ f++;
+ }
+
+ /* 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;
+}
+
+u8 *
+snat_template_rewrite_addr_exhausted (flow_report_main_t * frm,
+ flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address,
+ u16 collector_port)
+{
+ return snat_template_rewrite (frm, fr, collector_address, src_address,
+ collector_port, NAT_ADDRESSES_EXHAUTED);
+}
+
+u8 *
+snat_template_rewrite_nat44_session (flow_report_main_t * frm,
+ flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address,
+ u16 collector_port)
+{
+ return snat_template_rewrite (frm, fr, collector_address, src_address,
+ collector_port, NAT44_SESSION_CREATE);
+}
+
+static inline void
+snat_ipfix_header_create (flow_report_main_t * frm,
+ vlib_buffer_t * b0,
+ u32 * offset)
+{
+ snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main;
+ flow_report_stream_t *stream;
+ ip4_ipfix_template_packet_t * tp;
+ ipfix_message_header_t * h = 0;
+ ipfix_set_header_t * s = 0;
+ ip4_header_t * ip;
+ udp_header_t * udp;
+
+ stream = &frm->streams[silm->stream_index];
+
+ b0->current_data = 0;
+ b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
+ sizeof (*s);
+ b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT);
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
+ tp = vlib_buffer_get_current (b0);
+ 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);
+
+ ip->ip_version_and_header_length = 0x45;
+ ip->ttl = 254;
+ ip->protocol = IP_PROTOCOL_UDP;
+ ip->flags_and_fragment_offset = 0;
+ ip->src_address.as_u32 = frm->src_address.as_u32;
+ ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
+ udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
+ udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
+ udp->checksum = 0;
+
+ h->export_time = clib_host_to_net_u32 (
+ (u32) (((f64)frm->unix_time_0) + (vlib_time_now(frm->vlib_main) -
+ frm->vlib_time_0)));
+ h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++);
+ h->domain_id = clib_host_to_net_u32 (stream->domain_id);
+
+ *offset = (u32) (((u8 *)(s+1)) - (u8 *)tp);
+}
+
+static inline void
+snat_ipfix_send (flow_report_main_t * frm,
+ vlib_frame_t * f,
+ vlib_buffer_t * b0,
+ u16 template_id)
+{
+ ip4_ipfix_template_packet_t * tp;
+ ipfix_message_header_t * h = 0;
+ ipfix_set_header_t * s = 0;
+ ip4_header_t * ip;
+ udp_header_t * udp;
+ vlib_main_t * vm = frm->vlib_main;
+
+ tp = vlib_buffer_get_current (b0);
+ 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);
+
+ s->set_id_length = ipfix_set_id_length (template_id,
+ b0->current_length -
+ (sizeof (*ip) + sizeof (*udp) +
+ sizeof (*h)));
+ h->version_length = version_length (b0->current_length -
+ (sizeof (*ip) + sizeof (*udp)));
+
+ ip->length = clib_host_to_net_u16 (b0->current_length);
+ ip->checksum = ip4_header_checksum (ip);
+ udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
+
+ if (frm->udp_checksum)
+ {
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+ }
+
+ ASSERT (ip->checksum == ip4_header_checksum (ip));
+
+ vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
+}
+
+static void
+snat_ipfix_logging_nat44_ses (u8 nat_event, u32 src_ip, u32 nat_src_ip,
+ snat_protocol_t snat_proto, u16 src_port,
+ u16 nat_src_port, u32 vrf_id, int do_flush)
+{
+ snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main;
+ flow_report_main_t *frm = &flow_report_main;
+ vlib_frame_t *f;
+ vlib_buffer_t *b0 = 0;
+ u32 bi0 = ~0;
+ u32 offset;
+ vlib_main_t * vm = frm->vlib_main;
+ u64 now;
+ vlib_buffer_free_list_t *fl;
+ u8 proto = ~0;
+
+ if (!silm->enabled)
+ return;
+
+ proto = (snat_proto == SNAT_PROTOCOL_UDP) ? IP_PROTOCOL_UDP : proto;
+ proto = (snat_proto == SNAT_PROTOCOL_TCP) ? IP_PROTOCOL_TCP : proto;
+ proto = (snat_proto == SNAT_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP : proto;
+
+ now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3);
+ now += silm->milisecond_time_0;
+
+ b0 = silm->nat44_session_buffer;
+
+ if (PREDICT_FALSE (b0 == 0))
+ {
+ if (do_flush)
+ return;
+
+ if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
+ {
+ clib_warning ("can't allocate buffer for NAT IPFIX event");
+ return;
+ }
+
+ b0 = silm->nat44_session_buffer =
+ vlib_get_buffer (vm, bi0);
+ fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
+ vlib_buffer_init_for_free_list (b0, fl);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+ offset = 0;
+ }
+ else
+ {
+ bi0 = vlib_get_buffer_index (vm, b0);
+ offset = silm->nat44_session_next_record_offset;
+ }
+
+ f = silm->nat44_session_frame;
+ if (PREDICT_FALSE (f == 0))
+ {
+ u32 * to_next;
+ f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
+ silm->nat44_session_frame = f;
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+ }
+
+ if (PREDICT_FALSE (offset == 0))
+ snat_ipfix_header_create (frm, b0, &offset);
+
+ if (PREDICT_TRUE (do_flush == 0))
+ {
+ u64 time_stamp = clib_host_to_net_u64 (now);
+ clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp));
+ offset += sizeof (time_stamp);
+
+ clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event));
+ offset += sizeof (nat_event);
+
+ clib_memcpy (b0->data + offset, &src_ip, sizeof (src_ip));
+ offset += sizeof (src_ip);
+
+ clib_memcpy (b0->data + offset, &nat_src_ip, sizeof (nat_src_ip));
+ offset += sizeof (nat_src_ip);
+
+ clib_memcpy (b0->data + offset, &proto, sizeof (proto));
+ offset += sizeof (proto);
+
+ clib_memcpy (b0->data + offset, &src_port, sizeof (src_port));
+ offset += sizeof (src_port);
+
+ clib_memcpy (b0->data + offset, &nat_src_port, sizeof (nat_src_port));
+ offset += sizeof (nat_src_port);
+
+ clib_memcpy (b0->data + offset, &vrf_id, sizeof(vrf_id));
+ offset += sizeof (vrf_id);
+
+ b0->current_length += NAT44_SESSION_CREATE_LEN;
+ }
+
+ if (PREDICT_FALSE (do_flush || (offset + NAT44_SESSION_CREATE_LEN) > frm->path_mtu))
+ {
+ snat_ipfix_send (frm, f, b0, silm->nat44_session_template_id);
+ silm->nat44_session_frame = 0;
+ silm->nat44_session_buffer = 0;
+ offset = 0;
+ }
+ silm->nat44_session_next_record_offset = offset;
+ }
+
+static void
+snat_ipfix_logging_addr_exhausted (u32 pool_id, int do_flush)
+{
+ snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main;
+ flow_report_main_t *frm = &flow_report_main;
+ vlib_frame_t *f;
+ vlib_buffer_t *b0 = 0;
+ u32 bi0 = ~0;
+ u32 offset;
+ vlib_main_t * vm = frm->vlib_main;
+ u64 now;
+ vlib_buffer_free_list_t *fl;
+ u8 nat_event = NAT_ADDRESSES_EXHAUTED;
+
+ if (!silm->enabled)
+ return;
+
+ now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3);
+ now += silm->milisecond_time_0;
+
+ b0 = silm->addr_exhausted_buffer;
+
+ if (PREDICT_FALSE (b0 == 0))
+ {
+ if (do_flush)
+ return;
+
+ if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
+ {
+ clib_warning ("can't allocate buffer for NAT IPFIX event");
+ return;
+ }
+
+ b0 = silm->addr_exhausted_buffer =
+ vlib_get_buffer (vm, bi0);
+ fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
+ vlib_buffer_init_for_free_list (b0, fl);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+ offset = 0;
+ }
+ else
+ {
+ bi0 = vlib_get_buffer_index (vm, b0);
+ offset = silm->addr_exhausted_next_record_offset;
+ }
+
+ f = silm->addr_exhausted_frame;
+ if (PREDICT_FALSE (f == 0))
+ {
+ u32 * to_next;
+ f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
+ silm->addr_exhausted_frame = f;
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+ }
+
+ if (PREDICT_FALSE (offset == 0))
+ snat_ipfix_header_create (frm, b0, &offset);
+
+ if (PREDICT_TRUE (do_flush == 0))
+ {
+ u64 time_stamp = clib_host_to_net_u64 (now);
+ clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp));
+ offset += sizeof (time_stamp);
+
+ clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event));
+ offset += sizeof (nat_event);
+
+ clib_memcpy (b0->data + offset, &pool_id, sizeof(pool_id));
+ offset += sizeof (pool_id);
+
+ b0->current_length += NAT_ADDRESSES_EXHAUTED_LEN;
+ }
+
+ if (PREDICT_FALSE (do_flush || (offset + NAT_ADDRESSES_EXHAUTED_LEN) > frm->path_mtu))
+ {
+ snat_ipfix_send (frm, f, b0, silm->addr_exhausted_template_id);
+ silm->addr_exhausted_frame = 0;
+ silm->addr_exhausted_buffer = 0;
+ offset = 0;
+ }
+ silm->addr_exhausted_next_record_offset = offset;
+ }
+
+static void
+snat_ipfix_logging_nat44_ses_rpc_cb (snat_ipfix_logging_nat44_ses_args_t *a)
+{
+ snat_ipfix_logging_nat44_ses(a->nat_event, a->src_ip, a->nat_src_ip,
+ a->snat_proto, a->src_port, a->nat_src_port,
+ a->vrf_id, 0);
+}
+
+/**
+ * @brief Generate NAT44 session create event
+ *
+ * @param src_ip source IPv4 address
+ * @param nat_src_ip transaltes source IPv4 address
+ * @param snat_proto SNAT transport protocol
+ * @param src_port source port
+ * @param nat_src_port translated source port
+ * @param vrf_id VRF ID
+ */
+void
+snat_ipfix_logging_nat44_ses_create (u32 src_ip,
+ u32 nat_src_ip,
+ snat_protocol_t snat_proto,
+ u16 src_port,
+ u16 nat_src_port,
+ u32 vrf_id)
+{
+ snat_ipfix_logging_nat44_ses_args_t a;
+
+ a.nat_event = NAT44_SESSION_CREATE;
+ a.src_ip = src_ip;
+ a.nat_src_ip = nat_src_ip;
+ a.snat_proto = snat_proto;
+ a.src_port = src_port;
+ a.nat_src_port = nat_src_port;
+ a.vrf_id = vrf_id;
+
+ vl_api_rpc_call_main_thread (snat_ipfix_logging_nat44_ses_rpc_cb, (u8 *) &a,
+ sizeof (a));
+}
+
+/**
+ * @brief Generate NAT44 session delete event
+ *
+ * @param src_ip source IPv4 address
+ * @param nat_src_ip transaltes source IPv4 address
+ * @param snat_proto SNAT transport protocol
+ * @param src_port source port
+ * @param nat_src_port translated source port
+ * @param vrf_id VRF ID
+ */
+void
+snat_ipfix_logging_nat44_ses_delete (u32 src_ip,
+ u32 nat_src_ip,
+ snat_protocol_t snat_proto,
+ u16 src_port,
+ u16 nat_src_port,
+ u32 vrf_id)
+{
+ snat_ipfix_logging_nat44_ses_args_t a;
+
+ a.nat_event = NAT44_SESSION_DELETE;
+ a.src_ip = src_ip;
+ a.nat_src_ip = nat_src_ip;
+ a.snat_proto = snat_proto;
+ a.src_port = src_port;
+ a.nat_src_port = nat_src_port;
+ a.vrf_id = vrf_id;
+
+ vl_api_rpc_call_main_thread (snat_ipfix_logging_nat44_ses_rpc_cb, (u8 *) &a,
+ sizeof (a));
+}
+
+vlib_frame_t *
+snat_data_callback_nat44_session (flow_report_main_t * frm,
+ flow_report_t * fr,
+ vlib_frame_t * f,
+ u32 * to_next,
+ u32 node_index)
+{
+ snat_ipfix_logging_nat44_ses(0, 0, 0, 0, 0, 0, 0, 1);
+ return f;
+}
+
+static void
+snat_ipfix_logging_addr_exhausted_rpc_cb
+ (snat_ipfix_logging_addr_exhausted_args_t * a)
+{
+ snat_ipfix_logging_addr_exhausted(a->pool_id, 0);
+}
+
+/**
+ * @brief Generate NAT addresses exhausted event
+ *
+ * @param pool_id NAT pool ID
+ */
+void
+snat_ipfix_logging_addresses_exhausted(u32 pool_id)
+{
+ //TODO: This event SHOULD be rate limited
+ snat_ipfix_logging_addr_exhausted_args_t a;
+
+ a.pool_id = pool_id;
+
+ vl_api_rpc_call_main_thread (snat_ipfix_logging_addr_exhausted_rpc_cb,
+ (u8 *) &a, sizeof (a));
+}
+
+vlib_frame_t *
+snat_data_callback_addr_exhausted (flow_report_main_t * frm,
+ flow_report_t * fr,
+ vlib_frame_t * f,
+ u32 * to_next,
+ u32 node_index)
+{
+ snat_ipfix_logging_addr_exhausted(0, 1);
+ return f;
+}
+
+/**
+ * @brief Enable/disable SNAT IPFIX logging
+ *
+ * @param enable 1 if enable, 0 if disable
+ * @param domain_id observation domain ID
+ * @param src_port source port number
+ *
+ * @returns 0 if success
+ */
+int
+snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port)
+{
+ snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main;
+ flow_report_main_t *frm = &flow_report_main;
+ vnet_flow_report_add_del_args_t a;
+ int rv;
+ u8 e = enable ? 1 : 0;
+
+ if (silm->enabled == e)
+ return 0;
+
+ silm->enabled = e;
+
+ memset (&a, 0, sizeof (a));
+ a.rewrite_callback = snat_template_rewrite_nat44_session;
+ a.flow_data_callback = snat_data_callback_nat44_session;
+ a.is_add = enable;
+ a.domain_id = domain_id ? domain_id : 1;
+ a.src_port = src_port ? src_port : UDP_DST_PORT_ipfix;
+
+ rv = vnet_flow_report_add_del (frm, &a);
+ if (rv)
+ {
+ clib_warning ("vnet_flow_report_add_del returned %d", rv);
+ return -1;
+ }
+
+ a.rewrite_callback = snat_template_rewrite_addr_exhausted;
+ a.flow_data_callback = snat_data_callback_addr_exhausted;
+
+ rv = vnet_flow_report_add_del (frm, &a);
+ if (rv)
+ {
+ clib_warning ("vnet_flow_report_add_del returned %d", rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Initialize SNAT IPFIX logging
+ *
+ * @param vm vlib main
+ */
+void
+snat_ipfix_logging_init (vlib_main_t * vm)
+{
+ snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main;
+
+ silm->enabled = 0;
+
+ /* Set up time reference pair */
+ silm->vlib_time_0 = vlib_time_now (vm);
+ silm->milisecond_time_0 = unix_time_now_nsec () * 1e-6;
+}
diff --git a/src/plugins/snat/snat_ipfix_logging.h b/src/plugins/snat/snat_ipfix_logging.h
new file mode 100644
index 00000000000..b968ee21e9c
--- /dev/null
+++ b/src/plugins/snat/snat_ipfix_logging.h
@@ -0,0 +1,68 @@
+/*
+ * snat_ipfix_logging.h - NAT Events IPFIX logging
+ *
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * 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.
+ */
+#ifndef __included_snat_ipfix_logging_h__
+#define __included_snat_ipfix_logging_h__
+
+typedef enum {
+ NAT_ADDRESSES_EXHAUTED = 3,
+ NAT44_SESSION_CREATE = 4,
+ NAT44_SESSION_DELETE = 5,
+ NAT_PORTS_EXHAUSTED = 12,
+} nat_event_t;
+
+typedef struct {
+ /** S-NAT IPFIX logging enabled */
+ u8 enabled;
+
+ /** ipfix buffers under construction */
+ vlib_buffer_t *nat44_session_buffer;
+ vlib_buffer_t *addr_exhausted_buffer;
+
+ /** frames containing ipfix buffers */
+ vlib_frame_t *nat44_session_frame;
+ vlib_frame_t *addr_exhausted_frame;
+
+ /** next record offset */
+ u32 nat44_session_next_record_offset;
+ u32 addr_exhausted_next_record_offset;
+
+ /** Time reference pair */
+ u64 milisecond_time_0;
+ f64 vlib_time_0;
+
+ /** template IDs */
+ u16 nat44_session_template_id;
+ u16 addr_exhausted_template_id;
+
+ /** stream index */
+ u32 stream_index;
+} snat_ipfix_logging_main_t;
+
+extern snat_ipfix_logging_main_t snat_ipfix_logging_main;
+
+void snat_ipfix_logging_init (vlib_main_t * vm);
+int snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port);
+void snat_ipfix_logging_nat44_ses_create (u32 src_ip, u32 nat_src_ip,
+ snat_protocol_t snat_proto,
+ u16 src_port, u16 nat_src_port,
+ u32 vrf_id);
+void snat_ipfix_logging_nat44_ses_delete (u32 src_ip, u32 nat_src_ip,
+ snat_protocol_t snat_proto,
+ u16 src_port, u16 nat_src_port,
+ u32 vrf_id);
+void snat_ipfix_logging_addresses_exhausted(u32 pool_id);
+#endif /* __included_snat_ipfix_logging_h__ */
diff --git a/src/plugins/snat/snat_test.c b/src/plugins/snat/snat_test.c
index 6f87d80379b..013d7d9b700 100644
--- a/src/plugins/snat/snat_test.c
+++ b/src/plugins/snat/snat_test.c
@@ -62,7 +62,8 @@ _(snat_add_address_range_reply) \
_(snat_interface_add_del_feature_reply) \
_(snat_add_static_mapping_reply) \
_(snat_set_workers_reply) \
-_(snat_add_del_interface_addr_reply)
+_(snat_add_del_interface_addr_reply) \
+_(snat_ipfix_enable_disable_reply)
#define _(n) \
static void vl_api_##n##_t_handler \
@@ -98,7 +99,9 @@ _(SNAT_SET_WORKERS_REPLY, snat_set_workers_reply) \
_(SNAT_WORKER_DETAILS, snat_worker_details) \
_(SNAT_ADD_DEL_INTERFACE_ADDR_REPLY, \
snat_add_del_interface_addr_reply) \
-_(SNAT_INTERFACE_ADDR_DETAILS, snat_interface_addr_details)
+_(SNAT_INTERFACE_ADDR_DETAILS, snat_interface_addr_details) \
+_(SNAT_IPFIX_ENABLE_DISABLE_REPLY, \
+ snat_ipfix_enable_disable_reply)
/* M: construct, but don't yet send a message */
#define M(T,t) \
@@ -543,7 +546,7 @@ static int api_snat_worker_dump(vat_main_t * vam)
return 0;
}
-static int api_snat_add_del_interface_addr (vat_main_t * vam)
+static int api_snat_ipfix_enable_disable (vat_main_t * vam)
{
snat_test_main_t * sm = &snat_test_main;
unformat_input_t * i = vam->input;
@@ -617,6 +620,41 @@ static int api_snat_interface_addr_dump(vat_main_t * vam)
return 0;
}
+static int api_snat_add_del_interface_addr (vat_main_t * vam)
+{
+ snat_test_main_t * sm = &snat_test_main;
+ unformat_input_t * i = vam->input;
+ f64 timeout;
+ vl_api_snat_ipfix_enable_disable_t * mp;
+ u32 domain_id = 0;
+ u32 src_port = 0;
+ u8 enable = 1;
+
+ 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 if (unformat (i, "disable"))
+ enable = 0;
+ else
+ {
+ clib_warning("unknown input '%U'", format_unformat_error, i);
+ return -99;
+ }
+ }
+
+ M(SNAT_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable);
+ mp->domain_id = htonl(domain_id);
+ mp->src_port = htons((u16) src_port);
+ mp->enable = enable;
+
+ S; W;
+ /* NOTREACHED */
+ return 0;
+}
+
/*
* List of messages that the api test plugin sends,
* and that the data plane plugin processes
@@ -635,7 +673,9 @@ _(snat_interface_dump, "") \
_(snat_worker_dump, "") \
_(snat_add_del_interface_addr, \
"<intfc> | sw_if_index <id> [del]") \
-_(snat_interface_addr_dump, "")
+_(snat_interface_addr_dump, "") \
+_(snat_ipfix_enable_disable, "[domain <id>] [src_port <n>] " \
+ "[disable]")
void vat_api_hookup (vat_main_t *vam)
{