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"] ...
By default it uses destination ip 127.0.0.1 and port 514.
There are some constraints like not much error handling. And this
destination replaces file destination, i.e. you can not use those
simultaneously and it may leak descriptors and some memory probably if
both are specified. It also makes a new socket every time
configuration is reloaded even if there is no change in options, but
this is not a great deal.
diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y
index af82e5bd..bec8d7dc 100644
--- a/sysdep/unix/config.Y
+++ b/sysdep/unix/config.Y
@@ -20,11 +20,12 @@ 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)
+CF_KEYWORDS(UDP, HOST, PORT)
 
 %type <i> log_mask log_mask_list log_cat cfg_timeout
 %type <t> cfg_name
 %type <tf> timeformat_which
-%type <t> syslog_name
+%type <t> syslog_name log_udp_server_name log_udp_server_port
 
 CF_GRAMMAR
 
@@ -58,8 +59,36 @@ log_file:
    }
  | SYSLOG syslog_name { this_log->fh = NULL; new_config->syslog_name = $2; }
  | STDERR { this_log->fh = stderr; }
+ | UDP log_udp_server_name log_udp_server_port {
+     this_log->rf = rf_open_udp(new_config->pool, $2, $3);
+     if (!this_log->rf) cf_error("Unable to open udp log '%s %s': %m", $2, $3);
+     this_log->fh = rf_file(this_log->rf);
+     this_log->pos = -1;
+     this_log->filename = NULL;
+   }
  ;
 
+log_udp_server_name:
+    /* empty */ { $$ = "127.0.0.1"; }
+  | HOST ipa {
+      char *server_name = cfg_allocz(IPA_MAX_TEXT_LENGTH+1);
+      bsnprintf(server_name, IPA_MAX_TEXT_LENGTH+1, "%I", $2);
+      $$ = server_name;
+    }
+  | HOST text { $$ = $2; }
+  ;
+
+log_udp_server_port:
+    /* empty */ { $$ = "514"; }
+  | PORT NUM {
+      check_u16($2);
+      char *server_port = cfg_allocz(32);
+      bsnprintf(server_port, 32, "%u", $2);
+      $$ = server_port;
+    }
+  | PORT text { $$ = $2; }
+  ;
+
 log_mask:
    ALL { $$ = ~0; }
  | '{' log_mask_list '}' { $$ = $2; }
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index 5e4d9573..c98fe8a6 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"
@@ -103,6 +104,54 @@ rf_open(pool *p, char *name, char *mode)
   return r;
 }
 
+struct rfile *
+rf_open_udp(pool *p, char *server_hostname, char *server_port)
+{
+  int gai_error;
+  struct addrinfo hints;
+  struct addrinfo *ai;
+  int s;
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_DGRAM;
+  hints.ai_flags = 0;
+  hints.ai_protocol = 0;
+
+  gai_error = getaddrinfo(server_hostname, server_port, &hints, &ai);
+  if (gai_error != 0)
+    return NULL;
+
+  s = socket(ai->ai_family,
+             ai->ai_socktype,
+             ai->ai_protocol);
+  if (s < 0)
+  {
+    freeaddrinfo(ai);
+    return NULL;
+  }
+
+  if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0)
+  {
+    freeaddrinfo(ai);
+    return NULL;
+  }
+
+  freeaddrinfo(ai);
+
+  FILE *f = fdopen(s, "a");
+
+  if (!f)
+  {
+    close(s);
+    return NULL;
+  }
+
+  struct rfile *r = ralloc(p, &rf_class);
+  r->f = f;
+  return r;
+}
+
 void *
 rf_file(struct rfile *f)
 {
diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h
index bd817bf2..7d88e6f6 100644
--- a/sysdep/unix/unix.h
+++ b/sysdep/unix/unix.h
@@ -107,6 +107,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 *, char *name, char *mode);
+struct rfile *rf_open_udp(struct pool *, char *server_hostname, char *server_port);
 void *rf_file(struct rfile *f);
 int rf_fileno(struct rfile *f);
 void test_old_bird(char *path);

Reply via email to