]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - sysdep/unix/io.c
Merge branch 'master' into mq-filter-stack
[thirdparty/bird.git] / sysdep / unix / io.c
index 136355869ee34eabb082c1e37ab2b3697669c659..c9fee3ab371d6a1368c2c1839b72150682214d30 100644 (file)
 #include "nest/bird.h"
 #include "lib/lists.h"
 #include "lib/resource.h"
-#include "sysdep/unix/timer.h"
 #include "lib/socket.h"
 #include "lib/event.h"
 #include "lib/timer.h"
 #include "lib/string.h"
 #include "nest/iface.h"
+#include "conf/conf.h"
 
 #include "sysdep/unix/unix.h"
 #include CONFIG_INCLUDE_SYSIO_H
@@ -55,6 +55,7 @@
    this to gen small latencies */
 #define MAX_RX_STEPS 4
 
+
 /*
  *     Tracked Files
  */
@@ -89,394 +90,29 @@ static struct resclass rf_class = {
   NULL
 };
 
-void *
-tracked_fopen(pool *p, char *name, char *mode)
+struct rfile *
+rf_open(pool *p, char *name, char *mode)
 {
   FILE *f = fopen(name, mode);
 
-  if (f)
-    {
-      struct rfile *r = ralloc(p, &rf_class);
-      r->f = f;
-    }
-  return f;
-}
-
-/**
- * DOC: Timers
- *
- * Timers are resources which represent a wish of a module to call
- * a function at the specified time. The platform dependent code
- * doesn't guarantee exact timing, only that a timer function
- * won't be called before the requested time.
- *
- * In BIRD, time is represented by values of the &bird_clock_t type
- * which are integral numbers interpreted as a relative number of seconds since
- * some fixed time point in past. The current time can be read
- * from variable @now with reasonable accuracy and is monotonic. There is also
- * a current 'absolute' time in variable @now_real reported by OS.
- *
- * Each timer is described by a &timer structure containing a pointer
- * to the handler function (@hook), data private to this function (@data),
- * time the function should be called at (@expires, 0 for inactive timers),
- * for the other fields see |timer.h|.
- */
-
-#define NEAR_TIMER_LIMIT 4
-
-static list near_timers, far_timers;
-static bird_clock_t first_far_timer = TIME_INFINITY;
-
-/* now must be different from 0, because 0 is a special value in timer->expires */
-bird_clock_t now = 1, now_real, boot_time;
-
-static void
-update_times_plain(void)
-{
-  bird_clock_t new_time = time(NULL);
-  int delta = new_time - now_real;
-
-  if ((delta >= 0) && (delta < 60))
-    now += delta;
-  else if (now_real != 0)
-   log(L_WARN "Time jump, delta %d s", delta);
-
-  now_real = new_time;
-}
-
-static void
-update_times_gettime(void)
-{
-  struct timespec ts;
-  int rv;
-
-  rv = clock_gettime(CLOCK_MONOTONIC, &ts);
-  if (rv != 0)
-    die("clock_gettime: %m");
-
-  if (ts.tv_sec != now) {
-    if (ts.tv_sec < now)
-      log(L_ERR "Monotonic timer is broken");
-
-    now = ts.tv_sec;
-    now_real = time(NULL);
-  }
-}
-
-static int clock_monotonic_available;
-
-static inline void
-update_times(void)
-{
-  if (clock_monotonic_available)
-    update_times_gettime();
-  else
-    update_times_plain();
-}
-
-static inline void
-init_times(void)
-{
- struct timespec ts;
- clock_monotonic_available = (clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
- if (!clock_monotonic_available)
-   log(L_WARN "Monotonic timer is missing");
-}
-
-
-static void
-tm_free(resource *r)
-{
-  timer *t = (timer *) r;
-
-  tm_stop(t);
-}
-
-static void
-tm_dump(resource *r)
-{
-  timer *t = (timer *) r;
-
-  debug("(code %p, data %p, ", t->hook, t->data);
-  if (t->randomize)
-    debug("rand %d, ", t->randomize);
-  if (t->recurrent)
-    debug("recur %d, ", t->recurrent);
-  if (t->expires)
-    debug("expires in %d sec)\n", t->expires - now);
-  else
-    debug("inactive)\n");
-}
-
-static struct resclass tm_class = {
-  "Timer",
-  sizeof(timer),
-  tm_free,
-  tm_dump,
-  NULL,
-  NULL
-};
-
-/**
- * tm_new - create a timer
- * @p: pool
- *
- * This function creates a new timer resource and returns
- * a pointer to it. To use the timer, you need to fill in
- * the structure fields and call tm_start() to start timing.
- */
-timer *
-tm_new(pool *p)
-{
-  timer *t = ralloc(p, &tm_class);
-  return t;
-}
-
-static inline void
-tm_insert_near(timer *t)
-{
-  node *n = HEAD(near_timers);
-
-  while (n->next && (SKIP_BACK(timer, n, n)->expires < t->expires))
-    n = n->next;
-  insert_node(&t->n, n->prev);
-}
-
-/**
- * tm_start - start a timer
- * @t: timer
- * @after: number of seconds the timer should be run after
- *
- * This function schedules the hook function of the timer to
- * be called after @after seconds. If the timer has been already
- * started, it's @expire time is replaced by the new value.
- *
- * You can have set the @randomize field of @t, the timeout
- * will be increased by a random number of seconds chosen
- * uniformly from range 0 .. @randomize.
- *
- * You can call tm_start() from the handler function of the timer
- * to request another run of the timer. Also, you can set the @recurrent
- * field to have the timer re-added automatically with the same timeout.
- */
-void
-tm_start(timer *t, unsigned after)
-{
-  bird_clock_t when;
-
-  if (t->randomize)
-    after += random() % (t->randomize + 1);
-  when = now + after;
-  if (t->expires == when)
-    return;
-  if (t->expires)
-    rem_node(&t->n);
-  t->expires = when;
-  if (after <= NEAR_TIMER_LIMIT)
-    tm_insert_near(t);
-  else
-    {
-      if (!first_far_timer || first_far_timer > when)
-       first_far_timer = when;
-      add_tail(&far_timers, &t->n);
-    }
-}
-
-/**
- * tm_stop - stop a timer
- * @t: timer
- *
- * This function stops a timer. If the timer is already stopped,
- * nothing happens.
- */
-void
-tm_stop(timer *t)
-{
-  if (t->expires)
-    {
-      rem_node(&t->n);
-      t->expires = 0;
-    }
-}
-
-static void
-tm_dump_them(char *name, list *l)
-{
-  node *n;
-  timer *t;
-
-  debug("%s timers:\n", name);
-  WALK_LIST(n, *l)
-    {
-      t = SKIP_BACK(timer, n, n);
-      debug("%p ", t);
-      tm_dump(&t->r);
-    }
-  debug("\n");
-}
-
-void
-tm_dump_all(void)
-{
-  tm_dump_them("Near", &near_timers);
-  tm_dump_them("Far", &far_timers);
-}
-
-static inline time_t
-tm_first_shot(void)
-{
-  time_t x = first_far_timer;
-
-  if (!EMPTY_LIST(near_timers))
-    {
-      timer *t = SKIP_BACK(timer, n, HEAD(near_timers));
-      if (t->expires < x)
-       x = t->expires;
-    }
-  return x;
-}
-
-void io_log_event(void *hook, void *data);
-
-static void
-tm_shot(void)
-{
-  timer *t;
-  node *n, *m;
-
-  if (first_far_timer <= now)
-    {
-      bird_clock_t limit = now + NEAR_TIMER_LIMIT;
-      first_far_timer = TIME_INFINITY;
-      n = HEAD(far_timers);
-      while (m = n->next)
-       {
-         t = SKIP_BACK(timer, n, n);
-         if (t->expires <= limit)
-           {
-             rem_node(n);
-             tm_insert_near(t);
-           }
-         else if (t->expires < first_far_timer)
-           first_far_timer = t->expires;
-         n = m;
-       }
-    }
-  while ((n = HEAD(near_timers)) -> next)
-    {
-      int delay;
-      t = SKIP_BACK(timer, n, n);
-      if (t->expires > now)
-       break;
-      rem_node(n);
-      delay = t->expires - now;
-      t->expires = 0;
-      if (t->recurrent)
-       {
-         int i = t->recurrent - delay;
-         if (i < 0)
-           i = 0;
-         tm_start(t, i);
-       }
-      io_log_event(t->hook, t->data);
-      t->hook(t);
-    }
-}
-
-/**
- * tm_parse_datetime - parse a date and time
- * @x: datetime string
- *
- * tm_parse_datetime() takes a textual representation of
- * a date and time (dd-mm-yyyy hh:mm:ss)
- * and converts it to the corresponding value of type &bird_clock_t.
- */
-bird_clock_t
-tm_parse_datetime(char *x)
-{
-  struct tm tm;
-  int n;
-  time_t t;
-
-  if (sscanf(x, "%d-%d-%d %d:%d:%d%n", &tm.tm_mday, &tm.tm_mon, &tm.tm_year, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n) != 6 || x[n])
-    return tm_parse_date(x);
-  tm.tm_mon--;
-  tm.tm_year -= 1900;
-  t = mktime(&tm);
-  if (t == (time_t) -1)
-    return 0;
-  return t;
-}
-/**
- * tm_parse_date - parse a date
- * @x: date string
- *
- * tm_parse_date() takes a textual representation of a date (dd-mm-yyyy)
- * and converts it to the corresponding value of type &bird_clock_t.
- */
-bird_clock_t
-tm_parse_date(char *x)
-{
-  struct tm tm;
-  int n;
-  time_t t;
+  if (!f)
+    return NULL;
 
-  if (sscanf(x, "%d-%d-%d%n", &tm.tm_mday, &tm.tm_mon, &tm.tm_year, &n) != 3 || x[n])
-    return 0;
-  tm.tm_mon--;
-  tm.tm_year -= 1900;
-  tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
-  t = mktime(&tm);
-  if (t == (time_t) -1)
-    return 0;
-  return t;
+  struct rfile *r = ralloc(p, &rf_class);
+  r->f = f;
+  return r;
 }
 
-static void
-tm_format_reltime(char *x, struct tm *tm, bird_clock_t delta)
+void *
+rf_file(struct rfile *f)
 {
-  static char *month_names[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-                                  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-
-  if (delta < 20*3600)
-    bsprintf(x, "%02d:%02d", tm->tm_hour, tm->tm_min);
-  else if (delta < 360*86400)
-    bsprintf(x, "%s%02d", month_names[tm->tm_mon], tm->tm_mday);
-  else
-    bsprintf(x, "%d", tm->tm_year+1900);
+  return f->f;
 }
 
-#include "conf/conf.h"
-
-/**
- * tm_format_datetime - convert date and time to textual representation
- * @x: destination buffer of size %TM_DATETIME_BUFFER_SIZE
- * @fmt_spec: specification of resulting textual representation of the time
- * @t: time
- *
- * This function formats the given relative time value @t to a textual
- * date/time representation (dd-mm-yyyy hh:mm:ss) in real time.
- */
-void
-tm_format_datetime(char *x, struct timeformat *fmt_spec, bird_clock_t t)
+int
+rf_fileno(struct rfile *f)
 {
-  const char *fmt_used;
-  struct tm *tm;
-  bird_clock_t delta = now - t;
-  t = now_real - delta;
-  tm = localtime(&t);
-
-  if (fmt_spec->fmt1 == NULL)
-    return tm_format_reltime(x, tm, delta);
-
-  if ((fmt_spec->limit == 0) || (delta < fmt_spec->limit))
-    fmt_used = fmt_spec->fmt1;
-  else
-    fmt_used = fmt_spec->fmt2;
-
-  int rv = strftime(x, TM_DATETIME_BUFFER_SIZE, fmt_used, tm);
-  if (((rv == 0) && fmt_used[0]) || (rv == TM_DATETIME_BUFFER_SIZE))
-    strcpy(x, "<too-long>");
+  return fileno(f->f);
 }
 
 
@@ -484,6 +120,8 @@ tm_format_datetime(char *x, struct timeformat *fmt_spec, bird_clock_t t)
  *     Time clock
  */
 
+btime boot_time;
+
 void
 times_init(struct timeloop *loop)
 {
@@ -494,10 +132,10 @@ times_init(struct timeloop *loop)
   if (rv < 0)
     die("Monotonic clock is missing");
 
-  if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40)))
+  if ((ts.tv_sec < 0) || (((u64) ts.tv_sec) > ((u64) 1 << 40)))
     log(L_WARN "Monotonic clock is crazy");
 
-  loop->last_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
+  loop->last_time = ts.tv_sec S + ts.tv_nsec NS;
   loop->real_time = 0;
 }
 
@@ -511,7 +149,7 @@ times_update(struct timeloop *loop)
   if (rv < 0)
     die("clock_gettime: %m");
 
-  btime new_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
+  btime new_time = ts.tv_sec S + ts.tv_nsec NS;
 
   if (new_time < loop->last_time)
     log(L_ERR "Monotonic clock is broken");
@@ -520,6 +158,19 @@ times_update(struct timeloop *loop)
   loop->real_time = 0;
 }
 
+void
+times_update_real_time(struct timeloop *loop)
+{
+  struct timespec ts;
+  int rv;
+
+  rv = clock_gettime(CLOCK_REALTIME, &ts);
+  if (rv < 0)
+    die("clock_gettime: %m");
+
+  loop->real_time = ts.tv_sec S + ts.tv_nsec NS;
+}
+
 
 /**
  * DOC: Sockets
@@ -1293,6 +944,18 @@ sk_setup(sock *s)
   }
 #endif
 
+  if (s->vrf && !s->iface)
+  {
+    /* Bind socket to associated VRF interface.
+       This is Linux-specific, but so is SO_BINDTODEVICE. */
+#ifdef SO_BINDTODEVICE
+    struct ifreq ifr = {};
+    strcpy(ifr.ifr_name, s->vrf->name);
+    if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
+      ERR("SO_BINDTODEVICE");
+#endif
+  }
+
   if (s->iface)
   {
 #ifdef SO_BINDTODEVICE
@@ -1308,10 +971,6 @@ sk_setup(sock *s)
 #endif
   }
 
-  if (s->priority >= 0)
-    if (sk_set_priority(s, s->priority) < 0)
-      return -1;
-
   if (sk_is_ipv4(s))
   {
     if (s->flags & SKF_LADDR_RX)
@@ -1362,6 +1021,11 @@ sk_setup(sock *s)
        return -1;
   }
 
+  /* Must be after sk_set_tos4() as setting ToS on Linux also mangles priority */
+  if (s->priority >= 0)
+    if (sk_set_priority(s, s->priority) < 0)
+      return -1;
+
   return 0;
 }
 
@@ -1413,10 +1077,12 @@ sk_passive_connected(sock *s, int type)
 
   sock *t = sk_new(s->pool);
   t->type = type;
+  t->data = s->data;
   t->af = s->af;
   t->fd = fd;
   t->ttl = s->ttl;
   t->tos = s->tos;
+  t->vrf = s->vrf;
   t->rbsize = s->rbsize;
   t->tbsize = s->tbsize;
 
@@ -1477,7 +1143,7 @@ sk_ssh_connect(sock *s)
     default:
       return SSH_ERROR;
     }
-  }
+  } /* fallthrough */
 
   case SK_SSH_SERVER_KNOWN:
   {
@@ -1524,7 +1190,7 @@ sk_ssh_connect(sock *s)
       if (!server_identity_is_ok)
        return SSH_ERROR;
     }
-  }
+  } /* fallthrough */
 
   case SK_SSH_USERAUTH:
   {
@@ -1540,7 +1206,7 @@ sk_ssh_connect(sock *s)
     default:
       return SSH_ERROR;
     }
-  }
+  } /* fallthrough */
 
   case SK_SSH_CHANNEL:
   {
@@ -1548,7 +1214,7 @@ sk_ssh_connect(sock *s)
     s->ssh->channel = ssh_channel_new(s->ssh->session);
     if (s->ssh->channel == NULL)
       return SSH_ERROR;
-  }
+  } /* fallthrough */
 
   case SK_SSH_SESSION:
   {
@@ -1564,7 +1230,7 @@ sk_ssh_connect(sock *s)
     default:
       return SSH_ERROR;
     }
-  }
+  } /* fallthrough */
 
   case SK_SSH_SUBSYSTEM:
   {
@@ -1583,7 +1249,7 @@ sk_ssh_connect(sock *s)
        return SSH_ERROR;
       }
     }
-  }
+  } /* fallthrough */
 
   case SK_SSH_ESTABLISHED:
     s->ssh->state = SK_SSH_ESTABLISHED;
@@ -1890,6 +1556,7 @@ sk_sendmsg(sock *s)
   struct iovec iov = {s->tbuf, s->tpos - s->tbuf};
   byte cmsg_buf[CMSG_TX_SPACE];
   sockaddr dst;
+  int flags = 0;
 
   sockaddr_fill(&dst, s->af, s->daddr, s->iface, s->dport);
 
@@ -1900,6 +1567,13 @@ sk_sendmsg(sock *s)
     .msg_iovlen = 1
   };
 
+#ifdef CONFIG_DONTROUTE_UNICAST
+  /* FreeBSD silently changes TTL to 1 when MSG_DONTROUTE is used, therefore we
+     cannot use it for other cases (e.g. when TTL security is used). */
+  if (ipa_is_ip4(s->daddr) && ip4_is_unicast(ipa_to_ip4(s->daddr)) && (s->ttl == 1))
+    flags = MSG_DONTROUTE;
+#endif
+
 #ifdef CONFIG_USE_HDRINCL
   byte hdr[20];
   struct iovec iov2[2] = { {hdr, 20}, iov };
@@ -1915,7 +1589,7 @@ sk_sendmsg(sock *s)
   if (s->flags & SKF_PKTINFO)
     sk_prepare_cmsgs(s, &msg, cmsg_buf, sizeof(cmsg_buf));
 
-  return sendmsg(s->fd, &msg, 0);
+  return sendmsg(s->fd, &msg, flags);
 }
 
 static inline int
@@ -2349,9 +2023,6 @@ io_update_time(void)
   struct timespec ts;
   int rv;
 
-  if (!clock_monotonic_available)
-    return;
-
   /*
    * This is third time-tracking procedure (after update_times() above and
    * times_update() in BFD), dedicated to internal event log and latency
@@ -2362,7 +2033,7 @@ io_update_time(void)
   if (rv < 0)
     die("clock_gettime: %m");
 
-  last_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
+  last_time = ts.tv_sec S + ts.tv_nsec NS;
 
   if (event_open)
   {
@@ -2490,15 +2161,15 @@ volatile int async_shutdown_flag;
 void
 io_init(void)
 {
-  init_list(&near_timers);
-  init_list(&far_timers);
   init_list(&sock_list);
   init_list(&global_event_list);
   krt_io_init();
-  init_times();
-  update_times();
-  boot_time = now;
-  srandom((int) now_real);
+  // XXX init_times();
+  // XXX update_times();
+  boot_time = current_time();
+
+  u64 now = (u64) current_real_time();
+  srandom((uint) (now ^ (now >> 32)));
 }
 
 static int short_loops = 0;
@@ -2508,9 +2179,8 @@ void
 io_loop(void)
 {
   int poll_tout, timeout;
-  time_t tout;
   int nfds, events, pout;
-  timer2 *t;
+  timer *t;
   sock *s;
   node *n;
   int fdmax = 256;
@@ -2522,21 +2192,14 @@ io_loop(void)
       times_update(&main_timeloop);
       events = ev_run_list(&global_event_list);
       timers_fire(&main_timeloop);
-    timers:
-      update_times();
-      tout = tm_first_shot();
-      if (tout <= now)
-       {
-         tm_shot();
-         goto timers;
-       }
       io_close_event();
 
-      poll_tout = (events ? 0 : MIN(tout - now, 3)) * 1000; /* Time in milliseconds */
+      // FIXME
+      poll_tout = (events ? 0 : 3000); /* Time in milliseconds */
       if (t = timers_first(&main_timeloop))
       {
        times_update(&main_timeloop);
-       timeout = (tm2_remains(t) TO_MS) + 1;
+       timeout = (tm_remains(t) TO_MS) + 1;
        poll_tout = MIN(poll_tout, timeout);
       }