diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2023-12-13 03:48:12 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2023-12-13 04:01:09 +0100 |
commit | 2c7555cf2ac8439713dd9148b348128c57222a38 (patch) | |
tree | 491afd5a5e62cffd85dee4f726d4eb2f3db42cf8 | |
parent | 8cf1be6f67eaeb9bfd2fffe4a4bc9ae419adffd5 (diff) |
Log: Add support for UDP logging
Add support for UDP logging, using RFC 3164 syslog protocol.
Based on the patch from Alexander Zubkov <green@qrator.net>, thanks!
-rw-r--r-- | doc/bird.sgml | 5 | ||||
-rw-r--r-- | lib/socket.h | 1 | ||||
-rw-r--r-- | sysdep/unix/config.Y | 26 | ||||
-rw-r--r-- | sysdep/unix/io.c | 71 | ||||
-rw-r--r-- | sysdep/unix/log.c | 64 | ||||
-rw-r--r-- | sysdep/unix/unix.h | 8 |
6 files changed, 163 insertions, 12 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 419a4537..79d5873f 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -543,11 +543,12 @@ ipv6 table include "tablename.conf";; </code> - <tag><label id="opt-log">log "<m/filename/" [<m/limit/ "<m/backup/"] | syslog [name <m/name/] | stderr all|{ <m/list of classes/ }</tag> + <tag><label id="opt-log">log "<m/filename/" [<m/limit/ "<m/backup/"] | syslog [name <m/name/] | stderr | udp <m/address/ [port <m/port/] all|{ <m/list of classes/ }</tag> Set logging of messages having the given class (either <cf/all/ or <cf>{ error|trace [, <m/.../] }</cf> etc.) into selected destination - a file specified as a filename string (with optional log rotation information), - syslog (with optional name argument), or the stderr output. + syslog (with optional name argument), the stderr output, or as a UDP + message (in <rfc id="3164"> syslog format). Classes are: <cf/info/, <cf/warning/, <cf/error/ and <cf/fatal/ for messages about local problems, diff --git a/lib/socket.h b/lib/socket.h index 0b6ac589..231c10d8 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -124,6 +124,7 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou #define SKF_BIND 0x10 /* Bind datagram socket to given source address */ #define SKF_HIGH_PORT 0x20 /* Choose port from high range if possible */ #define SKF_FREEBIND 0x40 /* Allow socket to bind to a nonlocal address */ +#define SKF_CONNECT 0x80 /* Connect datagram socket to given dst address/port */ #define SKF_THREAD 0x100 /* Socked used in thread, Do not add to main loop */ #define SKF_TRUNCATED 0x200 /* Received packet was truncated, set by IO layer */ diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index fce64794..8de79600 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -17,9 +17,9 @@ static struct log_config *this_log; CF_DECLS -CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) -CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS) -CF_KEYWORDS(GRACEFUL, RESTART, THREADS) +CF_KEYWORDS(LOG, SYSLOG, NAME, STDERR, UDP, PORT) +CF_KEYWORDS(ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG) +CF_KEYWORDS(DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, TIMEOUT, THREADS) %type <i> log_mask log_mask_list log_cat cfg_timeout %type <t> cfg_name @@ -64,8 +64,28 @@ log_file: } | SYSLOG syslog_name { this_log->fh = NULL; new_config->syslog_name = $2; } | STDERR { this_log->fh = stderr; } + | UDP log_udp_host log_udp_port { + this_log->udp_flag = 1; + + if (!parse_and_exit) + log_open_udp(this_log, new_config->pool); + } ; +log_udp_host: text_or_ipa +{ + if ($1.type == T_STRING) + this_log->host = $1.val.s; + else if ($1.type == T_IP) + this_log->ip = $1.val.ip; + else bug("Bad text_or_ipa"); +}; + +log_udp_port: + /* empty */ { this_log->port = 514; } + | PORT NUM { check_u16($2); this_log->port = $2; } + ; + log_mask: ALL { $$ = ~0; } | '{' log_mask_list '}' { $$ = $2; } diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 4b3eef48..9b499020 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -30,6 +30,7 @@ #include <netinet/tcp.h> #include <netinet/udp.h> #include <netinet/icmp6.h> +#include <netdb.h> #include "nest/bird.h" #include "lib/lists.h" @@ -94,12 +95,25 @@ struct rfile * rf_open(pool *p, const char *name, const char *mode) { FILE *f = fopen(name, mode); + if (!f) + return NULL; + + struct rfile *r = ralloc(p, &rf_class); + r->f = f; + return r; +} + +struct rfile * +rf_fdopen(pool *p, int fd, const char *mode) +{ + FILE *f = fdopen(fd, mode); if (!f) return NULL; struct rfile *r = ralloc(p, &rf_class); r->f = f; + return r; } @@ -1048,6 +1062,14 @@ sk_insert(sock *s) add_tail(&sock_list, &s->n); } +static int +sk_connect(sock *s) +{ + sockaddr sa; + sockaddr_fill(&sa, s->af, s->daddr, s->iface, s->dport); + return connect(s->fd, &sa.sa, SA_LEN(sa)); +} + static void sk_tcp_connected(sock *s) { @@ -1465,8 +1487,7 @@ sk_open(sock *s) switch (s->type) { case SK_TCP_ACTIVE: - sockaddr_fill(&sa, s->af, s->daddr, s->iface, s->dport); - if (connect(fd, &sa.sa, SA_LEN(sa)) >= 0) + if (sk_connect(s) >= 0) sk_tcp_connected(s); else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != ECONNREFUSED && errno != EHOSTUNREACH && errno != ENETUNREACH) @@ -1478,6 +1499,14 @@ sk_open(sock *s) ERR2("listen"); break; + case SK_UDP: + if (s->flags & SKF_CONNECT) + if (sk_connect(s) < 0) + ERR2("connect"); + + sk_alloc_bufs(s); + break; + case SK_SSH_ACTIVE: case SK_MAGIC: break; @@ -1945,10 +1974,7 @@ sk_write_noflush(sock *s) { case SK_TCP_ACTIVE: { - sockaddr sa; - sockaddr_fill(&sa, s->af, s->daddr, s->iface, s->dport); - - if (connect(s->fd, &sa.sa, SA_LEN(sa)) >= 0 || errno == EISCONN) + if (sk_connect(s) >= 0 || errno == EISCONN) sk_tcp_connected(s); else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) s->err_hook(s, errno); @@ -2418,3 +2444,36 @@ test_old_bird(char *path) die("I found another BIRD running."); close(fd); } + + +/* + * DNS resolver + */ + +ip_addr +resolve_hostname(const char *host, int type, const char **err_msg) +{ + struct addrinfo *res; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = (type == SK_UDP) ? SOCK_DGRAM : SOCK_STREAM, + .ai_flags = AI_ADDRCONFIG, + }; + + *err_msg = NULL; + + int err_code = getaddrinfo(host, NULL, &hints, &res); + if (err_code != 0) + { + *err_msg = gai_strerror(err_code); + return IPA_NONE; + } + + ip_addr addr = IPA_NONE; + uint unused; + + sockaddr_read((sockaddr *) res->ai_addr, res->ai_family, &addr, NULL, &unused); + freeaddrinfo(res); + + return addr; +} diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index 53122aee..613a6aa5 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -29,6 +29,7 @@ #include "conf/conf.h" #include "lib/string.h" #include "lib/lists.h" +#include "lib/socket.h" #include "sysdep/unix/unix.h" static int dbg_fd = -1; @@ -138,6 +139,55 @@ log_rotate(struct log_config *l) return log_open(l); } +/* Expected to be called during config parsing */ +int +log_open_udp(struct log_config *l, pool *p) +{ + ASSERT(l->host || ipa_nonzero(l->ip)); + + if (l->host && ipa_zero(l->ip)) + { + const char *err_msg; + l->ip = resolve_hostname(l->host, SK_UDP, &err_msg); + + if (ipa_zero(l->ip)) + { + cf_warn("Cannot resolve hostname '%s': %s", l->host, err_msg); + goto err0; + } + } + + sock *sk = sk_new(p); + sk->type = SK_UDP; + sk->daddr = l->ip; + sk->dport = l->port; + sk->flags = SKF_CONNECT | SKF_THREAD; + + if (sk_open(sk) < 0) + { + cf_warn("Cannot open UDP log socket: %s%#m", sk->err); + goto err1; + } + + /* Move fd from sk resource to rf resource */ + l->rf = rf_fdopen(p, sk->fd, "a"); + if (!l->rf) + goto err1; + + l->fh = rf_file(l->rf); + + sk->fd = -1; + rfree(sk); + + return 0; + +err1: + rfree(sk); +err0: + l->mask = 0; + return -1; +} + /** * log_commit - commit a log message * @class: message class information (%L_DEBUG to %L_BUG, see |lib/birdlib.h|) @@ -168,6 +218,18 @@ log_commit(int class, buffer *buf) { if (l->terminal_flag) fputs("bird: ", l->fh); + else if (l->udp_flag) + { + int pri = LOG_DAEMON | syslog_priorities[class]; + char tbuf[TM_DATETIME_BUFFER_SIZE]; + const char *hostname = (config && config->hostname) ? config->hostname : "<none>"; + const char *fmt = "%b %d %T.%6f"; + if (!tm_format_real_time(tbuf, sizeof(tbuf), fmt, current_real_time())) + strcpy(tbuf, "<error>"); + + /* Legacy RFC 3164 format, but with us precision */ + fprintf(l->fh, "<%d>%s %s %s: ", pri, tbuf, hostname, bird_name); + } else { byte tbuf[TM_DATETIME_BUFFER_SIZE]; @@ -400,7 +462,7 @@ log_switch(int initial, list *logs, const char *new_syslog_name) /* Close the logs to avoid pinning them on disk when deleted */ if (current_log_list) WALK_LIST(l, *current_log_list) - if (l->rf) + if (l->filename && l->rf) log_close(l); /* Reopen the logs, needed for 'configure undo' */ diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index ad85d1ea..c1d966f9 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -109,9 +109,11 @@ void io_loop(void); void io_log_dump(void); int sk_open_unix(struct birdsock *s, char *name); struct rfile *rf_open(struct pool *, const char *name, const char *mode); +struct rfile *rf_fdopen(pool *p, int fd, const char *mode); void *rf_file(struct rfile *f); int rf_fileno(struct rfile *f); void test_old_bird(char *path); +ip_addr resolve_hostname(const char *host, int type, const char **err_msg); /* krt.c bits */ @@ -133,6 +135,12 @@ struct log_config { off_t pos; /* Position/size of current log */ off_t limit; /* Log size limit */ int terminal_flag; + int udp_flag; + const char *host; /* UDP log dst host name */ + ip_addr ip; /* UDP log dst IP address */ + uint port; /* UDP log dst port */ }; +int log_open_udp(struct log_config *l, pool *p); + #endif |