Merge branch 'opaque-timer' into 'master'

Support for CFI

See merge request slirp/libslirp!117
diff --git a/meson.build b/meson.build
index 37d3bf4..1fb2cd3 100644
--- a/meson.build
+++ b/meson.build
@@ -44,9 +44,9 @@
 # - If the interface is the same as the previous version, but bugs are
 #   fixed, change:
 #      REVISION += 1
-lt_current = 3
-lt_revision = 1
-lt_age = 3
+lt_current = 4
+lt_revision = 0
+lt_age = 4
 lt_version = '@0@.@1@.@2@'.format(lt_current - lt_age, lt_age, lt_revision)
 
 host_system = host_machine.system()
diff --git a/src/ip6_icmp.c b/src/ip6_icmp.c
index 738b40f..0d7ee69 100644
--- a/src/ip6_icmp.c
+++ b/src/ip6_icmp.c
@@ -10,25 +10,14 @@
 #define NDP_Interval \
     g_rand_int_range(slirp->grand, NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
 
-static void ra_timer_handler(void *opaque)
-{
-    Slirp *slirp = opaque;
-
-    slirp->cb->timer_mod(slirp->ra_timer,
-                         slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS +
-                             NDP_Interval,
-                         slirp->opaque);
-    ndp_send_ra(slirp);
-}
-
-void icmp6_init(Slirp *slirp)
+void icmp6_post_init(Slirp *slirp)
 {
     if (!slirp->in6_enabled) {
         return;
     }
 
     slirp->ra_timer =
-        slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque);
+        slirp_timer_new(slirp, SLIRP_TIMER_RA, NULL);
     slirp->cb->timer_mod(slirp->ra_timer,
                          slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS +
                              NDP_Interval,
@@ -140,7 +129,7 @@
 /*
  * Send NDP Router Advertisement
  */
-void ndp_send_ra(Slirp *slirp)
+static void ndp_send_ra(Slirp *slirp)
 {
     DEBUG_CALL("ndp_send_ra");
 
@@ -219,6 +208,15 @@
     ip6_output(NULL, t, 0);
 }
 
+void ra_timer_handler(Slirp *slirp, void *unused)
+{
+    slirp->cb->timer_mod(slirp->ra_timer,
+                         slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS +
+                             NDP_Interval,
+                         slirp->opaque);
+    ndp_send_ra(slirp);
+}
+
 /*
  * Send NDP Neighbor Solitication
  */
diff --git a/src/ip6_icmp.h b/src/ip6_icmp.h
index 9070999..9f378f1 100644
--- a/src/ip6_icmp.h
+++ b/src/ip6_icmp.h
@@ -209,12 +209,12 @@
 #define NDP_AdvPrefLifetime 14400
 #define NDP_AdvAutonomousFlag 1
 
-void icmp6_init(Slirp *slirp);
+void icmp6_post_init(Slirp *slirp);
 void icmp6_cleanup(Slirp *slirp);
 void icmp6_input(struct mbuf *);
 void icmp6_forward_error(struct mbuf *m, uint8_t type, uint8_t code, struct in6_addr *src);
 void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code);
-void ndp_send_ra(Slirp *slirp);
 void ndp_send_ns(Slirp *slirp, struct in6_addr addr);
+void ra_timer_handler(Slirp *slirp, void *unused);
 
 #endif
diff --git a/src/ip6_input.c b/src/ip6_input.c
index b3d9865..4aca082 100644
--- a/src/ip6_input.c
+++ b/src/ip6_input.c
@@ -11,9 +11,9 @@
  * IP initialization: fill in IP protocol switch table.
  * All protocols not implemented in kernel go to raw IP protocol handler.
  */
-void ip6_init(Slirp *slirp)
+void ip6_post_init(Slirp *slirp)
 {
-    icmp6_init(slirp);
+    icmp6_post_init(slirp);
 }
 
 void ip6_cleanup(Slirp *slirp)
diff --git a/src/libslirp.h b/src/libslirp.h
index 1e75501..77396f0 100644
--- a/src/libslirp.h
+++ b/src/libslirp.h
@@ -39,6 +39,11 @@
 typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque);
 typedef int (*SlirpGetREventsCb)(int idx, void *opaque);
 
+typedef enum SlirpTimerId {
+    SLIRP_TIMER_RA,
+    SLIRP_TIMER_NUM,
+} SlirpTimerId;
+
 /*
  * Callbacks from slirp, to be set by the application.
  *
@@ -58,7 +63,8 @@
     void (*guest_error)(const char *msg, void *opaque);
     /* Return the virtual clock value in nanoseconds */
     int64_t (*clock_get_ns)(void *opaque);
-    /* Create a new timer with the given callback and opaque data */
+    /* Create a new timer with the given callback and opaque data. Not
+     * needed if timer_new_opaque is provided. */
     void *(*timer_new)(SlirpTimerCb cb, void *cb_opaque, void *opaque);
     /* Remove and free a timer */
     void (*timer_free)(void *timer, void *opaque);
@@ -71,6 +77,16 @@
     /* Kick the io-thread, to signal that new events may be processed because some TCP buffer
      * can now receive more data, i.e. slirp_socket_can_recv will return 1. */
     void (*notify)(void *opaque);
+
+    /*
+     * Fields introduced in SlirpConfig version 4 begin
+     */
+
+    /* Initialization has completed and a Slirp* has been created.  */
+    void (*init_completed)(Slirp *slirp, void *opaque);
+    /* Create a new timer.  When the timer fires, the application passes
+     * the SlirpTimerId and cb_opaque to slirp_handle_timer.  */
+    void *(*timer_new_opaque)(SlirpTimerId id, void *cb_opaque, void *opaque);
 } SlirpCb;
 
 #define SLIRP_CONFIG_VERSION_MIN 1
@@ -166,6 +182,11 @@
  * guest network, to be interpreted by slirp. */
 void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
 
+/* This is called by the application when a timer expires, if it provides
+ * the timer_new_opaque callback.  It is not needed if the application only
+ * uses timer_new. */
+void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque);
+
 /* These set up / remove port forwarding between a host port in the real world
  * and the guest network. */
 int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
diff --git a/src/libslirp.map b/src/libslirp.map
index 792b0a9..3921f8a 100644
--- a/src/libslirp.map
+++ b/src/libslirp.map
@@ -34,3 +34,7 @@
     slirp_remove_hostxfwd;
     slirp_neighbor_info;
 } SLIRP_4.2;
+
+SLIRP_4.7 {
+    slirp_handle_timer;
+} SLIRP_4.5;
diff --git a/src/slirp.c b/src/slirp.c
index 5e08b53..1423b01 100644
--- a/src/slirp.c
+++ b/src/slirp.c
@@ -528,6 +528,43 @@
     }
 }
 
+static void ra_timer_handler_cb(void *opaque)
+{
+    Slirp *slirp = opaque;
+
+    return ra_timer_handler(slirp, NULL);
+}
+
+void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque)
+{
+    g_return_if_fail(id >= 0 && id < SLIRP_TIMER_NUM);
+
+    switch (id) {
+    case SLIRP_TIMER_RA:
+        return ra_timer_handler(slirp, cb_opaque);
+    default:
+        abort();
+    }
+}
+
+void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque)
+{
+    g_return_val_if_fail(id >= 0 && id < SLIRP_TIMER_NUM, NULL);
+
+    if (slirp->cfg_version >= 4 && slirp->cb->timer_new_opaque) {
+        return slirp->cb->timer_new_opaque(id, cb_opaque, slirp->opaque);
+    }
+
+    switch (id) {
+    case SLIRP_TIMER_RA:
+        g_return_val_if_fail(cb_opaque == NULL, NULL);
+        return slirp->cb->timer_new(ra_timer_handler_cb, slirp, slirp->opaque);
+
+    default:
+	abort();
+    }
+}
+
 Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque)
 {
     Slirp *slirp;
@@ -547,6 +584,7 @@
 
     slirp_init_once();
 
+    slirp->cfg_version = cfg->version;
     slirp->opaque = opaque;
     slirp->cb = callbacks;
     slirp->grand = g_rand_new();
@@ -557,7 +595,6 @@
 
     if_init(slirp);
     ip_init(slirp);
-    ip6_init(slirp);
 
     m_init(slirp);
 
@@ -607,6 +644,11 @@
         slirp->disable_dhcp = false;
     }
 
+    if (slirp->cfg_version >= 4 && slirp->cb->init_completed) {
+        slirp->cb->init_completed(slirp, slirp->opaque);
+    }
+
+    ip6_post_init(slirp);
     return slirp;
 }
 
diff --git a/src/slirp.h b/src/slirp.h
index 239d570..35c2be3 100644
--- a/src/slirp.h
+++ b/src/slirp.h
@@ -120,6 +120,8 @@
                       uint8_t out_ethaddr[ETH_ALEN]);
 
 struct Slirp {
+    int cfg_version;
+
     unsigned time_fasttimo;
     unsigned last_slowtimo;
     bool do_slowtimo;
@@ -244,7 +246,7 @@
 int ip_output(struct socket *, struct mbuf *);
 
 /* ip6_input.c */
-void ip6_init(Slirp *);
+void ip6_post_init(Slirp *);
 void ip6_cleanup(Slirp *);
 void ip6_input(struct mbuf *);
 
@@ -280,5 +282,6 @@
                                      int guest_port);
 
 void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len);
+void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque);
 
 #endif
diff --git a/test/pingtest.c b/test/pingtest.c
index 15249f0..3bb0488 100644
--- a/test/pingtest.c
+++ b/test/pingtest.c
@@ -196,7 +196,7 @@
 }
 
 struct timer {
-    SlirpTimerCb cb;
+    SlirpTimerId id;
     void *cb_opaque;
     int64_t expire;
     struct timer *next;
@@ -204,9 +204,9 @@
 
 static struct timer *timer_queue;
 
-static void *timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque) {
+static void *timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *opaque) {
     struct timer *new_timer = malloc(sizeof(*new_timer));
-    new_timer->cb = cb;
+    new_timer->id = id;
     new_timer->cb_opaque = cb_opaque;
     new_timer->next = NULL;
     return new_timer;
@@ -242,14 +242,14 @@
     *t = timer;
 }
 
-static void timer_check(void) {
+static void timer_check(Slirp *slirp) {
     while (timer_queue && timer_queue->expire <= mytime)
     {
         struct timer *t = timer_queue;
         printf("handling %p at time %lu\n",
                t, (unsigned long) timer_queue->expire);
         timer_queue = t->next;
-        t->cb(t->cb_opaque);
+        slirp_handle_timer(slirp, t->id, t->cb_opaque);
     }
 }
 
@@ -378,7 +378,7 @@
     .send_packet = send_packet,
     .guest_error = guest_error,
     .clock_get_ns = clock_get_ns,
-    .timer_new = timer_new,
+    .timer_new_opaque = timer_new_opaque,
     .timer_free = timer_free,
     .timer_mod = timer_mod,
     .register_poll_fd = register_poll_fd,
@@ -389,7 +389,7 @@
 
 int main(int argc, char *argv[]) {
     SlirpConfig config = {
-        .version = 3,
+        .version = 4,
         .restricted = false,
         .in_enabled = true,
         .vnetwork.s_addr = htonl(0x0a000200),
@@ -472,7 +472,7 @@
     while (!done) {
         printf("time %lu\n", (unsigned long) mytime);
 
-        timer_check();
+        timer_check(slirp);
         /* Here we make the virtual time wait like the real time, but we could
          * make it wait differently */
         timeout = timer_timeout();