summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2018-11-19 04:25:32 -0800
committerOle Trøan <otroan@employees.org>2018-11-22 06:31:53 +0000
commitb4515b4be4ead1ea5d0af205989cf313272e8770 (patch)
tree8a87c5c74cd8583690bdcf612727a2b4f4136cf2
parent19ca78fbd7567c676beefc2b511dfdcd9f20201a (diff)
Add RFC5424 syslog protocol support (VPP-1139)
Syslog protocol logging transport event messages across network over UDP protocol based on RFC5426. Change-Id: Ica74b40bcc2e6d0fbd41e9bf78e76395fbabab3c Signed-off-by: Matus Fabian <matfabia@cisco.com>
-rw-r--r--doxygen/dev_doc.md1
-rw-r--r--src/vnet/CMakeLists.txt17
-rw-r--r--src/vnet/syslog/dir.dox18
-rw-r--r--src/vnet/syslog/sylog_doc.md65
-rw-r--r--src/vnet/syslog/syslog.api119
-rw-r--r--src/vnet/syslog/syslog.c646
-rw-r--r--src/vnet/syslog/syslog.h225
-rw-r--r--src/vnet/syslog/syslog_api.c243
-rw-r--r--src/vnet/syslog/syslog_udp.c72
-rw-r--r--src/vnet/syslog/syslog_udp.h36
-rw-r--r--src/vnet/vnet_all_api_h.h1
-rw-r--r--test/Makefile2
-rw-r--r--test/test_syslog.py196
-rw-r--r--test/vpp_papi_provider.py50
14 files changed, 1690 insertions, 1 deletions
diff --git a/doxygen/dev_doc.md b/doxygen/dev_doc.md
index 690a8d8d89f..83c11ed89d3 100644
--- a/doxygen/dev_doc.md
+++ b/doxygen/dev_doc.md
@@ -11,3 +11,4 @@ Programming notes for developers.
- @subpage acl_multicore
- @subpage acl_lookup_context
- @subpage libmemif_doc
+- @subpage syslog_doc
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index ac448e268b6..290a4eeacc9 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -1355,6 +1355,23 @@ list(APPEND VNET_HEADERS
list(APPEND VNET_API_FILES bier/bier.api)
##############################################################################
+# SYSLOG
+##############################################################################
+
+list (APPEND VNET_SOURCES
+ syslog/syslog_api.c
+ syslog/syslog_udp.c
+ syslog/syslog.c
+)
+
+list(APPEND VNET_HEADERS
+ syslog/syslog_udp.h
+ syslog/syslog.h
+)
+
+list(APPEND VNET_API_FILES syslog/syslog.api)
+
+##############################################################################
# VNET Library
##############################################################################
diff --git a/src/vnet/syslog/dir.dox b/src/vnet/syslog/dir.dox
new file mode 100644
index 00000000000..ea64053e1df
--- /dev/null
+++ b/src/vnet/syslog/dir.dox
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+/**
+ @dir
+ @brief RFC5424 syslog protocol implementation
+*/
diff --git a/src/vnet/syslog/sylog_doc.md b/src/vnet/syslog/sylog_doc.md
new file mode 100644
index 00000000000..0b48d4db573
--- /dev/null
+++ b/src/vnet/syslog/sylog_doc.md
@@ -0,0 +1,65 @@
+# Syslog protocol support {#syslog_doc}
+
+VPP provides [RFC5424](https://tools.ietf.org/html/rfc5424) syslog protocol
+logging, which is used to transport event messages across network. VPP
+currently suports UDP transport based on
+[RFC5426](https://tools.ietf.org/html/rfc5426).
+
+The syslog message has the following format:
+* header
+* structured data
+* free-form message
+
+The header contains, priority, version, timestamp, hostname, application,
+process id and message id. It is followed by structured data, which provides
+a mechanism to express event data in easily parsable format. Structured data
+can contain zero, one or multiple structured data elements. Structured data
+element contains name-value pairs. Structured data can by followed by free-form
+message.
+
+Following example explains how to use the internal APIs to genrate syslog
+message:
+```{.c}
+ #include <vnet/syslog/syslog.h>
+
+ ...
+
+ syslog_msg_t syslog_msg;
+
+ /* Check if syslog logging is enabled */
+ if (!syslog_is_enabled ())
+ return;
+
+ /* Severity filer test */
+ if (syslog_severity_filter_block (severity))
+ return;
+
+ /* Initialize syslog message header */
+ syslog_msg_init (&syslog_msg, facility, severity, "NAT", "SADD");
+
+ /* Create structured data element */
+ syslog_msg_sd_init (&syslog_msg, "nsess");
+ /* Add structured data element parameters (name-value pairs) */
+ syslog_msg_add_sd_param (&syslog_msg, "SSUBIX", "%d", ssubix);
+ syslog_msg_add_sd_param (&syslog_msg, "SVLAN", "%d", svlan);
+ syslog_msg_add_sd_param (&syslog_msg, "IATYP", "IPv4");
+ syslog_msg_add_sd_param (&syslog_msg, "ISADDR", "%U",
+ format_ip4_address, isaddr);
+ syslog_msg_add_sd_param (&syslog_msg, "ISPORT", "%d", isport);
+ syslog_msg_add_sd_param (&syslog_msg, "XATYP", "IPv4");
+ syslog_msg_add_sd_param (&syslog_msg, "XSADDR", "%U",
+ format_ip4_address, xsaddr);
+ syslog_msg_add_sd_param (&syslog_msg, "XSPORT", "%d", xsport);
+ syslog_msg_add_sd_param (&syslog_msg, "PROTO", "%d", proto);
+
+ /* Send syslog message */
+ syslog_msg_send (&syslog_msg);
+```
+
+Example above produces following syslog message:
+ <134>1 2018-11-12T11:25:30.252715Z 172.16.4.1 NAT 5901 SADD [nsess SSUBIX="0" SVLAN="0" IATYP="IPv4" ISADDR="172.16.1.2" ISPORT="6303" XATYP="IPv4" XSADDR="10.0.0.3" XSPORT="16253" PROTO="6"]
+
+To add free-form message use:
+```{.c}
+ syslog_msg_add_msg (&syslog_msg, "event log entry");
+```
diff --git a/src/vnet/syslog/syslog.api b/src/vnet/syslog/syslog.api
new file mode 100644
index 00000000000..3ba5d69cc41
--- /dev/null
+++ b/src/vnet/syslog/syslog.api
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+option version = "1.0.0";
+import "vnet/ip/ip_types.api";
+
+enum syslog_severity
+{
+ SYSLOG_API_SEVERITY_EMERG = 0,
+ SYSLOG_API_SEVERITY_ALERT = 1,
+ SYSLOG_API_SEVERITY_CRIT = 2,
+ SYSLOG_API_SEVERITY_ERR = 3,
+ SYSLOG_API_SEVERITY_WARN = 4,
+ SYSLOG_API_SEVERITY_NOTICE = 5,
+ SYSLOG_API_SEVERITY_INFO = 6,
+ SYSLOG_API_SEVERITY_DBG = 7,
+};
+
+/** \brief Set syslog sender configuration
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param src_address - IPv4 address of syslog sender (source)
+ @param collector_address - IPv4 address of syslog collector (destination)
+ @param collector_port - UDP port of syslog colector (destination)
+ @param vrf_id - VRF/FIB table ID
+ @param max_msg_size - maximum message length
+*/
+autoreply define syslog_set_sender
+{
+ u32 client_index;
+ u32 context;
+ vl_api_ip4_address_t src_address;
+ vl_api_ip4_address_t collector_address;
+ u16 collector_port;
+ u32 vrf_id;
+ u32 max_msg_size;
+};
+
+/** \brief Get syslog sender configuration
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define syslog_get_sender
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Get syslog sender configuration reply
+ @param context - sender context, to match reply w/ request
+ @param retval - return code for the request
+ @param src_address - IPv4 address of syslog sender (source)
+ @param collector_address - IPv4 address of syslog collector (destination)
+ @param collector_port - UDP port of syslog colector (destination)
+ @param vrf_id - VRF/FIB table ID
+ @param max_msg_size - maximum message length
+*/
+define syslog_get_sender_reply
+{
+ u32 context;
+ i32 retval;
+ vl_api_ip4_address_t src_address;
+ vl_api_ip4_address_t collector_address;
+ u16 collector_port;
+ u32 vrf_id;
+ u32 max_msg_size;
+};
+
+/** \brief Set syslog filter
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param severity - severity filter (specified severity and greater match)
+*/
+autoreply define syslog_set_filter
+{
+ u32 client_index;
+ u32 context;
+ vl_api_syslog_severity_t severity;
+ };
+
+/** \brief Get syslog filter
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define syslog_get_filter
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Get syslog filter reply
+ @param context - sender context, to match reply w/ request
+ @param retval - return code for the request
+ @param severity - severity filter (specified severity and greater match)
+*/
+define syslog_get_filter_reply
+{
+ u32 context;
+ i32 retval;
+ vl_api_syslog_severity_t severity;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/syslog/syslog.c b/src/vnet/syslog/syslog.c
new file mode 100644
index 00000000000..1cffe03d768
--- /dev/null
+++ b/src/vnet/syslog/syslog.c
@@ -0,0 +1,646 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+/**
+ * @file RFC5424 syslog protocol implementation
+ */
+
+#include <unistd.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/ip/format.h>
+#include <vnet/syslog/syslog.h>
+#include <vnet/syslog/syslog_udp.h>
+
+#define SYSLOG_VERSION "1"
+#define NILVALUE "-"
+#define DEFAULT_UDP_PORT 514
+#define DEFAULT_MAX_MSG_SIZE 480
+
+#define encode_priority(f, p) ((f << 3) | p)
+
+syslog_main_t syslog_main;
+
+/* format timestamp RFC5424 6.2.3. */
+static u8 *
+format_syslog_timestamp (u8 * s, va_list * args)
+{
+ f64 timestamp = va_arg (*args, f64);
+ struct tm *tm;
+ word msec;
+
+ time_t t = timestamp;
+ tm = gmtime (&t);
+ msec = 1e6 * (timestamp - t);
+ return format (s, "%4d-%02d-%02dT%02d:%02d:%02d.%06dZ", 1900 + tm->tm_year,
+ 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec, msec);
+}
+
+/* format header RFC5424 6.2. */
+static u8 *
+format_syslog_header (u8 * s, va_list * args)
+{
+ syslog_main_t *sm = &syslog_main;
+ syslog_header_t *h = va_arg (*args, syslog_header_t *);
+ u32 pri = encode_priority (h->facility, h->severity);
+
+ return format (s, "<%d>%s %U %U %s %d %s", pri, SYSLOG_VERSION,
+ format_syslog_timestamp, h->timestamp + sm->time_offset,
+ format_ip4_address, &sm->src_address,
+ h->app_name ? h->app_name : NILVALUE, sm->procid,
+ h->msgid ? h->msgid : NILVALUE);
+}
+
+/* format strucured data elements RFC5424 6.3. */
+static u8 *
+format_syslog_structured_data (u8 * s, va_list * args)
+{
+ u8 **sds = va_arg (*args, u8 **);
+ int i;
+
+ if (vec_len (sds))
+ {
+ for (i = 0; i < vec_len (sds); i++)
+ s = format (s, "[%s]", sds[i]);
+ }
+ /* if zero structured data elemts field must contain NILVALUE */
+ else
+ s = format (s, "%s", NILVALUE);
+
+ return s;
+}
+
+static u8 *
+format_syslog_msg (u8 * s, va_list * args)
+{
+ syslog_msg_t *m = va_arg (*args, syslog_msg_t *);
+
+ s =
+ format (s, "%U %U", format_syslog_header, &m->header,
+ format_syslog_structured_data, m->structured_data);
+ /* free-form message is optional */
+ if (m->msg)
+ s = format (s, " %s", m->msg);
+
+ return s;
+}
+
+void
+syslog_msg_sd_init (syslog_msg_t * syslog_msg, char *sd_id)
+{
+ u8 *sd;
+
+ sd = format (0, "%s", sd_id);
+ vec_add1 (syslog_msg->structured_data, sd);
+ syslog_msg->curr_sd_index++;
+}
+
+void
+syslog_msg_add_sd_param (syslog_msg_t * syslog_msg, char *name, char *fmt,
+ ...)
+{
+ va_list va;
+ u8 *value;
+
+ va_start (va, fmt);
+ value = va_format (0, fmt, &va);
+ va_end (va);
+ vec_terminate_c_string (value);
+
+ syslog_msg->structured_data[syslog_msg->curr_sd_index] =
+ format (syslog_msg->structured_data[syslog_msg->curr_sd_index],
+ " %s=\"%s\"", name, value);
+ vec_free (value);
+}
+
+void
+syslog_msg_add_msg (syslog_msg_t * syslog_msg, char *fmt, ...)
+{
+ va_list va;
+ u8 *msg;
+
+ va_start (va, fmt);
+ msg = va_format (0, fmt, &va);
+ va_end (va);
+ vec_terminate_c_string (msg);
+
+ syslog_msg->msg = msg;
+}
+
+void
+syslog_msg_init (syslog_msg_t * syslog_msg, syslog_facility_t facility,
+ syslog_severity_t severity, char *app_name, char *msgid)
+{
+ syslog_main_t *sm = &syslog_main;
+ vlib_main_t *vm = sm->vlib_main;
+
+ syslog_msg->header.facility = facility;
+ syslog_msg->header.severity = severity;
+ syslog_msg->header.timestamp = vlib_time_now (vm);
+ syslog_msg->header.app_name = app_name;
+ syslog_msg->header.msgid = msgid;
+ syslog_msg->structured_data = 0;
+ syslog_msg->curr_sd_index = ~0;
+ syslog_msg->msg = 0;
+}
+
+int
+syslog_msg_send (syslog_msg_t * syslog_msg)
+{
+ syslog_main_t *sm = &syslog_main;
+ vlib_main_t *vm = sm->vlib_main;
+ u32 bi, msg_len, *to_next;
+ u8 *tmp;
+ vlib_buffer_t *b;
+ vlib_buffer_free_list_t *fl;
+ vlib_frame_t *f;
+ int i;
+
+ if (vlib_buffer_alloc (vm, &bi, 1) != 1)
+ return -1;
+
+ b = vlib_get_buffer (vm, bi);
+ clib_memset (vnet_buffer (b), 0, sizeof (*vnet_buffer (b)));
+ fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
+ vlib_buffer_init_for_free_list (b, fl);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b);
+
+ /* one message per UDP datagram RFC5426 3.1. */
+ tmp = format (0, "%U", format_syslog_msg, syslog_msg);
+ msg_len = vec_len (tmp) - (vec_c_string_is_terminated (tmp) ? 1 : 0);
+ msg_len = msg_len < sm->max_msg_size ? msg_len : sm->max_msg_size;
+ clib_memcpy_fast (b->data, tmp, msg_len);
+ b->current_length = msg_len;
+ vec_free (tmp);
+
+ vec_free (syslog_msg->msg);
+ for (i = 0; i < vec_len (syslog_msg->structured_data); i++)
+ vec_free (syslog_msg->structured_data[i]);
+ vec_free (syslog_msg->structured_data);
+
+ syslog_add_udp_transport (vm, bi);
+
+ f = vlib_get_frame_to_node (vm, sm->ip4_lookup_node_index);
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi;
+ f->n_vectors = 1;
+ vlib_put_frame_to_node (vm, sm->ip4_lookup_node_index, f);
+
+ return 0;
+}
+
+static uword
+unformat_syslog_facility (unformat_input_t * input, va_list * args)
+{
+ u32 *r = va_arg (*args, u32 *);
+
+ if (0);
+#define _(v,f,s) else if (unformat (input, s)) *r = SYSLOG_FACILITY_##f;
+ foreach_syslog_facility
+#undef _
+ else
+ return 0;
+
+ return 1;
+}
+
+static uword
+unformat_syslog_severity (unformat_input_t * input, va_list * args)
+{
+ u32 *r = va_arg (*args, u32 *);
+
+ if (0);
+#define _(v,f,s) else if (unformat (input, s)) *r = SYSLOG_SEVERITY_##f;
+ foreach_syslog_severity
+#undef _
+ else
+ return 0;
+
+ return 1;
+}
+
+static u8 *
+format_syslog_severity (u8 * s, va_list * args)
+{
+ u32 i = va_arg (*args, u32);
+ u8 *t = 0;
+
+ switch (i)
+ {
+#define _(v,f,str) case SYSLOG_SEVERITY_##f: t = (u8 *) str; break;
+ foreach_syslog_severity
+#undef _
+ default:
+ return format (s, "unknown");
+ }
+
+ return format (s, "%s", t);
+}
+
+vnet_api_error_t
+set_syslog_sender (ip4_address_t * collector, u16 collector_port,
+ ip4_address_t * src, u32 vrf_id, u32 max_msg_size)
+{
+ syslog_main_t *sm = &syslog_main;
+ u32 fib_index;
+
+ if (max_msg_size < DEFAULT_MAX_MSG_SIZE)
+ return VNET_API_ERROR_INVALID_VALUE;
+
+ if (collector->as_u32 == 0 || collector_port == 0 || src->as_u32 == 0)
+ return VNET_API_ERROR_INVALID_VALUE;
+
+ if (vrf_id == ~0)
+ {
+ fib_index = ~0;
+ }
+ else
+ {
+ fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
+ if (fib_index == ~0)
+ return VNET_API_ERROR_NO_SUCH_FIB;
+ }
+
+ sm->fib_index = fib_index;
+
+ sm->collector.as_u32 = collector->as_u32;
+ sm->collector_port = (u16) collector_port;
+ sm->src_address.as_u32 = src->as_u32;
+ sm->max_msg_size = max_msg_size;
+
+ return 0;
+}
+
+static clib_error_t *
+set_syslog_sender_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ ip4_address_t collector, src;
+ u32 collector_port = DEFAULT_UDP_PORT;
+ u32 vrf_id = ~0;
+ u32 max_msg_size = DEFAULT_MAX_MSG_SIZE;
+ clib_error_t *ret = 0;
+
+ collector.as_u32 = 0;
+ src.as_u32 = 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, "collector %U", unformat_ip4_address, &collector))
+ ;
+ else if (unformat (line_input, "port %u", &collector_port))
+ ;
+ else if (unformat (line_input, "src %U", unformat_ip4_address, &src))
+ ;
+ else if (unformat (line_input, "vrf-id %u", &vrf_id))
+ ;
+ else if (unformat (line_input, "max-msg-size %u", &max_msg_size))
+ ;
+ else
+ {
+ ret = clib_error_return (0, "Unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ if (collector.as_u32 == 0)
+ {
+ ret = clib_error_return (0, "collector address required");
+ goto done;
+ }
+
+ if (src.as_u32 == 0)
+ {
+ ret = clib_error_return (0, "src address required");
+ goto done;
+ }
+
+ if (max_msg_size < DEFAULT_MAX_MSG_SIZE)
+ {
+ ret =
+ clib_error_return (0, "too small max-msg-size value, minimum is %u",
+ DEFAULT_MAX_MSG_SIZE);
+ goto done;
+ }
+
+ vnet_api_error_t rv =
+ set_syslog_sender (&collector, collector_port, &src, vrf_id,
+ max_msg_size);
+
+ if (rv)
+ ret =
+ clib_error_return (0, "set syslog sender failed rv=%d:%U", (int) rv,
+ format_vnet_api_errno, rv);
+
+done:
+ unformat_free (line_input);
+ return ret;
+}
+
+static clib_error_t *
+show_syslog_sender_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ syslog_main_t *sm = &syslog_main;
+ u32 vrf_id = ~0;
+
+ if (sm->fib_index != ~0)
+ vrf_id = fib_table_get_table_id (sm->fib_index, FIB_PROTOCOL_IP4);
+
+ if (syslog_is_enabled ())
+ vlib_cli_output (vm, "collector %U:%u, src address %U, VRF ID %d, "
+ "max-msg-size %u",
+ format_ip4_address, &sm->collector, sm->collector_port,
+ format_ip4_address, &sm->src_address,
+ vrf_id, sm->max_msg_size);
+ else
+ vlib_cli_output (vm, "syslog sender is disabled");
+
+ return 0;
+}
+
+static clib_error_t *
+test_syslog_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ syslog_msg_t syslog_msg;
+ syslog_facility_t facility;
+ syslog_severity_t severity;
+ clib_error_t *ret = 0;
+ u8 *app_name = 0, *msgid = 0, *sd_id = 0, *param_name = 0, *param_value = 0;
+
+ if (!syslog_is_enabled ())
+ return 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ if (unformat (line_input, "%U", unformat_syslog_facility, &facility))
+ {
+ if (unformat (line_input, "%U", unformat_syslog_severity, &severity))
+ {
+ if (syslog_severity_filter_block (severity))
+ goto done;
+
+ if (unformat (line_input, "%s", &app_name))
+ {
+ if (unformat (line_input, "%s", &msgid))
+ {
+ syslog_msg_init (&syslog_msg, facility, severity,
+ (char *) app_name, (char *) msgid);
+ while (unformat (line_input, "sd-id %s", &sd_id))
+ {
+ syslog_msg_sd_init (&syslog_msg, (char *) sd_id);
+ while (unformat
+ (line_input, "sd-param %s %s", &param_name,
+ &param_value))
+ {
+ syslog_msg_add_sd_param (&syslog_msg,
+ (char *) param_name,
+ (char *) param_value);
+ vec_free (param_name);
+ vec_free (param_value);
+ }
+ vec_free (sd_id);
+ }
+ if (unformat_check_input (line_input) !=
+ UNFORMAT_END_OF_INPUT)
+ syslog_msg_add_msg (&syslog_msg, "%U",
+ format_unformat_input, line_input);
+ syslog_msg_send (&syslog_msg);
+ }
+ else
+ {
+ ret =
+ clib_error_return (0, "Unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+ else
+ {
+ ret =
+ clib_error_return (0, "Unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+ else
+ {
+ ret =
+ clib_error_return (0, "Unknown input `%U'", format_unformat_error,
+ line_input);
+ goto done;
+ }
+ }
+ else
+ {
+ ret =
+ clib_error_return (0, "Unknown input `%U'", format_unformat_error,
+ line_input);
+ goto done;
+ }
+
+done:
+ vec_free (app_name);
+ vec_free (msgid);
+ unformat_free (line_input);
+ return ret;
+}
+
+static clib_error_t *
+set_syslog_filter_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ syslog_main_t *sm = &syslog_main;
+ clib_error_t *ret = 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, "severity %U", unformat_syslog_severity,
+ &sm->severity_filter))
+ ;
+ else
+ {
+ ret = clib_error_return (0, "Unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+done:
+ unformat_free (line_input);
+ return ret;
+}
+
+static clib_error_t *
+show_syslog_filter_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ syslog_main_t *sm = &syslog_main;
+
+ vlib_cli_output (vm, "severity-filter: %U", format_syslog_severity,
+ sm->severity_filter);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+/*?
+ * Set syslog sender configuration.
+ *
+ * @cliexpar
+ * @parblock
+ *
+ * Example of how to configure syslog sender:
+ * @cliexcmd{set syslog sender collector 10.10.10.10 port 514 src 172.16.2.2}
+ * @endparblock
+?*/
+VLIB_CLI_COMMAND (set_syslog_sender_command, static) = {
+ .path = "set syslog sender",
+ .short_help = "set syslog sender "
+ "collector <ip4-address> [port <port>] "
+ "src <ip4-address> [vrf-id <vrf-id>] "
+ "[max-msg-size <max-msg-size>]",
+ .function = set_syslog_sender_command_fn,
+};
+
+/*?
+ * Show syslog sender configuration.
+ *
+ * @cliexpar
+ * @parblock
+ *
+ * Example of how to display syslog sender configuration:
+ * @cliexstart{show syslog sender}
+ * collector 10.10.10.10:514, src address 172.16.2.2, VRF ID 0, max-msg-size 480
+ * @cliexend
+ * @endparblock
+?*/
+VLIB_CLI_COMMAND (show_syslog_sender_command, static) = {
+ .path = "show syslog sender",
+ .short_help = "show syslog sender",
+ .function = show_syslog_sender_command_fn,
+};
+
+/*?
+ * This command generate test syslog message.
+ *
+ * @cliexpar
+ * @parblock
+ *
+ * Example of how to generate following syslog message
+ * '<em><180>1 2018-11-07T11:36:41.231759Z 172.16.1.1 test 10484 testMsg
+ * [exampleSDID@32473 eventID="1011" eventSource="App" iut="3"]
+ * this is message</em>'
+ * @cliexcmd{test syslog local6 warning test testMsg sd-id <!--
+ * --> exampleSDID@32473 sd-param eventID 1011 sd-param eventSource App <!--
+ * --> sd-param iut 3 this is message}
+ * @endparblock
+?*/
+VLIB_CLI_COMMAND (test_syslog_command, static) = {
+ .path = "test syslog",
+ .short_help = "test syslog <facility> <severity> <app-name> <msgid> "
+ "[sd-id <sd-id> sd-param <name> <value>] [<message]",
+ .function = test_syslog_command_fn,
+};
+
+/*?
+ * Set syslog severity filter, specified severity and greater match.
+ *
+ * @cliexpar
+ * @parblock
+ *
+ * Example of how to configure syslog severity filter:
+ * @cliexcmd{set syslog filter severity warning}
+ * @endparblock
+?*/
+VLIB_CLI_COMMAND (set_syslog_filter_command, static) = {
+ .path = "set syslog filter",
+ .short_help = "set syslog filter severity <severity>",
+ .function = set_syslog_filter_command_fn,
+};
+
+/*?
+ * Show syslog severity filter.
+ *
+ * @cliexpar
+ * @parblock
+ *
+ * Example of how to display syslog severity filter:
+ * @cliexstart{show syslog filter}
+ * severity-filter: warning
+ * @cliexend
+ * @endparblock
+?*/
+VLIB_CLI_COMMAND (show_syslog_filter_command, static) = {
+ .path = "show syslog filter",
+ .short_help = "show syslog filter",
+ .function = show_syslog_filter_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+syslog_init (vlib_main_t * vm)
+{
+ syslog_main_t *sm = &syslog_main;
+ f64 vlib_time_0 = vlib_time_now (vm);
+ struct timeval timeval_0;
+ vlib_node_t *ip4_lookup_node;
+
+ sm->vlib_main = vm;
+ sm->vnet_main = vnet_get_main ();
+
+ sm->procid = getpid ();
+ gettimeofday (&timeval_0, 0);
+ sm->time_offset =
+ (f64) timeval_0.tv_sec + (((f64) timeval_0.tv_usec) * 1e-6) - vlib_time_0;
+
+ sm->collector.as_u32 = 0;
+ sm->src_address.as_u32 = 0;
+ sm->collector_port = DEFAULT_UDP_PORT;
+ sm->max_msg_size = DEFAULT_MAX_MSG_SIZE;
+ sm->fib_index = ~0;
+ sm->severity_filter = SYSLOG_SEVERITY_INFORMATIONAL;
+
+ ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
+ sm->ip4_lookup_node_index = ip4_lookup_node->index;
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (syslog_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/syslog/syslog.h b/src/vnet/syslog/syslog.h
new file mode 100644
index 00000000000..ed915877518
--- /dev/null
+++ b/src/vnet/syslog/syslog.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+/**
+ * @file RFC5424 syslog protocol declarations
+ */
+#ifndef __included_syslog_h__
+#define __included_syslog_h__
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip4_packet.h>
+
+/* syslog message facilities */
+#define foreach_syslog_facility \
+ _(0, KERNEL, "kernel") \
+ _(1, USER_LEVEL, "user-level") \
+ _(2, MAIL_SYSTEM, "mail-system") \
+ _(3, SYSTEM_DAEMONS, "system-daemons") \
+ _(4, SEC_AUTH, "security-authorization") \
+ _(5, SYSLOGD, "syslogd") \
+ _(6, LINE_PRINTER, "line-printer") \
+ _(7, NETWORK_NEWS, "network-news") \
+ _(8, UUCP, "uucp") \
+ _(9, CLOCK, "clock-daemon") \
+ _(11, FTP, "ftp-daemon") \
+ _(12, NTP, "ntp-subsystem") \
+ _(13, LOG_AUDIT, "log-audit") \
+ _(14, LOG_ALERT, "log-alert") \
+ _(16, LOCAL0, "local0") \
+ _(17, LOCAL1, "local1") \
+ _(18, LOCAL2, "local2") \
+ _(19, LOCAL3, "local3") \
+ _(20, LOCAL4, "local4") \
+ _(21, LOCAL5, "local5") \
+ _(22, LOCAL6, "local6") \
+ _(23, LOCAL7, "local7")
+
+typedef enum
+{
+#define _(v, N, s) SYSLOG_FACILITY_##N = v,
+ foreach_syslog_facility
+#undef _
+} syslog_facility_t;
+
+/* syslog message severities */
+#define foreach_syslog_severity \
+ _(0, EMERGENCY, "emergency") \
+ _(1, ALERT, "alert") \
+ _(2, CRITICAL, "critical") \
+ _(3, ERROR, "error") \
+ _(4, WARNING, "warning") \
+ _(5, NOTICE, "notice") \
+ _(6, INFORMATIONAL, "informational") \
+ _(7, DEBUG, "debug")
+
+typedef enum
+{
+#define _(v, N, s) SYSLOG_SEVERITY_##N = v,
+ foreach_syslog_severity
+#undef _
+} syslog_severity_t;
+
+/** syslog header */
+typedef struct
+{
+ /** facility value, part of priority */
+ syslog_facility_t facility;
+
+ /** severity value, part of priority */
+ syslog_severity_t severity;
+
+ /** message timestamp */
+ f64 timestamp;
+
+ /** application that originated the message RFC5424 6.2.5. */
+ char *app_name;
+
+ /** identify the type of message RFC5424 6.2.7. */
+ char *msgid;
+} syslog_header_t;
+
+/** syslog message */
+typedef struct
+{
+ /** header */
+ syslog_header_t header;
+
+ /** structured data RFC5424 6.3. */
+ u8 **structured_data;
+ u32 curr_sd_index;
+
+ /** free-form message RFC5424 6.4. */
+ u8 *msg;
+} syslog_msg_t;
+
+typedef struct
+{
+ /** process ID RFC5424 6.2.6. */
+ u32 procid;
+
+ /** time offset */
+ f64 time_offset;
+
+ /** IPv4 address of remote host (destination) */
+ ip4_address_t collector;
+
+ /** UDP port number of remote host (destination) */
+ u16 collector_port;
+
+ /** IPv4 address of sender (source) */
+ ip4_address_t src_address;
+
+ /** FIB table index */
+ u32 fib_index;
+
+ /** message size limit */
+ u32 max_msg_size;
+
+ /** severity filter (specified severity and greater match) */
+ syslog_severity_t severity_filter;
+
+ /** ip4-lookup node index */
+ u32 ip4_lookup_node_index;
+
+ /** convenience variables */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+} syslog_main_t;
+
+extern syslog_main_t syslog_main;
+
+/**
+ * @brief Initialize syslog message header
+ *
+ * @param facility facility value
+ * @param severity severity level
+ * @param app_name application that originated message RFC424 6.2.5. (optional)
+ * @param msgid identify the type of message RFC5424 6.2.7. (optional)
+ */
+void syslog_msg_init (syslog_msg_t * syslog_msg, syslog_facility_t facility,
+ syslog_severity_t severity, char *app_name,
+ char *msgid);
+/**
+ * @brief Initialize structured data element
+ *
+ * @param sd_id structured data element name RFC5424 6.3.2.
+ */
+void syslog_msg_sd_init (syslog_msg_t * syslog_msg, char *sd_id);
+
+/**
+ * @brief Add structured data elemnt parameter name-value pair RFC5424 6.3.3.
+ */
+void syslog_msg_add_sd_param (syslog_msg_t * syslog_msg, char *name,
+ char *fmt, ...);
+
+/**
+ * @brief Add free-form message RFC5424 6.4.
+ */
+void syslog_msg_add_msg (syslog_msg_t * syslog_msg, char *fmt, ...);
+
+/**
+ * @brief Send syslog message
+ */
+int syslog_msg_send (syslog_msg_t * syslog_msg);
+
+/**
+ * @brief Set syslog sender configuration
+ *
+ * @param collector IPv4 address of syslog collector (destination)
+ * @param collector_port UDP port of syslog colector (destination)
+ * @param src IPv4 address of syslog sender (source)
+ * @param vrf_id VRF/FIB table ID
+ * @param max_msg_size maximum message length
+ */
+vnet_api_error_t set_syslog_sender (ip4_address_t * collector,
+ u16 collector_port, ip4_address_t * src,
+ u32 vrf_id, u32 max_msg_size);
+
+/**
+ * @brief Check if syslog logging is enabled
+ *
+ * @return 1 if syslog logging is enabled, 0 otherwise
+ */
+always_inline int
+syslog_is_enabled (void)
+{
+ syslog_main_t *sm = &syslog_main;
+
+ return sm->collector.as_u32 ? 1 : 0;
+}
+
+/**
+ * @brief Severity filter test
+ *
+ * @return 1 if message with specified severity is not selected to be logged
+ */
+always_inline int
+syslog_severity_filter_block (syslog_severity_t s)
+{
+ syslog_main_t *sm = &syslog_main;
+
+ return (sm->severity_filter < s);
+}
+
+#endif /* __included_syslog_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/syslog/syslog_api.c b/src/vnet/syslog/syslog_api.c
new file mode 100644
index 00000000000..8f94c72b291
--- /dev/null
+++ b/src/vnet/syslog/syslog_api.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2018 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/vnet.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/interface.h>
+#include <vnet/api_errno.h>
+
+#include <vnet/fib/fib_table.h>
+#include <vnet/syslog/syslog.h>
+
+#include <vnet/vnet_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun /* define message structures */
+#include <vnet/vnet_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 <vnet/vnet_all_api_h.h>
+#undef vl_printfun
+
+#include <vlibapi/api_helper_macros.h>
+
+#define foreach_vpe_api_msg \
+_(SYSLOG_SET_SENDER, syslog_set_sender) \
+_(SYSLOG_GET_SENDER, syslog_get_sender) \
+_(SYSLOG_SET_FILTER, syslog_set_filter) \
+_(SYSLOG_GET_FILTER, syslog_get_filter)
+
+static int
+syslog_severity_decode (vl_api_syslog_severity_t v, syslog_severity_t * s)
+{
+ v = ntohl (v);
+ int rv = 0;
+
+ switch (v)
+ {
+ case SYSLOG_API_SEVERITY_EMERG:
+ *s = SYSLOG_SEVERITY_EMERGENCY;
+ break;
+ case SYSLOG_API_SEVERITY_ALERT:
+ *s = SYSLOG_SEVERITY_ALERT;
+ break;
+ case SYSLOG_API_SEVERITY_CRIT:
+ *s = SYSLOG_SEVERITY_CRITICAL;
+ break;
+ case SYSLOG_API_SEVERITY_ERR:
+ *s = SYSLOG_SEVERITY_ERROR;
+ break;
+ case SYSLOG_API_SEVERITY_WARN:
+ *s = SYSLOG_SEVERITY_WARNING;
+ break;
+ case SYSLOG_API_SEVERITY_NOTICE:
+ *s = SYSLOG_SEVERITY_NOTICE;
+ break;
+ case SYSLOG_API_SEVERITY_INFO:
+ *s = SYSLOG_SEVERITY_INFORMATIONAL;
+ break;
+ case SYSLOG_API_SEVERITY_DBG:
+ *s = SYSLOG_SEVERITY_DEBUG;
+ break;
+ default:
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ }
+
+ return rv;
+}
+
+static int
+syslog_severity_encode (syslog_severity_t v, vl_api_syslog_severity_t * s)
+{
+ int rv = 0;
+ switch (v)
+ {
+ case SYSLOG_SEVERITY_EMERGENCY:
+ *s = SYSLOG_API_SEVERITY_EMERG;
+ break;
+ case SYSLOG_SEVERITY_ALERT:
+ *s = SYSLOG_API_SEVERITY_ALERT;
+ break;
+ case SYSLOG_SEVERITY_CRITICAL:
+ *s = SYSLOG_API_SEVERITY_CRIT;
+ break;
+ case SYSLOG_SEVERITY_ERROR:
+ *s = SYSLOG_API_SEVERITY_ERR;
+ break;
+ case SYSLOG_SEVERITY_WARNING:
+ *s = SYSLOG_API_SEVERITY_WARN;
+ break;
+ case SYSLOG_SEVERITY_NOTICE:
+ *s = SYSLOG_API_SEVERITY_NOTICE;
+ break;
+ case SYSLOG_SEVERITY_INFORMATIONAL:
+ *s = SYSLOG_API_SEVERITY_INFO;
+ break;
+ case SYSLOG_SEVERITY_DEBUG:
+ *s = SYSLOG_API_SEVERITY_DBG;
+ break;
+ default:
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ }
+
+ *s = htonl (*s);
+ return rv;
+}
+
+static void
+vl_api_syslog_set_sender_t_handler (vl_api_syslog_set_sender_t * mp)
+{
+ vl_api_syslog_set_sender_reply_t *rmp;
+ ip4_address_t collector, src;
+
+ clib_memcpy (&collector, &mp->collector_address, sizeof (collector));
+ clib_memcpy (&src, &mp->src_address, sizeof (src));
+
+ int rv = set_syslog_sender (&collector, ntohs (mp->collector_port), &src,
+ ntohl (mp->vrf_id), ntohl (mp->max_msg_size));
+
+ REPLY_MACRO (VL_API_SYSLOG_SET_SENDER_REPLY);
+}
+
+static void
+vl_api_syslog_get_sender_t_handler (vl_api_syslog_get_sender_t * mp)
+{
+ int rv = 0;
+ vl_api_syslog_get_sender_reply_t *rmp;
+ syslog_main_t *sm = &syslog_main;
+ u32 vrf_id;
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_SYSLOG_GET_SENDER_REPLY,
+ ({
+ clib_memcpy (rmp->collector_address.address, &(sm->collector),
+ sizeof(ip4_address_t));
+ clib_memcpy (rmp->src_address.address, &(sm->src_address),
+ sizeof(ip4_address_t));
+ rmp->collector_port = htons (sm->collector_port);
+ if (sm->fib_index == ~0)
+ vrf_id = ~0;
+ else
+ vrf_id = htonl (fib_table_get_table_id (sm->fib_index, FIB_PROTOCOL_IP4));
+ rmp->vrf_id = vrf_id;
+ rmp->max_msg_size = htonl (sm->max_msg_size);
+ }))
+ /* *INDENT-ON* */
+}
+
+static void
+vl_api_syslog_set_filter_t_handler (vl_api_syslog_set_filter_t * mp)
+{
+ vl_api_syslog_set_filter_reply_t *rmp;
+ syslog_main_t *sm = &syslog_main;
+ int rv = 0;
+ syslog_severity_t s;
+
+ rv = syslog_severity_decode (mp->severity, &s);
+ if (rv)
+ goto send_reply;
+
+ sm->severity_filter = s;
+
+send_reply:
+ REPLY_MACRO (VL_API_SYSLOG_SET_FILTER_REPLY);
+}
+
+static void
+vl_api_syslog_get_filter_t_handler (vl_api_syslog_get_filter_t * mp)
+{
+ int rv = 0;
+ vl_api_syslog_get_filter_reply_t *rmp;
+ syslog_main_t *sm = &syslog_main;
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_SYSLOG_GET_FILTER_REPLY,
+ ({
+ rv = syslog_severity_encode (sm->severity_filter, &rmp->severity);
+ }))
+ /* *INDENT-ON* */
+}
+
+#define vl_msg_name_crc_list
+#include <vnet/vnet_all_api_h.h>
+#undef vl_msg_name_crc_list
+
+static void
+setup_message_id_table (api_main_t * am)
+{
+#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
+ foreach_vl_msg_name_crc_syslog;
+#undef _
+}
+
+static clib_error_t *
+syslog_api_hookup (vlib_main_t * vm)
+{
+ api_main_t *am = &api_main;
+
+#define _(N,n) \
+ vl_msg_api_set_handlers(VL_API_##N, #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_vpe_api_msg;
+#undef _
+
+ /*
+ * Set up the (msg_name, crc, message-id) table
+ */
+ setup_message_id_table (am);
+
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (syslog_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/syslog/syslog_udp.c b/src/vnet/syslog/syslog_udp.c
new file mode 100644
index 00000000000..f4fa1d0baf5
--- /dev/null
+++ b/src/vnet/syslog/syslog_udp.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+/**
+ * @file syslog protocol UDP transport layer implementation (RFC5426)
+ */
+
+#include <vnet/syslog/syslog_udp.h>
+#include <vnet/ip/ip4.h>
+#include <vnet/udp/udp_packet.h>
+
+void
+syslog_add_udp_transport (vlib_main_t * vm, u32 bi)
+{
+ syslog_main_t *sm = &syslog_main;
+ vlib_buffer_t *b = vlib_get_buffer (vm, bi);
+ ip4_header_t *ip;
+ udp_header_t *udp;
+
+ vlib_buffer_advance (b, -(sizeof (ip4_header_t) + sizeof (udp_header_t)));
+
+ b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
+ vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
+ vnet_buffer (b)->sw_if_index[VLIB_TX] = sm->fib_index;
+
+ ip = vlib_buffer_get_current (b);
+ clib_memset (ip, 0, sizeof (*ip));
+ udp = (udp_header_t *) (ip + 1);
+ clib_memset (udp, 0, sizeof (*udp));
+
+ ip->ip_version_and_header_length = 0x45;
+ ip->flags_and_fragment_offset =
+ clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
+ ip->ttl = 255;
+ ip->protocol = IP_PROTOCOL_UDP;
+ ip->src_address.as_u32 = sm->src_address.as_u32;
+ ip->dst_address.as_u32 = sm->collector.as_u32;
+
+ udp->src_port = udp->dst_port = clib_host_to_net_u16 (sm->collector_port);
+
+ const u16 ip_length = vlib_buffer_length_in_chain (vm, b);
+ ip->length = clib_host_to_net_u16 (ip_length);
+ ip->checksum = ip4_header_checksum (ip);
+
+ const u16 udp_length = ip_length - (sizeof (ip4_header_t));
+ udp->length = clib_host_to_net_u16 (udp_length);
+ /* RFC5426 3.6. */
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+
+ b->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/syslog/syslog_udp.h b/src/vnet/syslog/syslog_udp.h
new file mode 100644
index 00000000000..008fed70d19
--- /dev/null
+++ b/src/vnet/syslog/syslog_udp.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+/**
+ * @file syslog protocol UDP transport layer declaration (RFC5426)
+ */
+#ifndef __included_syslog_udp_h__
+#define __included_syslog_udp_h__
+
+#include <vnet/syslog/syslog.h>
+
+/**
+ * @brief Add UDP/IP transport layer by prepending it to existing data
+ */
+void syslog_add_udp_transport (vlib_main_t * vm, u32 bi);
+
+#endif /* __included_syslog_udp_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/vnet_all_api_h.h b/src/vnet/vnet_all_api_h.h
index 157006b2643..927bcab9e92 100644
--- a/src/vnet/vnet_all_api_h.h
+++ b/src/vnet/vnet_all_api_h.h
@@ -76,6 +76,7 @@
#include <vnet/dhcp/dhcp6_ia_na_client_cp.api.h>
#include <vnet/devices/pipe/pipe.api.h>
#include <vnet/vxlan-gbp/vxlan_gbp.api.h>
+#include <vnet/syslog/syslog.api.h>
/*
* fd.io coding-style-patch-verification: ON
diff --git a/test/Makefile b/test/Makefile
index 0338062882a..5f456e172c4 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -73,7 +73,7 @@ PYTHON_EXTRA_DEPENDS=
endif
PYTHON_VENV_PATH=$(VPP_PYTHON_PREFIX)/virtualenv
-PYTHON_DEPENDS=$(PYTHON_EXTRA_DEPENDS) psutil faulthandler six scapy==2.4.0 pexpect cryptography subprocess32 cffi git+https://github.com/vpp-dev/py-lispnetworking
+PYTHON_DEPENDS=$(PYTHON_EXTRA_DEPENDS) psutil faulthandler six scapy==2.4.0 pexpect cryptography subprocess32 cffi syslog-rfc5424-parser git+https://github.com/vpp-dev/py-lispnetworking
SCAPY_SOURCE=$(shell find $(PYTHON_VENV_PATH) -name site-packages)
BUILD_COV_DIR=$(BR)/test-cov
diff --git a/test/test_syslog.py b/test/test_syslog.py
new file mode 100644
index 00000000000..db7d7bef2c7
--- /dev/null
+++ b/test/test_syslog.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+
+from framework import VppTestCase, VppTestRunner
+from util import ppp
+from scapy.packet import Raw
+from scapy.layers.inet import IP, UDP
+from vpp_papi_provider import SYSLOG_SEVERITY
+from syslog_rfc5424_parser import SyslogMessage, ParseError
+from syslog_rfc5424_parser.constants import SyslogFacility, SyslogSeverity
+
+
+class TestSyslog(VppTestCase):
+ """ Syslog Protocol Test Cases """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSyslog, cls).setUpClass()
+
+ try:
+ cls.create_pg_interfaces(range(1))
+ cls.pg0.admin_up()
+ cls.pg0.config_ip4()
+ cls.pg0.resolve_arp()
+
+ except Exception:
+ super(TestSyslog, cls).tearDownClass()
+ raise
+
+ def syslog_generate(self, facility, severity, appname, msgid, sd=None,
+ msg=None):
+ """
+ Generate syslog message
+
+ :param facility: facility value
+ :param severity: severity level
+ :param appname: application name that originate message
+ :param msgid: message indetifier
+ :param sd: structured data (optional)
+ :param msg: free-form message (optional)
+ """
+ facility_str = ['kernel', 'user-level', 'mail-system',
+ 'system-daemons', 'security-authorization', 'syslogd',
+ 'line-printer', 'network-news', 'uucp', 'clock-daemon',
+ '', 'ftp-daemon', 'ntp-subsystem', 'log-audit',
+ 'log-alert', '', 'local0', 'local1', 'local2',
+ 'local3', 'local4', 'local5', 'local6', 'local7']
+
+ severity_str = ['emergency', 'alert', 'critical', 'error', 'warning',
+ 'notice', 'informational', 'debug']
+
+ cli_str = "test syslog %s %s %s %s" % (facility_str[facility],
+ severity_str[severity],
+ appname,
+ msgid)
+ if sd is not None:
+ for sd_id, sd_params in sd.items():
+ cli_str += " sd-id %s" % (sd_id)
+ for name, value in sd_params.items():
+ cli_str += " sd-param %s %s" % (name, value)
+ if msg is not None:
+ cli_str += " %s" % (msg)
+ self.vapi.cli(cli_str)
+
+ def syslog_verify(self, data, facility, severity, appname, msgid, sd=None,
+ msg=None):
+ """
+ Verify syslog message
+
+ :param data: syslog message
+ :param facility: facility value
+ :param severity: severity level
+ :param appname: application name that originate message
+ :param msgid: message indetifier
+ :param sd: structured data (optional)
+ :param msg: free-form message (optional)
+ """
+ message = data.decode('utf-8')
+ if sd is None:
+ sd = {}
+ try:
+ message = SyslogMessage.parse(message)
+ self.assertEqual(message.facility, facility)
+ self.assertEqual(message.severity, severity)
+ self.assertEqual(message.appname, appname)
+ self.assertEqual(message.msgid, msgid)
+ self.assertEqual(message.msg, msg)
+ self.assertEqual(message.sd, sd)
+ self.assertEqual(message.version, 1)
+ self.assertEqual(message.hostname, self.pg0.local_ip4)
+ except ParseError as e:
+ self.logger.error(e)
+
+ def test_syslog(self):
+ """ Syslog Protocol test """
+ self.vapi.syslog_set_sender(self.pg0.remote_ip4n, self.pg0.local_ip4n)
+ config = self.vapi.syslog_get_sender()
+ self.assertEqual(config.collector_address.address,
+ self.pg0.remote_ip4n)
+ self.assertEqual(config.collector_port, 514)
+ self.assertEqual(config.src_address.address, self.pg0.local_ip4n)
+ self.assertEqual(config.vrf_id, 0)
+ self.assertEqual(config.max_msg_size, 480)
+
+ appname = 'test'
+ msgid = 'testMsg'
+ msg = 'this is message'
+ sd1 = {'exampleSDID@32473': {'iut': '3',
+ 'eventSource': 'App',
+ 'eventID': '1011'}}
+ sd2 = {'exampleSDID@32473': {'iut': '3',
+ 'eventSource': 'App',
+ 'eventID': '1011'},
+ 'examplePriority@32473': {'class': 'high'}}
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.syslog_generate(SyslogFacility.local7,
+ SyslogSeverity.info,
+ appname,
+ msgid,
+ None,
+ msg)
+ capture = self.pg0.get_capture(1)
+ try:
+ self.assertEqual(capture[0][IP].src, self.pg0.local_ip4)
+ self.assertEqual(capture[0][IP].dst, self.pg0.remote_ip4)
+ self.assertEqual(capture[0][UDP].dport, 514)
+ self.assert_packet_checksums_valid(capture[0], False)
+ except:
+ self.logger.error(ppp("invalid packet:", capture[0]))
+ raise
+ self.syslog_verify(capture[0][Raw].load,
+ SyslogFacility.local7,
+ SyslogSeverity.info,
+ appname,
+ msgid,
+ None,
+ msg)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.vapi.syslog_set_filter(SYSLOG_SEVERITY.WARN)
+ filter = self.vapi.syslog_get_filter()
+ self.assertEqual(filter.severity, SYSLOG_SEVERITY.WARN)
+ self.syslog_generate(SyslogFacility.local7,
+ SyslogSeverity.info,
+ appname,
+ msgid,
+ None,
+ msg)
+ self.pg0.assert_nothing_captured()
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.syslog_generate(SyslogFacility.local6,
+ SyslogSeverity.warning,
+ appname,
+ msgid,
+ sd1,
+ msg)
+ capture = self.pg0.get_capture(1)
+ self.syslog_verify(capture[0][Raw].load,
+ SyslogFacility.local6,
+ SyslogSeverity.warning,
+ appname,
+ msgid,
+ sd1,
+ msg)
+
+ self.vapi.syslog_set_sender(self.pg0.remote_ip4n,
+ self.pg0.local_ip4n,
+ collector_port=12345)
+ config = self.vapi.syslog_get_sender()
+ self.assertEqual(config.collector_port, 12345)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.syslog_generate(SyslogFacility.local5,
+ SyslogSeverity.err,
+ appname,
+ msgid,
+ sd2,
+ None)
+ capture = self.pg0.get_capture(1)
+ try:
+ self.assertEqual(capture[0][UDP].dport, 12345)
+ except:
+ self.logger.error(ppp("invalid packet:", capture[0]))
+ raise
+ self.syslog_verify(capture[0][Raw].load,
+ SyslogFacility.local5,
+ SyslogSeverity.err,
+ appname,
+ msgid,
+ sd2,
+ None)
+
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 8ed870ea0de..f8d0e6ca0f9 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -44,6 +44,17 @@ class QOS_SOURCE:
IP = 3
+class SYSLOG_SEVERITY:
+ EMERG = 0
+ ALERT = 1
+ CRIT = 2
+ ERR = 3
+ WARN = 4
+ NOTICE = 5
+ INFO = 6
+ DBG = 7
+
+
class UnexpectedApiReturnValueError(Exception):
""" exception raised when the API return value is unexpected """
pass
@@ -4026,3 +4037,42 @@ class VppPapiProvider(object):
def svs_dump(self):
return self.api(self.papi.svs_dump, {})
+
+ def syslog_set_sender(
+ self,
+ collector,
+ src,
+ collector_port=514,
+ vrf_id=0,
+ max_msg_size=480):
+ """Set syslog sender configuration
+
+ :param collector: colector IP address
+ :param src: source IP address
+ :param collector_port: collector UDP port (Default value = 514)
+ :param vrf_id: VRF id (Default value = 0)
+ :param max_msg_size: maximum message length (Default value = 480)
+ """
+ return self.api(self.papi.syslog_set_sender,
+ {'collector_address': {
+ 'address': collector},
+ 'src_address': {
+ 'address': src},
+ 'collector_port': collector_port,
+ 'vrf_id': vrf_id,
+ 'max_msg_size': max_msg_size})
+
+ def syslog_get_sender(self):
+ """Return syslog sender configuration"""
+ return self.api(self.papi.syslog_get_sender, {})
+
+ def syslog_set_filter(self, severity):
+ """Set syslog filter parameters
+
+ :param severity: severity filter (specified severity and greater match)
+ """
+ return self.api(self.papi.syslog_set_filter, {'severity': severity})
+
+ def syslog_get_filter(self):
+ """Return syslog filter parameters"""
+ return self.api(self.papi.syslog_get_filter, {})