blob: 1cffe03d768b586296408390182cb8a5a472d97c [file] [log] [blame]
Matus Fabianb4515b42018-11-19 04:25:32 -08001/*
2 * Copyright (c) 2018 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/**
16 * @file RFC5424 syslog protocol implementation
17 */
18
19#include <unistd.h>
20#include <vnet/fib/fib_table.h>
21#include <vnet/ip/format.h>
22#include <vnet/syslog/syslog.h>
23#include <vnet/syslog/syslog_udp.h>
24
25#define SYSLOG_VERSION "1"
26#define NILVALUE "-"
27#define DEFAULT_UDP_PORT 514
28#define DEFAULT_MAX_MSG_SIZE 480
29
30#define encode_priority(f, p) ((f << 3) | p)
31
32syslog_main_t syslog_main;
33
34/* format timestamp RFC5424 6.2.3. */
35static u8 *
36format_syslog_timestamp (u8 * s, va_list * args)
37{
38 f64 timestamp = va_arg (*args, f64);
39 struct tm *tm;
40 word msec;
41
42 time_t t = timestamp;
43 tm = gmtime (&t);
44 msec = 1e6 * (timestamp - t);
45 return format (s, "%4d-%02d-%02dT%02d:%02d:%02d.%06dZ", 1900 + tm->tm_year,
46 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
47 tm->tm_sec, msec);
48}
49
50/* format header RFC5424 6.2. */
51static u8 *
52format_syslog_header (u8 * s, va_list * args)
53{
54 syslog_main_t *sm = &syslog_main;
55 syslog_header_t *h = va_arg (*args, syslog_header_t *);
56 u32 pri = encode_priority (h->facility, h->severity);
57
58 return format (s, "<%d>%s %U %U %s %d %s", pri, SYSLOG_VERSION,
59 format_syslog_timestamp, h->timestamp + sm->time_offset,
60 format_ip4_address, &sm->src_address,
61 h->app_name ? h->app_name : NILVALUE, sm->procid,
62 h->msgid ? h->msgid : NILVALUE);
63}
64
65/* format strucured data elements RFC5424 6.3. */
66static u8 *
67format_syslog_structured_data (u8 * s, va_list * args)
68{
69 u8 **sds = va_arg (*args, u8 **);
70 int i;
71
72 if (vec_len (sds))
73 {
74 for (i = 0; i < vec_len (sds); i++)
75 s = format (s, "[%s]", sds[i]);
76 }
77 /* if zero structured data elemts field must contain NILVALUE */
78 else
79 s = format (s, "%s", NILVALUE);
80
81 return s;
82}
83
84static u8 *
85format_syslog_msg (u8 * s, va_list * args)
86{
87 syslog_msg_t *m = va_arg (*args, syslog_msg_t *);
88
89 s =
90 format (s, "%U %U", format_syslog_header, &m->header,
91 format_syslog_structured_data, m->structured_data);
92 /* free-form message is optional */
93 if (m->msg)
94 s = format (s, " %s", m->msg);
95
96 return s;
97}
98
99void
100syslog_msg_sd_init (syslog_msg_t * syslog_msg, char *sd_id)
101{
102 u8 *sd;
103
104 sd = format (0, "%s", sd_id);
105 vec_add1 (syslog_msg->structured_data, sd);
106 syslog_msg->curr_sd_index++;
107}
108
109void
110syslog_msg_add_sd_param (syslog_msg_t * syslog_msg, char *name, char *fmt,
111 ...)
112{
113 va_list va;
114 u8 *value;
115
116 va_start (va, fmt);
117 value = va_format (0, fmt, &va);
118 va_end (va);
119 vec_terminate_c_string (value);
120
121 syslog_msg->structured_data[syslog_msg->curr_sd_index] =
122 format (syslog_msg->structured_data[syslog_msg->curr_sd_index],
123 " %s=\"%s\"", name, value);
124 vec_free (value);
125}
126
127void
128syslog_msg_add_msg (syslog_msg_t * syslog_msg, char *fmt, ...)
129{
130 va_list va;
131 u8 *msg;
132
133 va_start (va, fmt);
134 msg = va_format (0, fmt, &va);
135 va_end (va);
136 vec_terminate_c_string (msg);
137
138 syslog_msg->msg = msg;
139}
140
141void
142syslog_msg_init (syslog_msg_t * syslog_msg, syslog_facility_t facility,
143 syslog_severity_t severity, char *app_name, char *msgid)
144{
145 syslog_main_t *sm = &syslog_main;
146 vlib_main_t *vm = sm->vlib_main;
147
148 syslog_msg->header.facility = facility;
149 syslog_msg->header.severity = severity;
150 syslog_msg->header.timestamp = vlib_time_now (vm);
151 syslog_msg->header.app_name = app_name;
152 syslog_msg->header.msgid = msgid;
153 syslog_msg->structured_data = 0;
154 syslog_msg->curr_sd_index = ~0;
155 syslog_msg->msg = 0;
156}
157
158int
159syslog_msg_send (syslog_msg_t * syslog_msg)
160{
161 syslog_main_t *sm = &syslog_main;
162 vlib_main_t *vm = sm->vlib_main;
163 u32 bi, msg_len, *to_next;
164 u8 *tmp;
165 vlib_buffer_t *b;
166 vlib_buffer_free_list_t *fl;
167 vlib_frame_t *f;
168 int i;
169
170 if (vlib_buffer_alloc (vm, &bi, 1) != 1)
171 return -1;
172
173 b = vlib_get_buffer (vm, bi);
174 clib_memset (vnet_buffer (b), 0, sizeof (*vnet_buffer (b)));
175 fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
176 vlib_buffer_init_for_free_list (b, fl);
177 VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b);
178
179 /* one message per UDP datagram RFC5426 3.1. */
180 tmp = format (0, "%U", format_syslog_msg, syslog_msg);
181 msg_len = vec_len (tmp) - (vec_c_string_is_terminated (tmp) ? 1 : 0);
182 msg_len = msg_len < sm->max_msg_size ? msg_len : sm->max_msg_size;
183 clib_memcpy_fast (b->data, tmp, msg_len);
184 b->current_length = msg_len;
185 vec_free (tmp);
186
187 vec_free (syslog_msg->msg);
188 for (i = 0; i < vec_len (syslog_msg->structured_data); i++)
189 vec_free (syslog_msg->structured_data[i]);
190 vec_free (syslog_msg->structured_data);
191
192 syslog_add_udp_transport (vm, bi);
193
194 f = vlib_get_frame_to_node (vm, sm->ip4_lookup_node_index);
195 to_next = vlib_frame_vector_args (f);
196 to_next[0] = bi;
197 f->n_vectors = 1;
198 vlib_put_frame_to_node (vm, sm->ip4_lookup_node_index, f);
199
200 return 0;
201}
202
203static uword
204unformat_syslog_facility (unformat_input_t * input, va_list * args)
205{
206 u32 *r = va_arg (*args, u32 *);
207
208 if (0);
209#define _(v,f,s) else if (unformat (input, s)) *r = SYSLOG_FACILITY_##f;
210 foreach_syslog_facility
211#undef _
212 else
213 return 0;
214
215 return 1;
216}
217
218static uword
219unformat_syslog_severity (unformat_input_t * input, va_list * args)
220{
221 u32 *r = va_arg (*args, u32 *);
222
223 if (0);
224#define _(v,f,s) else if (unformat (input, s)) *r = SYSLOG_SEVERITY_##f;
225 foreach_syslog_severity
226#undef _
227 else
228 return 0;
229
230 return 1;
231}
232
233static u8 *
234format_syslog_severity (u8 * s, va_list * args)
235{
236 u32 i = va_arg (*args, u32);
237 u8 *t = 0;
238
239 switch (i)
240 {
241#define _(v,f,str) case SYSLOG_SEVERITY_##f: t = (u8 *) str; break;
242 foreach_syslog_severity
243#undef _
244 default:
245 return format (s, "unknown");
246 }
247
248 return format (s, "%s", t);
249}
250
251vnet_api_error_t
252set_syslog_sender (ip4_address_t * collector, u16 collector_port,
253 ip4_address_t * src, u32 vrf_id, u32 max_msg_size)
254{
255 syslog_main_t *sm = &syslog_main;
256 u32 fib_index;
257
258 if (max_msg_size < DEFAULT_MAX_MSG_SIZE)
259 return VNET_API_ERROR_INVALID_VALUE;
260
261 if (collector->as_u32 == 0 || collector_port == 0 || src->as_u32 == 0)
262 return VNET_API_ERROR_INVALID_VALUE;
263
264 if (vrf_id == ~0)
265 {
266 fib_index = ~0;
267 }
268 else
269 {
270 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
271 if (fib_index == ~0)
272 return VNET_API_ERROR_NO_SUCH_FIB;
273 }
274
275 sm->fib_index = fib_index;
276
277 sm->collector.as_u32 = collector->as_u32;
278 sm->collector_port = (u16) collector_port;
279 sm->src_address.as_u32 = src->as_u32;
280 sm->max_msg_size = max_msg_size;
281
282 return 0;
283}
284
285static clib_error_t *
286set_syslog_sender_command_fn (vlib_main_t * vm, unformat_input_t * input,
287 vlib_cli_command_t * cmd)
288{
289 unformat_input_t _line_input, *line_input = &_line_input;
290 ip4_address_t collector, src;
291 u32 collector_port = DEFAULT_UDP_PORT;
292 u32 vrf_id = ~0;
293 u32 max_msg_size = DEFAULT_MAX_MSG_SIZE;
294 clib_error_t *ret = 0;
295
296 collector.as_u32 = 0;
297 src.as_u32 = 0;
298
299 /* Get a line of input. */
300 if (!unformat_user (input, unformat_line_input, line_input))
301 return 0;
302
303 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
304 {
305 if (unformat
306 (line_input, "collector %U", unformat_ip4_address, &collector))
307 ;
308 else if (unformat (line_input, "port %u", &collector_port))
309 ;
310 else if (unformat (line_input, "src %U", unformat_ip4_address, &src))
311 ;
312 else if (unformat (line_input, "vrf-id %u", &vrf_id))
313 ;
314 else if (unformat (line_input, "max-msg-size %u", &max_msg_size))
315 ;
316 else
317 {
318 ret = clib_error_return (0, "Unknown input `%U'",
319 format_unformat_error, line_input);
320 goto done;
321 }
322 }
323
324 if (collector.as_u32 == 0)
325 {
326 ret = clib_error_return (0, "collector address required");
327 goto done;
328 }
329
330 if (src.as_u32 == 0)
331 {
332 ret = clib_error_return (0, "src address required");
333 goto done;
334 }
335
336 if (max_msg_size < DEFAULT_MAX_MSG_SIZE)
337 {
338 ret =
339 clib_error_return (0, "too small max-msg-size value, minimum is %u",
340 DEFAULT_MAX_MSG_SIZE);
341 goto done;
342 }
343
344 vnet_api_error_t rv =
345 set_syslog_sender (&collector, collector_port, &src, vrf_id,
346 max_msg_size);
347
348 if (rv)
349 ret =
350 clib_error_return (0, "set syslog sender failed rv=%d:%U", (int) rv,
351 format_vnet_api_errno, rv);
352
353done:
354 unformat_free (line_input);
355 return ret;
356}
357
358static clib_error_t *
359show_syslog_sender_command_fn (vlib_main_t * vm, unformat_input_t * input,
360 vlib_cli_command_t * cmd)
361{
362 syslog_main_t *sm = &syslog_main;
363 u32 vrf_id = ~0;
364
365 if (sm->fib_index != ~0)
366 vrf_id = fib_table_get_table_id (sm->fib_index, FIB_PROTOCOL_IP4);
367
368 if (syslog_is_enabled ())
369 vlib_cli_output (vm, "collector %U:%u, src address %U, VRF ID %d, "
370 "max-msg-size %u",
371 format_ip4_address, &sm->collector, sm->collector_port,
372 format_ip4_address, &sm->src_address,
373 vrf_id, sm->max_msg_size);
374 else
375 vlib_cli_output (vm, "syslog sender is disabled");
376
377 return 0;
378}
379
380static clib_error_t *
381test_syslog_command_fn (vlib_main_t * vm, unformat_input_t * input,
382 vlib_cli_command_t * cmd)
383{
384 unformat_input_t _line_input, *line_input = &_line_input;
385 syslog_msg_t syslog_msg;
386 syslog_facility_t facility;
387 syslog_severity_t severity;
388 clib_error_t *ret = 0;
389 u8 *app_name = 0, *msgid = 0, *sd_id = 0, *param_name = 0, *param_value = 0;
390
391 if (!syslog_is_enabled ())
392 return 0;
393
394 /* Get a line of input. */
395 if (!unformat_user (input, unformat_line_input, line_input))
396 return 0;
397
398 if (unformat (line_input, "%U", unformat_syslog_facility, &facility))
399 {
400 if (unformat (line_input, "%U", unformat_syslog_severity, &severity))
401 {
402 if (syslog_severity_filter_block (severity))
403 goto done;
404
405 if (unformat (line_input, "%s", &app_name))
406 {
407 if (unformat (line_input, "%s", &msgid))
408 {
409 syslog_msg_init (&syslog_msg, facility, severity,
410 (char *) app_name, (char *) msgid);
411 while (unformat (line_input, "sd-id %s", &sd_id))
412 {
413 syslog_msg_sd_init (&syslog_msg, (char *) sd_id);
414 while (unformat
415 (line_input, "sd-param %s %s", &param_name,
416 &param_value))
417 {
418 syslog_msg_add_sd_param (&syslog_msg,
419 (char *) param_name,
420 (char *) param_value);
421 vec_free (param_name);
422 vec_free (param_value);
423 }
424 vec_free (sd_id);
425 }
426 if (unformat_check_input (line_input) !=
427 UNFORMAT_END_OF_INPUT)
428 syslog_msg_add_msg (&syslog_msg, "%U",
429 format_unformat_input, line_input);
430 syslog_msg_send (&syslog_msg);
431 }
432 else
433 {
434 ret =
435 clib_error_return (0, "Unknown input `%U'",
436 format_unformat_error, line_input);
437 goto done;
438 }
439 }
440 else
441 {
442 ret =
443 clib_error_return (0, "Unknown input `%U'",
444 format_unformat_error, line_input);
445 goto done;
446 }
447 }
448 else
449 {
450 ret =
451 clib_error_return (0, "Unknown input `%U'", format_unformat_error,
452 line_input);
453 goto done;
454 }
455 }
456 else
457 {
458 ret =
459 clib_error_return (0, "Unknown input `%U'", format_unformat_error,
460 line_input);
461 goto done;
462 }
463
464done:
465 vec_free (app_name);
466 vec_free (msgid);
467 unformat_free (line_input);
468 return ret;
469}
470
471static clib_error_t *
472set_syslog_filter_command_fn (vlib_main_t * vm, unformat_input_t * input,
473 vlib_cli_command_t * cmd)
474{
475 unformat_input_t _line_input, *line_input = &_line_input;
476 syslog_main_t *sm = &syslog_main;
477 clib_error_t *ret = 0;
478
479 /* Get a line of input. */
480 if (!unformat_user (input, unformat_line_input, line_input))
481 return 0;
482
483 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
484 {
485 if (unformat
486 (line_input, "severity %U", unformat_syslog_severity,
487 &sm->severity_filter))
488 ;
489 else
490 {
491 ret = clib_error_return (0, "Unknown input `%U'",
492 format_unformat_error, line_input);
493 goto done;
494 }
495 }
496
497done:
498 unformat_free (line_input);
499 return ret;
500}
501
502static clib_error_t *
503show_syslog_filter_command_fn (vlib_main_t * vm, unformat_input_t * input,
504 vlib_cli_command_t * cmd)
505{
506 syslog_main_t *sm = &syslog_main;
507
508 vlib_cli_output (vm, "severity-filter: %U", format_syslog_severity,
509 sm->severity_filter);
510
511 return 0;
512}
513
514/* *INDENT-OFF* */
515/*?
516 * Set syslog sender configuration.
517 *
518 * @cliexpar
519 * @parblock
520 *
521 * Example of how to configure syslog sender:
522 * @cliexcmd{set syslog sender collector 10.10.10.10 port 514 src 172.16.2.2}
523 * @endparblock
524?*/
525VLIB_CLI_COMMAND (set_syslog_sender_command, static) = {
526 .path = "set syslog sender",
527 .short_help = "set syslog sender "
528 "collector <ip4-address> [port <port>] "
529 "src <ip4-address> [vrf-id <vrf-id>] "
530 "[max-msg-size <max-msg-size>]",
531 .function = set_syslog_sender_command_fn,
532};
533
534/*?
535 * Show syslog sender configuration.
536 *
537 * @cliexpar
538 * @parblock
539 *
540 * Example of how to display syslog sender configuration:
541 * @cliexstart{show syslog sender}
542 * collector 10.10.10.10:514, src address 172.16.2.2, VRF ID 0, max-msg-size 480
543 * @cliexend
544 * @endparblock
545?*/
546VLIB_CLI_COMMAND (show_syslog_sender_command, static) = {
547 .path = "show syslog sender",
548 .short_help = "show syslog sender",
549 .function = show_syslog_sender_command_fn,
550};
551
552/*?
553 * This command generate test syslog message.
554 *
555 * @cliexpar
556 * @parblock
557 *
558 * Example of how to generate following syslog message
559 * '<em><180>1 2018-11-07T11:36:41.231759Z 172.16.1.1 test 10484 testMsg
560 * [exampleSDID@32473 eventID="1011" eventSource="App" iut="3"]
561 * this is message</em>'
562 * @cliexcmd{test syslog local6 warning test testMsg sd-id <!--
563 * --> exampleSDID@32473 sd-param eventID 1011 sd-param eventSource App <!--
564 * --> sd-param iut 3 this is message}
565 * @endparblock
566?*/
567VLIB_CLI_COMMAND (test_syslog_command, static) = {
568 .path = "test syslog",
569 .short_help = "test syslog <facility> <severity> <app-name> <msgid> "
570 "[sd-id <sd-id> sd-param <name> <value>] [<message]",
571 .function = test_syslog_command_fn,
572};
573
574/*?
575 * Set syslog severity filter, specified severity and greater match.
576 *
577 * @cliexpar
578 * @parblock
579 *
580 * Example of how to configure syslog severity filter:
581 * @cliexcmd{set syslog filter severity warning}
582 * @endparblock
583?*/
584VLIB_CLI_COMMAND (set_syslog_filter_command, static) = {
585 .path = "set syslog filter",
586 .short_help = "set syslog filter severity <severity>",
587 .function = set_syslog_filter_command_fn,
588};
589
590/*?
591 * Show syslog severity filter.
592 *
593 * @cliexpar
594 * @parblock
595 *
596 * Example of how to display syslog severity filter:
597 * @cliexstart{show syslog filter}
598 * severity-filter: warning
599 * @cliexend
600 * @endparblock
601?*/
602VLIB_CLI_COMMAND (show_syslog_filter_command, static) = {
603 .path = "show syslog filter",
604 .short_help = "show syslog filter",
605 .function = show_syslog_filter_command_fn,
606};
607/* *INDENT-ON* */
608
609static clib_error_t *
610syslog_init (vlib_main_t * vm)
611{
612 syslog_main_t *sm = &syslog_main;
613 f64 vlib_time_0 = vlib_time_now (vm);
614 struct timeval timeval_0;
615 vlib_node_t *ip4_lookup_node;
616
617 sm->vlib_main = vm;
618 sm->vnet_main = vnet_get_main ();
619
620 sm->procid = getpid ();
621 gettimeofday (&timeval_0, 0);
622 sm->time_offset =
623 (f64) timeval_0.tv_sec + (((f64) timeval_0.tv_usec) * 1e-6) - vlib_time_0;
624
625 sm->collector.as_u32 = 0;
626 sm->src_address.as_u32 = 0;
627 sm->collector_port = DEFAULT_UDP_PORT;
628 sm->max_msg_size = DEFAULT_MAX_MSG_SIZE;
629 sm->fib_index = ~0;
630 sm->severity_filter = SYSLOG_SEVERITY_INFORMATIONAL;
631
632 ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
633 sm->ip4_lookup_node_index = ip4_lookup_node->index;
634
635 return 0;
636}
637
638VLIB_INIT_FUNCTION (syslog_init);
639
640/*
641 * fd.io coding-style-patch-verification: ON
642 *
643 * Local Variables:
644 * eval: (c-set-style "gnu")
645 * End:
646 */