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