Hi,

Is there reason agains adding udp log destination in bird? I have in
attachmealsont a reworked version of the patch.
This version does not use direct socket interface, but extends
birdsock API for its needs. It also should not call (and possibly
block) getaddrinfo() in case when plain IP is specified.
It is a bit hacky - it uses birdsock to bind and connect the socket,
then "steals" its file descriptor for rfile. But it shoud close
correctly as I understand.
And I'm not sure about my implementation using "log_udp_cfg" variable
in the parser to gather configuration. Maybe it is not "safe" and it
should not be global, but some space in pool should be allocated for
it each time.
There are other decisions, I'm not sure about. So if anything is bad -
I can try to update it.

On Wed, Dec 18, 2019 at 8:15 PM Alexander Zubkov <gr...@qrator.net> wrote:
>
> Hi,
>
> On Wed, Dec 18, 2019 at 1:01 PM Ondrej Zajicek <santi...@crfreenet.org> wrote:
> >
> > On Wed, Dec 18, 2019 at 09:14:43AM +0100, Alexander Zubkov wrote:
> > > Hello,
> > >
> > > Made some dirty patch for myself to allow bird to send logs via udp.
> > > But it may be useful not only for me, so posting it here. It could be
> > > useful when server experiencing high IO-load. As syslog and file
> > > operations in bird are blocking, it can be blocked on writing to it
> > > for indefinite time, which could lead to various problems like
> > > protocol timeouts. So udp logging comes in handy here. The tradeoff is
> > > that we can miss some logs if they are not processed in time.
> > > You can specify udp log destination like that:
> > > log udp [host IP|"hostname"] [port NUMBER|"portname"] ...
> >
> > Hello
> >
> > Is this compatible with some standard for UDP logging or with other
> > infrastructure (log deamons), or you just collect it using netcat?
>
> Not sure if it is standard now. And message format could be relatively
> easily converted into one. That is one of the reasons the patch is
> dirty. :)
> From my experience syslog servers are mostly ok with non formatted
> plain text udp messages with some issues, which are more or less
> severe depending on what you do with these logs later. For example
> just tested couple of them:
>
> * syslog-ng with simple source config:
>
> source s_net { network(ip(127.0.0.1) transport(udp) port(514)); };
>
> It most cases it takes the message from packet, prepends it with
> timestamp and hostname (ip) from which the packet was received. There
> are several options of how to parse messages, though. I get logs like
> those with syslog-ng:
>
> Dec 18 21:16:51 127.0.0.1 2019-12-18 21:16:51.773 <INFO> Reconfigured
> Dec 18 21:17:09 127.0.0.1 2019-12-18 21:17:09.090 <INFO> Reconfiguring
> Dec 18 21:17:09 127.0.0.1 2019-12-18 21:17:09.090 <INFO> Reconfigured
>
> * rsyslog with simple udp config:
>
> module(load="imudp")
> input(type="imudp" address="127.0.0.1" port="514")
>
> It is also mostly ok with plaintext messages, it prepends them with
> timestamp, but it tries to parse first field of the message as a
> hostname. I get logs like those with rsyslog:
>
> Dec 18 21:51:27 2019-12-18 21: 51:27.917 <INFO> Started
> Dec 18 21:51:41 2019-12-18 21: 51:41.273 <INFO> Reconfiguring
>
> There can also be issues with message splitting. If message is cut
> into two packets or several messages are there in one packet. This
> should be also taken into consideration when we look at the resulting
> logs.
>
> >
> > One issue in the patch is that getaddrinfo() is blocking and could block
> > for several seconds if you have unreachable DNS servers. The same issue
> > is also in RPKI code, and we plan to add some asynchronous resolver, so
> > no need to handle it in this patch.
>
> Yes, you are right. It can block in that place. But on the other hand
> it is at reload only, i.e. at controllable points in time. And I also
> hope that if I use plain IP address there, it will not use DNS for it.
>
> >
> > We already experienced issues related to excsessive logging, another
> > neat solution is to log to (length-limited) file on ramfs.
> >
> > --
> > Elen sila lumenn' omentielvo
> >
> > Ondrej 'Santiago' Zajicek (email: santi...@crfreenet.org)
> > OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net)
> > "To err is human -- to blame it on a computer is even more so."
commit cbc332b64149ed67af5e2d0d2f9f3ab2f78f708b
Author: Alexander Zubkov <gr...@qrator.net>
Date:   Sun Jan 2 14:14:49 2022 +0100

    Log: add udp log destination
    
    Add UDP log destination, which can be configured with:
    
    log udp ["hostname"|<ip>] [<port>] <classes>;

diff --git a/lib/socket.h b/lib/socket.h
index 96fedeeb..642a56ce 100644
--- a/lib/socket.h
+++ b/lib/socket.h
@@ -84,6 +84,7 @@ typedef struct birdsock {
 sock *sock_new(pool *);			/* Allocate new socket */
 #define sk_new(X) sock_new(X)		/* Wrapper to avoid name collision with OpenSSL */
 
+int sk_hostname_autoresolv(sock *);	/* Auto-resolve an IP address from a hostname */
 int sk_open(sock *);			/* Open socket */
 int sk_rx_ready(sock *s);
 int sk_send(sock *, uint len);		/* Send data, <0=err, >0=ok, 0=sleep */
diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y
index 5c4b5bef..a14d39e4 100644
--- a/sysdep/unix/config.Y
+++ b/sysdep/unix/config.Y
@@ -15,9 +15,15 @@ CF_DEFINES
 
 static struct log_config *this_log;
 
+static struct {
+  ip_addr ip;
+  const char *host;
+  uint port;
+} log_udp_cfg;
+
 CF_DECLS
 
-CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT)
+CF_KEYWORDS(LOG, SYSLOG, UDP, 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)
 
@@ -61,8 +67,41 @@ log_file:
    }
  | SYSLOG syslog_name { this_log->fh = NULL; new_config->syslog_name = $2; }
  | STDERR { this_log->fh = stderr; }
+ | log_udp {
+     sock *sk = sk_new(new_config->pool);
+     sk->type = SK_UDP;
+     sk->daddr = log_udp_cfg.ip;
+     sk->host = log_udp_cfg.host;
+     sk->dport = log_udp_cfg.port;
+
+     this_log->rf = rf_open_sk(new_config->pool, sk);
+     if (!this_log->rf)
+       cf_error("Unable to open udp log");
+     this_log->fh = rf_file(this_log->rf);
+     this_log->pos = -1;
+     this_log->filename = NULL;
+   }
  ;
 
+log_udp: log_udp_start log_udp_host log_udp_port;
+
+log_udp_start: UDP {
+  log_udp_cfg.ip = IPA_NONE;
+  log_udp_cfg.host = NULL;
+  log_udp_cfg.port = 0;
+};
+
+log_udp_host:
+    /* empty */ { log_udp_cfg.ip = ipa_build4(127, 0, 0, 1); }
+  | ipa { log_udp_cfg.ip = $1; }
+  | text { log_udp_cfg.host = $1; }
+  ;
+
+log_udp_port:
+    /* empty */ { log_udp_cfg.port = 514; }
+  | NUM { check_u16($1); log_udp_cfg.port = $1; }
+  ;
+
 log_mask:
    ALL { $$ = ~0; }
  | '{' log_mask_list '}' { $$ = $2; }
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index 3d67d0a7..baccb3b3 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"
@@ -55,6 +56,7 @@
    this to gen small latencies */
 #define MAX_RX_STEPS 4
 
+int sk_write(sock *);
 
 /*
  *	Tracked Files
@@ -103,6 +105,27 @@ rf_open(pool *p, const char *name, const char *mode)
   return r;
 }
 
+struct rfile *
+rf_open_sk(pool *p, sock *s)
+{
+  if (sk_hostname_autoresolv(s) < 0)
+    return NULL;
+  if (sk_open(s) < 0)
+    return NULL;
+  if (sk_write(s) != 0)
+    return NULL;
+
+  FILE *f = fdopen(s->fd, "a");
+  if (!f)
+    return NULL;
+
+  struct rfile *r = ralloc(p, &rf_class);
+  r->f = f;
+  s->fd = -1;
+
+  return r;
+}
+
 void *
 rf_file(struct rfile *f)
 {
@@ -1309,6 +1332,40 @@ sk_open_ssh(sock *s)
 }
 #endif
 
+/*
+ * Resolve the hostname in case IP is not defined.
+ */
+int
+sk_hostname_autoresolv(sock *s)
+{
+  if (ipa_zero(s->daddr) && s->host)
+  {
+    struct addrinfo *res;
+    struct addrinfo hints = {
+        .ai_family = AF_UNSPEC,
+        .ai_socktype = (s->type == SK_UDP) ? SOCK_DGRAM : SOCK_STREAM,
+        .ai_flags = AI_ADDRCONFIG,
+    };
+
+    int err_code = getaddrinfo(s->host, NULL, &hints, &res);
+    if (err_code != 0)
+    {
+      log(L_ERR "Cannot resolve hostname '%s': %s",
+          s->host, gai_strerror(err_code));
+      return -1;
+    }
+
+    ip_addr addr = IPA_NONE;
+    uint unused;
+    sockaddr_read((sockaddr *) res->ai_addr, res->ai_family, &addr, NULL, &unused);
+    freeaddrinfo(res);
+
+    s->daddr = addr;
+  }
+
+  return 0;
+}
+
 /**
  * sk_open - open a socket
  * @s: socket
@@ -1919,12 +1976,15 @@ sk_write(sock *s)
   switch (s->type)
   {
   case SK_TCP_ACTIVE:
+  case SK_UDP:
     {
       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)
-	sk_tcp_connected(s);
+      {
+	if (s->type != SK_UDP) sk_tcp_connected(s);
+      }
       else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS)
 	s->err_hook(s, errno);
       return 0;
diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h
index ad85d1ea..621c3236 100644
--- a/sysdep/unix/unix.h
+++ b/sysdep/unix/unix.h
@@ -109,6 +109,7 @@ 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_open_sk(struct pool *, struct birdsock *);
 void *rf_file(struct rfile *f);
 int rf_fileno(struct rfile *f);
 void test_old_bird(char *path);

Reply via email to