syslog: Add networking backend
If network based syslog backend is enabled in Kconfig file, then syslog messages are sent to external system using UDP. See RFC 5424 and RFC 5426 for details about the syslog protocol. Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
parent
2f4fa71b87
commit
9f73d5e2fd
3 changed files with 274 additions and 0 deletions
|
@ -4,3 +4,4 @@ zephyr_sources_ifdef(
|
|||
event_logger.c
|
||||
kernel_event_logger.c
|
||||
)
|
||||
zephyr_sources_ifdef(CONFIG_SYS_LOG_BACKEND_NET sys_log_net.c)
|
||||
|
|
|
@ -72,5 +72,53 @@ config SYS_LOG_EXT_HOOK
|
|||
default n
|
||||
help
|
||||
Use external hook function for logging.
|
||||
|
||||
config SYS_LOG_BACKEND_NET
|
||||
bool "Networking syslog backend"
|
||||
default n
|
||||
depends on SYS_LOG && NETWORKING
|
||||
select SYS_LOG_EXT_HOOK
|
||||
select NET_CONTEXT_NET_PKT_POOL
|
||||
help
|
||||
Send syslog messages to network server.
|
||||
See RFC 5424 (syslog protocol) and RFC 5426 (syslog over UDP)
|
||||
specifications for details.
|
||||
|
||||
if SYS_LOG_BACKEND_NET
|
||||
|
||||
config SYS_LOG_BACKEND_NET_SERVER
|
||||
string "Syslog server IP address"
|
||||
help
|
||||
This can be either IPv4 or IPv6 address.
|
||||
Server listen UDP port number can be configured here too.
|
||||
Following syntax is supported:
|
||||
192.0.2.1:514
|
||||
192.0.2.42
|
||||
[2001:db8::1]:514
|
||||
[2001:db8::2]
|
||||
2001:db::42
|
||||
|
||||
config SYS_LOG_BACKEND_NET_MAX_BUF
|
||||
int "How many network buffers to allocate for sending messages"
|
||||
range 3 256
|
||||
default 3
|
||||
help
|
||||
Each syslog message will occupy one network buffer.
|
||||
|
||||
config SYS_LOG_BACKEND_NET_MAX_BUF_SIZE
|
||||
int "Max syslog message size"
|
||||
range 64 1180
|
||||
default 256
|
||||
help
|
||||
As each syslog message needs to fit to UDP packet, set this value
|
||||
so that messages are not truncated.
|
||||
The RFC 5426 recommends that for IPv4 the size is 480 octets and for
|
||||
IPv6 the size is 1180 octets. As each buffer will use RAM, the value
|
||||
should be selected so that typical messages will fit the buffer.
|
||||
The total allocated memory will be
|
||||
SYS_LOG_BACKEND_NET_MAX_BUF * SYS_LOG_BACKEND_NET_MAX_BUF_SIZE
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
||||
|
|
225
subsys/logging/sys_log_net.c
Normal file
225
subsys/logging/sys_log_net.c
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <logging/sys_log.h>
|
||||
|
||||
#include <net/net_pkt.h>
|
||||
#include <net/net_context.h>
|
||||
|
||||
/* Set this to 1 if you want to see what is being sent to server */
|
||||
#define DEBUG_PRINTING 0
|
||||
|
||||
static struct net_context *ctx;
|
||||
static struct sockaddr server_addr;
|
||||
|
||||
/* FIXME: As there is no way to figure out these values in the hook
|
||||
* function, use some pre-defined values. Change this to use the
|
||||
* real facility and severity of the logging call when that info is
|
||||
* available.
|
||||
*/
|
||||
static const int facility = 16; /* local0 */
|
||||
static const int severity = 6; /* info */
|
||||
|
||||
#define DATE_EPOCH "1970-01-01T00:00:00.000000-00:00"
|
||||
static char date[sizeof(DATE_EPOCH)];
|
||||
|
||||
#if defined(CONFIG_NET_IPV6) || CONFIG_NET_HOSTNAME_ENABLE
|
||||
#define MAX_HOSTNAME_LEN NET_IPV6_ADDR_LEN
|
||||
#else
|
||||
#define MAX_HOSTNAME_LEN NET_IPV4_ADDR_LEN
|
||||
#endif
|
||||
|
||||
static char hostname[MAX_HOSTNAME_LEN + 1];
|
||||
|
||||
NET_PKT_SLAB_DEFINE(syslog_tx_pkts, CONFIG_SYS_LOG_BACKEND_NET_MAX_BUF);
|
||||
NET_BUF_POOL_DEFINE(syslog_tx_bufs, CONFIG_SYS_LOG_BACKEND_NET_MAX_BUF,
|
||||
CONFIG_SYS_LOG_BACKEND_NET_MAX_BUF_SIZE,
|
||||
CONFIG_NET_BUF_USER_DATA_SIZE, NULL);
|
||||
|
||||
static struct k_mem_slab *get_tx_slab(void)
|
||||
{
|
||||
return &syslog_tx_pkts;
|
||||
}
|
||||
|
||||
struct net_buf_pool *get_data_pool(void)
|
||||
{
|
||||
return &syslog_tx_bufs;
|
||||
}
|
||||
|
||||
static void fill_header(struct net_buf *buf)
|
||||
{
|
||||
snprintk(net_buf_tail(buf),
|
||||
net_buf_tailroom(buf),
|
||||
"<%d>1 %s %s - - - - ",
|
||||
facility * 8 + severity,
|
||||
date,
|
||||
hostname);
|
||||
|
||||
net_buf_add(buf, strlen(buf->data));
|
||||
}
|
||||
|
||||
static void syslog_hook_net(const char *fmt, ...)
|
||||
{
|
||||
struct net_pkt *pkt;
|
||||
struct net_buf *frag;
|
||||
u8_t *ptr;
|
||||
va_list vargs;
|
||||
int ret;
|
||||
|
||||
pkt = net_pkt_get_tx(ctx, K_NO_WAIT);
|
||||
if (!pkt) {
|
||||
return;
|
||||
}
|
||||
|
||||
frag = net_pkt_get_data(ctx, K_NO_WAIT);
|
||||
if (!frag) {
|
||||
net_pkt_unref(pkt);
|
||||
return;
|
||||
}
|
||||
|
||||
net_pkt_frag_add(pkt, frag);
|
||||
|
||||
fill_header(frag);
|
||||
|
||||
va_start(vargs, fmt);
|
||||
|
||||
ptr = net_buf_tail(frag);
|
||||
|
||||
ret = vsnprintk(ptr, (net_buf_tailroom(frag) - 1), fmt, vargs);
|
||||
if (ret < 0) {
|
||||
net_pkt_unref(pkt);
|
||||
return;
|
||||
}
|
||||
|
||||
va_end(vargs);
|
||||
|
||||
if (ret > 0 && ptr[ret - 1] == '\n') {
|
||||
/* No need to send \n to peer so strip it away */
|
||||
ret--;
|
||||
}
|
||||
|
||||
net_buf_add(frag, ret);
|
||||
|
||||
#if DEBUG_PRINTING
|
||||
{
|
||||
static u32_t count;
|
||||
|
||||
printk("%d:%s", ++count, frag->data);
|
||||
}
|
||||
#endif
|
||||
ret = net_context_send(pkt, NULL, K_NO_WAIT, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
net_pkt_unref(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
void syslog_net_hook_install(void)
|
||||
{
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
struct sockaddr_in6 local_addr6 = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_port = 0,
|
||||
};
|
||||
#endif
|
||||
#if defined(CONFIG_NET_IPV4)
|
||||
struct sockaddr_in local_addr4 = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = 0,
|
||||
};
|
||||
#endif
|
||||
struct sockaddr *local_addr = NULL;
|
||||
socklen_t local_addr_len = 0;
|
||||
socklen_t server_addr_len = 0;
|
||||
int ret;
|
||||
|
||||
net_sin(&server_addr)->sin_port = htons(514);
|
||||
|
||||
ret = net_ipaddr_parse(CONFIG_SYS_LOG_BACKEND_NET_SERVER,
|
||||
sizeof(CONFIG_SYS_LOG_BACKEND_NET_SERVER) - 1,
|
||||
&server_addr);
|
||||
if (!ret) {
|
||||
SYS_LOG_ERR("Cannot configure syslog server address");
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_IPV4)
|
||||
if (server_addr.sa_family == AF_INET) {
|
||||
local_addr = (struct sockaddr *)&local_addr4;
|
||||
local_addr_len = sizeof(struct sockaddr_in);
|
||||
server_addr_len = sizeof(struct sockaddr_in);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
if (server_addr.sa_family == AF_INET6) {
|
||||
local_addr = (struct sockaddr *)&local_addr6;
|
||||
local_addr_len = sizeof(struct sockaddr_in6);
|
||||
server_addr_len = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = net_context_get(server_addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
|
||||
&ctx);
|
||||
if (ret < 0) {
|
||||
SYS_LOG_ERR("Cannot get context (%d)", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
#if CONFIG_NET_HOSTNAME_ENABLE
|
||||
memcpy(hostname, net_hostname_get(), MAX_HOSTNAME_LEN);
|
||||
#else /* CONFIG_NET_HOSTNAME_ENABLE */
|
||||
if (server_addr.sa_family == AF_INET6) {
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
const struct in6_addr *src;
|
||||
|
||||
src = net_if_ipv6_select_src_addr(
|
||||
NULL, &net_sin6(&server_addr)->sin6_addr);
|
||||
if (src) {
|
||||
net_addr_ntop(AF_INET6, src, hostname,
|
||||
MAX_HOSTNAME_LEN);
|
||||
|
||||
net_ipaddr_copy(&local_addr6.sin6_addr, src);
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
#else
|
||||
goto unknown;
|
||||
#endif
|
||||
} else if (server_addr.sa_family == AF_INET) {
|
||||
#if defined(CONFIG_NET_IPV4)
|
||||
/* FIXME: take the first IPv4 address of an interface */
|
||||
net_ipaddr_copy(&local_addr4.sin_addr,
|
||||
&net_if_get_default()->ipv4.unicast[0].address.in_addr);
|
||||
|
||||
net_addr_ntop(AF_INET, &local_addr4.sin_addr, hostname,
|
||||
MAX_HOSTNAME_LEN);
|
||||
#else
|
||||
goto unknown;
|
||||
#endif
|
||||
} else {
|
||||
unknown:
|
||||
strncpy(hostname, "zephyr", MAX_HOSTNAME_LEN);
|
||||
}
|
||||
#endif /* CONFIG_NET_HOSTNAME_ENABLE */
|
||||
|
||||
ret = net_context_bind(ctx, local_addr, local_addr_len);
|
||||
if (ret < 0) {
|
||||
SYS_LOG_ERR("Cannot bind context (%d)", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = net_context_connect(ctx, &server_addr, server_addr_len,
|
||||
NULL, K_NO_WAIT, NULL);
|
||||
|
||||
/* We do not care about return value for this UDP connect call that
|
||||
* basically does nothing. Calling the connect is only useful so that
|
||||
* we can see the syslog connection in net-shell.
|
||||
*/
|
||||
|
||||
net_context_setup_pools(ctx, get_tx_slab, get_data_pool);
|
||||
|
||||
syslog_hook_install(syslog_hook_net);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue