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