Index | Thread | Search

From:
Claudio Jeker <cjeker@diehard.n-r-g.com>
Subject:
bgpd: better monotime handling
To:
tech@openbsd.org
Date:
Thu, 20 Feb 2025 13:50:32 +0100

Download raw body.

Thread
I wanted to increase the resolution of getmonotime to milliseconds as a
quick way to get a but higher resolution. tb@ convinced me to make this
better so that times are not easily confused.

So here is that diff. monotime_t is now a new type (wrapped into a struct
to prevent integer promotion). The resolution is now actually microseconds
which should be good enough.

This adds enough helper functions to work with monotime_t which was
inspired by things like timeradd(3).

Regress still passes and also my limited testing showed no issues.
-- 
:wq Claudio

Index: bgpctl/Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/Makefile,v
diff -u -p -r1.19 Makefile
--- bgpctl/Makefile	20 Apr 2023 14:01:50 -0000	1.19
+++ bgpctl/Makefile	13 Feb 2025 15:29:22 -0000
@@ -5,7 +5,7 @@
 PROG=	bgpctl
 SRCS=	bgpctl.c output.c output_json.c output_ometric.c parser.c \
 	mrtparser.c json.c ometric.c
-SRCS+=	util.c flowspec.c
+SRCS+=	util.c flowspec.c monotime.c
 CFLAGS+= -Wall
 CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+= -Wmissing-declarations
Index: bgpctl/bgpctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
diff -u -p -r1.315 bgpctl.c
--- bgpctl/bgpctl.c	31 Jan 2025 20:07:53 -0000	1.315
+++ bgpctl/bgpctl.c	17 Feb 2025 13:56:15 -0000
@@ -588,17 +588,14 @@ show(struct imsg *imsg, struct parse_res
 }
 
 time_t
-get_monotime(time_t t)
+get_rel_monotime(monotime_t t)
 {
-	struct timespec ts;
+	monotime_t now;
 
-	if (t == 0)
-		return -1;
-	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
-		err(1, "clock_gettime");
-	if (t > ts.tv_sec)	/* time in the future is not possible */
-		t = ts.tv_sec;
-	return (ts.tv_sec - t);
+	if (!monotime_valid(t))
+		return 0;
+	now = getmonotime();
+	return monotime_to_sec(monotime_sub(now, t));
 }
 
 char *
@@ -649,15 +646,18 @@ fmt_auth_method(enum auth_method method)
 
 #define TF_LEN	16
 
-const char *
+static const char *
 fmt_timeframe(time_t t)
 {
-	static char	 buf[TF_LEN];
-	unsigned int	 sec, min, hrs, day;
-	unsigned long long	 week;
-
-	if (t < 0)
-		t = 0;
+	static char		buf[TF_LEN];
+	unsigned long long	week;
+	unsigned int		sec, min, hrs, day;
+	const char		*due = "";
+
+	if (t < 0) {
+		due = "due in ";
+		t = -t;
+	}
 	week = t;
 
 	sec = week % 60;
@@ -670,25 +670,29 @@ fmt_timeframe(time_t t)
 	week /= 7;
 
 	if (week >= 1000)
-		snprintf(buf, TF_LEN, "%02lluw", week);
+		snprintf(buf, sizeof(buf), "%s%02lluw", due, week);
 	else if (week > 0)
-		snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs);
+		snprintf(buf, sizeof(buf), "%s%02lluw%01ud%02uh",
+		    due, week, day, hrs);
 	else if (day > 0)
-		snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min);
+		snprintf(buf, sizeof(buf), "%s%01ud%02uh%02um",
+		    due, day, hrs, min);
 	else
-		snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec);
+		snprintf(buf, sizeof(buf), "%s%02u:%02u:%02u",
+		    due, hrs, min, sec);
 
 	return (buf);
 }
 
 const char *
-fmt_monotime(time_t t)
+fmt_monotime(monotime_t mt)
 {
-	t = get_monotime(t);
+	time_t t;
 
-	if (t == -1)
+	if (!monotime_valid(mt))
 		return ("Never");
 
+	t = get_rel_monotime(mt);
 	return (fmt_timeframe(t));
 }
 
@@ -1229,20 +1233,17 @@ show_mrt_dump(struct mrt_rib *mr, struct
 	struct ctl_show_rib_request	*req = arg;
 	struct mrt_rib_entry		*mre;
 	struct ibuf			 ibuf;
-	time_t				 now;
 	uint16_t			 i, j;
 
 	memset(&res, 0, sizeof(res));
 	res.flags = req->flags;
-	now = time(NULL);
 
 	for (i = 0; i < mr->nentries; i++) {
 		mre = &mr->entries[i];
 		memset(&ctl, 0, sizeof(ctl));
 		ctl.prefix = mr->prefix;
 		ctl.prefixlen = mr->prefixlen;
-		if (mre->originated <= now)
-			ctl.age = now - mre->originated;
+		ctl.lastchange = time_to_monotime(mre->originated);
 		ctl.true_nexthop = mre->nexthop;
 		ctl.exit_nexthop = mre->nexthop;
 		ctl.origin = mre->origin;
@@ -1314,21 +1315,18 @@ network_mrt_dump(struct mrt_rib *mr, str
 	struct ctl_show_rib_request	*req = arg;
 	struct mrt_rib_entry		*mre;
 	struct ibuf			*msg;
-	time_t				 now;
 	uint16_t			 i, j;
 
 	/* can't announce more than one path so ignore add-path */
 	if (mr->add_path)
 		return;
 
-	now = time(NULL);
 	for (i = 0; i < mr->nentries; i++) {
 		mre = &mr->entries[i];
 		memset(&ctl, 0, sizeof(ctl));
 		ctl.prefix = mr->prefix;
 		ctl.prefixlen = mr->prefixlen;
-		if (mre->originated <= now)
-			ctl.age = now - mre->originated;
+		ctl.lastchange = time_to_monotime(mre->originated);
 		ctl.true_nexthop = mre->nexthop;
 		ctl.exit_nexthop = mre->nexthop;
 		ctl.origin = mre->origin;
Index: bgpctl/bgpctl.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.h,v
diff -u -p -r1.24 bgpctl.h
--- bgpctl/bgpctl.h	31 Jan 2024 11:23:20 -0000	1.24
+++ bgpctl/bgpctl.h	13 Feb 2025 15:30:17 -0000
@@ -42,10 +42,9 @@ extern const struct output show_output, 
 
 #define EOL0(flag)	((flag & F_CTL_SSV) ? ';' : '\n')
 
-time_t		 get_monotime(time_t);
+time_t		 get_rel_monotime(monotime_t);
 char		*fmt_peer(const char *, const struct bgpd_addr *, int);
-const char	*fmt_timeframe(time_t);
-const char	*fmt_monotime(time_t);
+const char	*fmt_monotime(monotime_t);
 const char	*fmt_fib_flags(uint16_t);
 const char	*fmt_origin(uint8_t, int);
 const char	*fmt_flags(uint32_t, int);
Index: bgpctl/output.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v
diff -u -p -r1.59 output.c
--- bgpctl/output.c	29 Jan 2025 13:14:41 -0000	1.59
+++ bgpctl/output.c	13 Feb 2025 16:20:20 -0000
@@ -314,7 +314,7 @@ show_neighbor_full(struct peer *p, struc
 		printf(" with shutdown reason \"%s\"",
 		    log_reason(p->conf.reason));
 	}
-	if (p->stats.last_updown != 0)
+	if (monotime_valid(p->stats.last_updown))
 		printf(", %s for %s",
 		    p->state == STATE_ESTABLISHED ? "up" : "down",
 		    fmt_monotime(p->stats.last_updown));
@@ -467,10 +467,10 @@ show_timer(struct ctl_timer *t)
 {
 	printf("  %-20s ", timernames[t->type]);
 
-	if (t->val <= 0)
-		printf("%-20s\n", "due");
+	if (get_rel_monotime(t->val) >= 0)
+		printf("%s\n", "due");
 	else
-		printf("due in %-13s\n", fmt_timeframe(t->val));
+		printf("%s\n", fmt_monotime(t->val));
 }
 
 static void
@@ -1033,7 +1033,7 @@ show_rib_detail(struct ctl_show_rib *r, 
 	    fmt_flags(r->flags, 0));
 
 	printf("%c    Last update: %s ago%c", EOL0(flag0),
-	    fmt_timeframe(r->age), EOL0(flag0));
+	    fmt_monotime(r->lastchange), EOL0(flag0));
 }
 
 static void
@@ -1113,7 +1113,7 @@ show_rib_set(struct ctl_show_set *set)
 		snprintf(buf, sizeof(buf), "%7zu %7zu %6s",
 		    set->v4_cnt, set->v6_cnt, "-");
 
-	printf("%-6s %-34s %s %11s\n", fmt_set_type(set), set->name,
+	printf("%-6s %-34s %s %12s\n", fmt_set_type(set), set->name,
 	    buf, fmt_monotime(set->lastchange));
 }
 
Index: bgpctl/output_json.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output_json.c,v
diff -u -p -r1.50 output_json.c
--- bgpctl/output_json.c	13 Dec 2024 19:22:01 -0000	1.50
+++ bgpctl/output_json.c	13 Feb 2025 16:06:03 -0000
@@ -140,9 +140,9 @@ json_neighbor_stats(struct peer *p)
 {
 	json_do_object("stats", 0);
 	json_do_string("last_read", fmt_monotime(p->stats.last_read));
-	json_do_int("last_read_sec", get_monotime(p->stats.last_read));
+	json_do_int("last_read_sec", get_rel_monotime(p->stats.last_read));
 	json_do_string("last_write", fmt_monotime(p->stats.last_write));
-	json_do_int("last_write_sec", get_monotime(p->stats.last_write));
+	json_do_int("last_write_sec", get_rel_monotime(p->stats.last_write));
 
 	json_do_object("prefixes", 1);
 	json_do_uint("sent", p->stats.prefix_out_cnt);
@@ -332,7 +332,7 @@ json_neighbor(struct peer *p, struct par
 	}
 	json_do_string("state", statenames[p->state]);
 	json_do_string("last_updown", fmt_monotime(p->stats.last_updown));
-	json_do_int("last_updown_sec", get_monotime(p->stats.last_updown));
+	json_do_int("last_updown_sec", get_rel_monotime(p->stats.last_updown));
 
 	switch (res->action) {
 	case SHOW:
@@ -359,7 +359,7 @@ json_timer(struct ctl_timer *t)
 
 	json_do_object("timer", 1);
 	json_do_string("name", timernames[t->type]);
-	json_do_int("due", t->val);
+	json_do_int("due", -get_rel_monotime(t->val));
 	json_do_end();
 }
 
@@ -862,8 +862,8 @@ json_rib(struct ctl_show_rib *r, struct 
 	json_do_uint("localpref", r->local_pref);
 	json_do_uint("weight", r->weight);
 	json_do_int("dmetric", r->dmetric);
-	json_do_string("last_update", fmt_timeframe(r->age));
-	json_do_int("last_update_sec", r->age);
+	json_do_string("last_update", fmt_monotime(r->lastchange));
+	json_do_int("last_update_sec", get_rel_monotime(r->lastchange));
 
 	/* keep the object open for communities and attributes */
 }
@@ -942,7 +942,7 @@ json_rib_set(struct ctl_show_set *set)
 	json_do_string("name", set->name);
 	json_do_string("type", fmt_set_type(set));
 	json_do_string("last_change", fmt_monotime(set->lastchange));
-	json_do_int("last_change_sec", get_monotime(set->lastchange));
+	json_do_int("last_change_sec", get_rel_monotime(set->lastchange));
 	if (set->type == ASNUM_SET || set->type == ASPA_SET) {
 		json_do_uint("num_ASnum", set->as_cnt);
 	} else {
Index: bgpctl/output_ometric.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output_ometric.c,v
diff -u -p -r1.13 output_ometric.c
--- bgpctl/output_ometric.c	23 Jan 2024 15:55:20 -0000	1.13
+++ bgpctl/output_ometric.c	13 Feb 2025 15:41:17 -0000
@@ -193,14 +193,14 @@ ometric_neighbor_stats(struct peer *p, s
 	ometric_set_state(peer_state, statenames[p->state], ol);
 	ometric_set_int(peer_state_raw, p->state, ol);
 
-	ometric_set_int(peer_last_change, get_monotime(p->stats.last_updown),
-	    ol);
+	ometric_set_int(peer_last_change,
+	    get_rel_monotime(p->stats.last_updown), ol);
 
 	if (p->state == STATE_ESTABLISHED) {
 		ometric_set_int(peer_last_read,
-		    get_monotime(p->stats.last_read), ol);
+		    get_rel_monotime(p->stats.last_read), ol);
 		ometric_set_int(peer_last_write,
-		    get_monotime(p->stats.last_write), ol);
+		    get_rel_monotime(p->stats.last_write), ol);
 	}
 
 	ometric_set_int(peer_prefixes_transmit, p->stats.prefix_out_cnt, ol);
Index: bgpd/Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/Makefile,v
diff -u -p -r1.39 Makefile
--- bgpd/Makefile	17 Apr 2023 08:02:21 -0000	1.39
+++ bgpd/Makefile	13 Feb 2025 15:25:00 -0000
@@ -1,7 +1,7 @@
 #	$OpenBSD: Makefile,v 1.39 2023/04/17 08:02:21 claudio Exp $
 
 PROG=	bgpd
-SRCS=	bgpd.c session.c log.c logmsg.c parse.y config.c \
+SRCS=	bgpd.c session.c log.c logmsg.c parse.y config.c monotime.c \
 	rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c control.c \
 	pfkey.c rde_update.c rde_attr.c rde_community.c printconf.c \
 	rde_filter.c rde_sets.c rde_aspa.c rde_trie.c pftable.c name2id.c \
Index: bgpd/bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
diff -u -p -r1.281 bgpd.c
--- bgpd/bgpd.c	12 Feb 2025 19:33:20 -0000	1.281
+++ bgpd/bgpd.c	13 Feb 2025 16:35:07 -0000
@@ -118,7 +118,7 @@ usage(void)
 #define PFD_SOCK_ROUTE		3
 #define PFD_SOCK_PFKEY		4
 #define PFD_CONNECT_START	5
-#define MAX_TIMEOUT		3600
+#define MAX_TIMEOUT		(3600 * 1000)
 
 int	 cmd_opts;
 
@@ -359,7 +359,7 @@ BROKEN	if (pledge("stdio rpath wpath cpa
 
 		if (timeout < 0 || timeout > MAX_TIMEOUT)
 			timeout = MAX_TIMEOUT;
-		if (poll(pfd, npfd, timeout * 1000) == -1) {
+		if (poll(pfd, npfd, timeout) == -1) {
 			if (errno != EINTR) {
 				log_warn("poll error");
 				quit = 1;
Index: bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
diff -u -p -r1.514 bgpd.h
--- bgpd/bgpd.h	12 Feb 2025 19:33:20 -0000	1.514
+++ bgpd/bgpd.h	17 Feb 2025 13:57:18 -0000
@@ -30,9 +30,10 @@
 #include <poll.h>
 #include <stdarg.h>
 #include <stdint.h>
-
 #include <imsg.h>
 
+#include "monotime.h" 
+
 #define	BGP_VERSION			4
 #define	RTR_MAX_VERSION			2
 #define	RTR_DEFAULT_VERSION		1
@@ -281,7 +282,7 @@ struct rde_prefixset {
 	char				name[SET_NAME_LEN];
 	struct trie_head		th;
 	SIMPLEQ_ENTRY(rde_prefixset)	entry;
-	time_t				lastchange;
+	monotime_t			lastchange;
 	int				dirty;
 };
 SIMPLEQ_HEAD(rde_prefixset_head, rde_prefixset);
@@ -896,7 +897,7 @@ struct ctl_show_nexthop {
 
 struct ctl_show_set {
 	char			name[SET_NAME_LEN];
-	time_t			lastchange;
+	monotime_t		lastchange;
 	size_t			v4_cnt;
 	size_t			v6_cnt;
 	size_t			as_cnt;
@@ -935,7 +936,7 @@ struct ctl_show_rib {
 	struct bgpd_addr	prefix;
 	struct bgpd_addr	remote_addr;
 	char			descr[PEER_DESCR_LEN];
-	time_t			age;
+	monotime_t		lastchange;
 	uint32_t		remote_id;
 	uint32_t		path_id;
 	uint32_t		local_pref;
@@ -1331,7 +1332,7 @@ struct as_set {
 	char				 name[SET_NAME_LEN];
 	SIMPLEQ_ENTRY(as_set)		 entry;
 	struct set_table		*set;
-	time_t				 lastchange;
+	monotime_t			 lastchange;
 	int				 dirty;
 };
 
@@ -1590,9 +1591,6 @@ int	trie_roa_check(struct trie_head *, s
 	    uint32_t);
 void	trie_dump(struct trie_head *);
 int	trie_equal(struct trie_head *, struct trie_head *);
-
-/* timer.c */
-time_t			 getmonotime(void);
 
 /* util.c */
 char		*ibuf_get_string(struct ibuf *, size_t);
Index: bgpd/control.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/control.c,v
diff -u -p -r1.133 control.c
--- bgpd/control.c	12 Feb 2025 13:10:13 -0000	1.133
+++ bgpd/control.c	13 Feb 2025 12:28:46 -0000
@@ -232,7 +232,7 @@ control_close(struct ctl_conn *c)
 
 	close(c->imsgbuf.fd);
 	free(c);
-	pauseaccept = 0;
+	pauseaccept = monotime_clear();
 	return (1);
 }
 
@@ -340,7 +340,7 @@ control_dispatch_msg(struct pollfd *pfd,
 					    p->conf.id, pid);
 				} else {
 					u_int			 i;
-					time_t			 d;
+					monotime_t		 d;
 					struct ctl_timer	 ct;
 
 					imsg_compose(&c->imsgbuf,
Index: bgpd/monotime.c
===================================================================
RCS file: bgpd/monotime.c
diff -N bgpd/monotime.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ bgpd/monotime.c	19 Feb 2025 16:13:01 -0000
@@ -0,0 +1,58 @@
+/*	$OpenBSD$ */
+
+/*
+ * Copyright (c) 2025 Claudio Jeker <claudio@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <time.h>
+
+#include "monotime.h"
+
+static inline monotime_t
+monotime_from_ts(struct timespec *ts)
+{
+	monotime_t mt;
+
+	mt = monotime_from_sec(ts->tv_sec);
+	mt.monotime += ts->tv_nsec / (1000 * 1000 * 1000LL / MONOTIME_RES);
+	return mt;
+}
+
+monotime_t
+getmonotime(void)
+{
+	struct timespec	ts;
+
+	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+		return monotime_clear();
+	return monotime_from_ts(&ts);
+}
+
+time_t
+monotime_to_time(monotime_t mt)
+{
+	mt = monotime_sub(getmonotime(), mt);
+	return time(NULL) - monotime_to_sec(mt);
+}
+
+monotime_t
+time_to_monotime(time_t t)
+{
+	time_t now = time(NULL);
+
+	if (now < t)
+		return monotime_clear();
+	t = now - t;
+	return monotime_sub(getmonotime(), monotime_from_sec(t));
+}
Index: bgpd/monotime.h
===================================================================
RCS file: bgpd/monotime.h
diff -N bgpd/monotime.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ bgpd/monotime.h	20 Feb 2025 12:12:28 -0000
@@ -0,0 +1,93 @@
+/*	$OpenBSD$ */
+
+/*
+ * Copyright (c) 2025 Claudio Jeker <claudio@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <time.h>
+
+/*
+ * bgpd uses an internal microsecond time format.
+ * To reduce errors this is wrapped into a struct and comes
+ * with a bunch of helper functions.
+ */
+#define MONOTIME_RES	(1000 * 1000LL)
+
+typedef struct monotime {
+	long long	monotime;
+} monotime_t;
+
+monotime_t	getmonotime(void);
+time_t		monotime_to_time(monotime_t);
+monotime_t	time_to_monotime(time_t);
+
+static inline monotime_t
+monotime_clear(void)
+{
+	monotime_t mt = { 0 };
+	return mt;
+}
+
+static inline int
+monotime_valid(monotime_t mt)
+{
+	return mt.monotime > 0;
+}
+
+static inline int
+monotime_cmp(monotime_t a, monotime_t b)
+{
+	if (a.monotime > b.monotime)
+		return 1;
+	if (a.monotime < b.monotime)
+		return -1;
+	return 0;
+}
+
+static inline monotime_t
+monotime_add(monotime_t add1, monotime_t add2)
+{
+	monotime_t sum;
+	sum.monotime = add1.monotime + add2.monotime;
+	return sum;
+}
+
+static inline monotime_t
+monotime_sub(monotime_t minu, monotime_t subt)
+{
+	monotime_t dif;
+	dif.monotime = minu.monotime - subt.monotime;
+	return dif;
+}
+
+static inline long long
+monotime_to_msec(monotime_t mt)
+{
+	return mt.monotime / (MONOTIME_RES / 1000);
+}
+
+static inline long long
+monotime_to_sec(monotime_t mt)
+{
+	return mt.monotime / MONOTIME_RES;
+}
+
+static inline monotime_t
+monotime_from_sec(time_t sec)
+{
+	monotime_t mt;
+	mt.monotime = sec;
+	mt.monotime *= MONOTIME_RES;
+	return mt;
+}
Index: bgpd/mrt.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/mrt.c,v
diff -u -p -r1.125 mrt.c
--- bgpd/mrt.c	12 Feb 2025 16:49:56 -0000	1.125
+++ bgpd/mrt.c	13 Feb 2025 12:24:45 -0000
@@ -415,8 +415,7 @@ mrt_dump_entry_mp(struct mrt *mrt, struc
 	if (ibuf_add_n16(h2buf, 1) == -1)		/* status */
 		goto fail;
 	/* originated timestamp */
-	if (ibuf_add_n32(h2buf, time(NULL) - (getmonotime() -
-	    p->lastchange)) == -1)
+	if (ibuf_add_n32(h2buf, monotime_to_time(p->lastchange)) == -1)
 		goto fail;
 
 	n = prefix_nexthop(p);
@@ -577,8 +576,7 @@ mrt_dump_entry(struct mrt *mrt, struct p
 	if (ibuf_add_n8(hbuf, 1) == -1)		/* state */
 		goto fail;
 	/* originated timestamp */
-	if (ibuf_add_n32(hbuf, time(NULL) - (getmonotime() -
-	    p->lastchange)) == -1)
+	if (ibuf_add_n32(hbuf, monotime_to_time(p->lastchange)) == -1)
 		goto fail;
 	switch (p->pt->aid) {
 	case AID_INET:
@@ -653,8 +651,7 @@ mrt_dump_entry_v2_rib(struct rib_entry *
 		if (ibuf_add_n16(buf, prefix_peer(p)->mrt_idx) == -1)
 			goto fail;
 		/* originated timestamp */
-		if (ibuf_add_n32(buf, time(NULL) - (getmonotime() -
-		    p->lastchange)) == -1)
+		if (ibuf_add_n32(buf, monotime_to_time(p->lastchange)) == -1)
 			goto fail;
 
 		/* RFC8050: path-id if add-path is used */
@@ -976,7 +973,7 @@ mrt_dump_hdr_se(struct ibuf ** bp, struc
 
 	if (ibuf_add_n32(*bp, len) == -1)
 		goto fail;
-	/* millisecond field use by the _ET format */
+	/* microsecond field use by the _ET format */
 	if (ibuf_add_n32(*bp, time.tv_nsec / 1000) == -1)
 		goto fail;
 
Index: bgpd/rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
diff -u -p -r1.652 rde.c
--- bgpd/rde.c	12 Feb 2025 16:49:56 -0000	1.652
+++ bgpd/rde.c	13 Feb 2025 14:28:12 -0000
@@ -467,7 +467,7 @@ rde_dispatch_imsg_session(struct imsgbuf
 				peer_flush(peer, aid, peer->staletime[aid]);
 				break;
 			case IMSG_SESSION_RESTARTED:
-				if (peer->staletime[aid])
+				if (monotime_valid(peer->staletime[aid]))
 					peer_flush(peer, aid,
 					    peer->staletime[aid]);
 				break;
@@ -1369,7 +1369,7 @@ rde_dispatch_imsg_peer(struct rde_peer *
 			break;
 		case ROUTE_REFRESH_END_RR:
 			if ((peer->recv_eor & (1 << rr.aid)) != 0 &&
-			    peer->staletime[rr.aid])
+			    monotime_valid(peer->staletime[rr.aid]))
 				peer_flush(peer, rr.aid,
 				    peer->staletime[rr.aid]);
 			else
@@ -2822,14 +2822,14 @@ rde_dump_rib_as(struct prefix *p, struct
 	struct rib_entry	*re;
 	struct prefix		*xp;
 	struct rde_peer		*peer;
-	time_t			 staletime;
+	monotime_t		 staletime;
 	size_t			 aslen;
 	uint8_t			 l;
 
 	nexthop = prefix_nexthop(p);
 	peer = prefix_peer(p);
 	memset(&rib, 0, sizeof(rib));
-	rib.age = getmonotime() - p->lastchange;
+	rib.lastchange = p->lastchange;
 	rib.local_pref = asp->lpref;
 	rib.med = asp->med;
 	rib.weight = asp->weight;
@@ -2890,7 +2890,8 @@ rde_dump_rib_as(struct prefix *p, struct
 	else if (asp->flags & F_ATTR_PARSE_ERR)
 		rib.flags |= F_PREF_INVALID;
 	staletime = peer->staletime[p->pt->aid];
-	if (staletime && p->lastchange <= staletime)
+	if (monotime_valid(staletime) &&
+	    monotime_cmp(p->lastchange, staletime) <= 0)
 		rib.flags |= F_PREF_STALE;
 	if (!adjout) {
 		if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_RECV)) {
Index: bgpd/rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
diff -u -p -r1.313 rde.h
--- bgpd/rde.h	27 Jan 2025 15:22:11 -0000	1.313
+++ bgpd/rde.h	13 Feb 2025 10:42:46 -0000
@@ -89,7 +89,7 @@ struct rde_peer {
 	struct prefix_tree		 updates[AID_MAX];
 	struct prefix_tree		 withdraws[AID_MAX];
 	struct filter_head		*out_rules;
-	time_t				 staletime[AID_MAX];
+	monotime_t			 staletime[AID_MAX];
 	uint32_t			 remote_bgpid;
 	uint32_t			 path_id_tx;
 	unsigned int			 local_if_scope;
@@ -279,7 +279,7 @@ struct prefix {
 	struct rde_community		*communities;
 	struct rde_peer			*peer;
 	struct nexthop			*nexthop;	/* may be NULL */
-	time_t				 lastchange;
+	monotime_t			 lastchange;
 	uint32_t			 path_id;
 	uint32_t			 path_id_tx;
 	uint16_t			 flags;
@@ -373,7 +373,7 @@ void		 rde_generate_updates(struct rib_e
 void		 peer_up(struct rde_peer *, struct session_up *);
 void		 peer_down(struct rde_peer *);
 void		 peer_delete(struct rde_peer *);
-void		 peer_flush(struct rde_peer *, uint8_t, time_t);
+void		 peer_flush(struct rde_peer *, uint8_t, monotime_t);
 void		 peer_stale(struct rde_peer *, uint8_t, int);
 void		 peer_blast(struct rde_peer *, uint8_t);
 void		 peer_dump(struct rde_peer *, uint8_t);
Index: bgpd/rde_aspa.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_aspa.c,v
diff -u -p -r1.5 rde_aspa.c
--- bgpd/rde_aspa.c	16 Aug 2023 08:26:35 -0000	1.5
+++ bgpd/rde_aspa.c	13 Feb 2025 10:45:25 -0000
@@ -51,7 +51,7 @@ struct rde_aspa {
 	size_t			  maxdata;
 	size_t			  curdata;
 	uint32_t		  curset;
-	time_t			  lastchange;
+	monotime_t		  lastchange;
 };
 
 struct aspa_state {
Index: bgpd/rde_decide.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_decide.c,v
diff -u -p -r1.103 rde_decide.c
--- bgpd/rde_decide.c	14 Aug 2024 19:09:51 -0000	1.103
+++ bgpd/rde_decide.c	13 Feb 2025 13:10:50 -0000
@@ -232,10 +232,12 @@ prefix_cmp(struct prefix *p1, struct pre
 	 * evaluation is enabled.
 	 */
 	if (rde_decisionflags() & BGPD_FLAG_DECISION_ROUTEAGE) {
-		if (p1->lastchange < p2->lastchange) /* p1 is older */
+		switch (monotime_cmp(p1->lastchange, p2->lastchange)) {
+		case -1:	/* p1 is older */
 			return rv;
-		if (p1->lastchange > p2->lastchange)
+		case 1:		/* p2 is older */
 			return -rv;
+		}
 	}
 
 	/* 10. lowest BGP Id wins, use ORIGINATOR_ID if present */
Index: bgpd/rde_peer.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v
diff -u -p -r1.46 rde_peer.c
--- bgpd/rde_peer.c	27 Jan 2025 15:22:11 -0000	1.46
+++ bgpd/rde_peer.c	13 Feb 2025 13:14:08 -0000
@@ -303,7 +303,7 @@ rde_generate_updates(struct rib_entry *r
  */
 struct peer_flush {
 	struct rde_peer *peer;
-	time_t		 staletime;
+	monotime_t	 staletime;
 };
 
 static void
@@ -313,7 +313,7 @@ peer_flush_upcall(struct rib_entry *re, 
 	struct rde_aspath *asp;
 	struct bgpd_addr addr;
 	struct prefix *p, *np, *rp;
-	time_t staletime = ((struct peer_flush *)arg)->staletime;
+	monotime_t staletime = ((struct peer_flush *)arg)->staletime;
 	uint32_t i;
 	uint8_t prefixlen;
 
@@ -322,7 +322,8 @@ peer_flush_upcall(struct rib_entry *re, 
 	TAILQ_FOREACH_SAFE(p, &re->prefix_h, entry.list.rib, np) {
 		if (peer != prefix_peer(p))
 			continue;
-		if (staletime && p->lastchange > staletime)
+		if (monotime_valid(staletime) &&
+		    monotime_cmp(p->lastchange, staletime) > 0)
 			continue;
 
 		for (i = RIB_LOC_START; i < rib_size; i++) {
@@ -363,7 +364,7 @@ peer_up(struct rde_peer *peer, struct se
 		 */
 		rib_dump_terminate(peer);
 		peer_imsg_flush(peer);
-		peer_flush(peer, AID_UNSPEC, 0);
+		peer_flush(peer, AID_UNSPEC, monotime_clear());
 		peer->stats.prefix_cnt = 0;
 		peer->state = PEER_DOWN;
 	}
@@ -433,7 +434,7 @@ peer_down(struct rde_peer *peer)
 	peer_imsg_flush(peer);
 
 	/* flush Adj-RIB-In */
-	peer_flush(peer, AID_UNSPEC, 0);
+	peer_flush(peer, AID_UNSPEC, monotime_clear());
 	peer->stats.prefix_cnt = 0;
 }
 
@@ -461,7 +462,7 @@ peer_delete(struct rde_peer *peer)
  * be flushed.
  */
 void
-peer_flush(struct rde_peer *peer, uint8_t aid, time_t staletime)
+peer_flush(struct rde_peer *peer, uint8_t aid, monotime_t staletime)
 {
 	struct peer_flush pf = { peer, staletime };
 
@@ -474,9 +475,9 @@ peer_flush(struct rde_peer *peer, uint8_
 	if (aid == AID_UNSPEC) {
 		uint8_t i;
 		for (i = AID_MIN; i < AID_MAX; i++)
-			peer->staletime[i] = 0;
+			peer->staletime[i] = monotime_clear();
 	} else {
-		peer->staletime[aid] = 0;
+		peer->staletime[aid] = monotime_clear();
 	}
 }
 
@@ -488,10 +489,10 @@ peer_flush(struct rde_peer *peer, uint8_
 void
 peer_stale(struct rde_peer *peer, uint8_t aid, int flushall)
 {
-	time_t now;
+	monotime_t now;
 
 	/* flush the now even staler routes out */
-	if (peer->staletime[aid])
+	if (monotime_valid(peer->staletime[aid]))
 		peer_flush(peer, aid, peer->staletime[aid]);
 
 	peer->staletime[aid] = now = getmonotime();
@@ -506,11 +507,13 @@ peer_stale(struct rde_peer *peer, uint8_
 	peer_imsg_flush(peer);
 
 	if (flushall)
-		peer_flush(peer, aid, 0);
+		peer_flush(peer, aid, monotime_clear());
 
 	/* make sure new prefixes start on a higher timestamp */
-	while (now >= getmonotime())
-		sleep(1);
+	while (monotime_cmp(now, getmonotime()) >= 0) {
+		struct timespec ts = { .tv_nsec = 1000 * 1000 };
+		nanosleep(&ts, NULL);
+	}
 }
 
 /*
@@ -619,17 +622,19 @@ peer_dump(struct rde_peer *peer, uint8_t
 void
 peer_begin_rrefresh(struct rde_peer *peer, uint8_t aid)
 {
-	time_t now;
+	monotime_t now;
 
 	/* flush the now even staler routes out */
-	if (peer->staletime[aid])
+	if (monotime_valid(peer->staletime[aid]))
 		peer_flush(peer, aid, peer->staletime[aid]);
 
 	peer->staletime[aid] = now = getmonotime();
 
 	/* make sure new prefixes start on a higher timestamp */
-	while (now >= getmonotime())
-		sleep(1);
+	while (monotime_cmp(now, getmonotime()) >= 0) {
+		struct timespec ts = { .tv_nsec = 1000 * 1000 };
+		nanosleep(&ts, NULL);
+	}
 }
 
 void
Index: bgpd/rtr.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rtr.c,v
diff -u -p -r1.29 rtr.c
--- bgpd/rtr.c	2 Dec 2024 15:13:57 -0000	1.29
+++ bgpd/rtr.c	13 Feb 2025 13:53:57 -0000
@@ -185,7 +185,7 @@ rtr_main(int debug, int verbose)
 	struct pollfd		*pfd = NULL;
 	void			*newp;
 	size_t			 pfd_elms = 0, i;
-	time_t			 timeout;
+	monotime_t		 timeout;
 
 	log_init(debug, LOG_DAEMON);
 	log_setverbose(verbose);
@@ -242,9 +242,10 @@ rtr_main(int debug, int verbose)
 		}
 
 		/* run the expire timeout every EXPIRE_TIMEOUT seconds */
-		timeout = timer_nextduein(&expire_timer, getmonotime());
-		if (timeout == -1)
+		timeout = timer_nextduein(&expire_timer);
+		if (!monotime_valid(timeout))
 			fatalx("roa-set expire timer no longer running");
+		timeout = monotime_sub(timeout, getmonotime());
 
 		memset(pfd, 0, sizeof(struct pollfd) * pfd_elms);
 
@@ -254,7 +255,7 @@ rtr_main(int debug, int verbose)
 		i = PFD_PIPE_COUNT;
 		i += rtr_poll_events(pfd + i, pfd_elms - i, &timeout);
 
-		if (poll(pfd, i, timeout * 1000) == -1) {
+		if (poll(pfd, i, monotime_to_msec(timeout)) == -1) {
 			if (errno == EINTR)
 				continue;
 			fatal("poll error");
Index: bgpd/rtr_proto.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rtr_proto.c,v
diff -u -p -r1.50 rtr_proto.c
--- bgpd/rtr_proto.c	10 Feb 2025 14:42:13 -0000	1.50
+++ bgpd/rtr_proto.c	13 Feb 2025 13:58:10 -0000
@@ -1310,7 +1310,7 @@ rtr_check_events(struct pollfd *pfds, si
 {
 	struct rtr_session *rs;
 	struct timer *t;
-	time_t now;
+	monotime_t now;
 	size_t i = 0;
 
 	for (i = 0; i < npfds; i++) {
@@ -1362,22 +1362,25 @@ rtr_count(void)
 }
 
 size_t
-rtr_poll_events(struct pollfd *pfds, size_t npfds, time_t *timeout)
+rtr_poll_events(struct pollfd *pfds, size_t npfds, monotime_t *timeout)
 {
 	struct rtr_session *rs;
-	time_t now = getmonotime();
+	monotime_t now = getmonotime();
 	size_t i = 0;
 
 	TAILQ_FOREACH(rs, &rtrs, entry) {
-		time_t nextaction;
+		monotime_t nextaction;
 		struct pollfd *pfd = pfds + i++;
 
 		if (i > npfds)
 			fatalx("%s: too many sessions for pollfd", __func__);
 
-		if ((nextaction = timer_nextduein(&rs->timers, now)) != -1 &&
-		    nextaction < *timeout)
-			*timeout = nextaction;
+		nextaction = timer_nextduein(&rs->timers);
+		if (monotime_valid(nextaction)) {
+			monotime_sub(nextaction, now);
+			if (monotime_cmp(nextaction, *timeout) < 0)
+				*timeout = nextaction;
+		}
 
 		if (rs->state == RTR_STATE_CLOSED) {
 			pfd->fd = -1;
@@ -1546,7 +1549,7 @@ rtr_show(struct rtr_session *rs, pid_t p
 	struct ctl_show_rtr msg;
 	struct ctl_timer ct;
 	u_int i;
-	time_t d;
+	monotime_t d;
 
 	memset(&msg, 0, sizeof(msg));
 
Index: bgpd/session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
diff -u -p -r1.517 session.c
--- bgpd/session.c	18 Feb 2025 16:02:20 -0000	1.517
+++ bgpd/session.c	19 Feb 2025 16:10:07 -0000
@@ -54,6 +54,9 @@
 #define PFD_SOCK_RCTL		4
 #define PFD_LISTENERS_START	5
 
+#define MAX_TIMEOUT		240
+#define PAUSEACCEPT_TIMEOUT	1
+
 void	session_sighdlr(int);
 int	setup_listeners(u_int *);
 void	init_peer(struct peer *, struct bgpd_config *);
@@ -111,7 +114,7 @@ int			 csock = -1, rcsock = -1;
 u_int			 peer_cnt;
 
 struct mrt_head		 mrthead;
-time_t			 pauseaccept;
+monotime_t		 pauseaccept;
 
 static const uint8_t	 marker[MSGSIZE_HEADER_MARKER] = {
 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -192,7 +195,6 @@ setup_listeners(u_int *la_cnt)
 void
 session_main(int debug, int verbose)
 {
-	int			 timeout;
 	unsigned int		 i, j, idx_peers, idx_listeners, idx_mrts;
 	u_int			 pfd_elms = 0, peer_l_elms = 0, mrt_l_elms = 0;
 	u_int			 listener_cnt, ctl_cnt, mrt_cnt;
@@ -203,7 +205,7 @@ session_main(int debug, int verbose)
 	struct pollfd		*pfd = NULL;
 	struct listen_addr	*la;
 	void			*newp;
-	time_t			 now;
+	monotime_t		 now, timeout;
 	short			 events;
 
 	log_init(debug, LOG_DAEMON);
@@ -338,7 +340,7 @@ session_main(int debug, int verbose)
 		set_pollfd(&pfd[PFD_PIPE_ROUTE], ibuf_rde);
 		set_pollfd(&pfd[PFD_PIPE_ROUTE_CTL], ibuf_rde_ctl);
 
-		if (pauseaccept == 0) {
+		if (!monotime_valid(pauseaccept)) {
 			pfd[PFD_SOCK_CTL].fd = csock;
 			pfd[PFD_SOCK_CTL].events = POLLIN;
 			pfd[PFD_SOCK_RCTL].fd = rcsock;
@@ -350,7 +352,7 @@ session_main(int debug, int verbose)
 
 		i = PFD_LISTENERS_START;
 		TAILQ_FOREACH(la, conf->listen_addrs, entry) {
-			if (pauseaccept == 0) {
+			if (!monotime_valid(pauseaccept)) {
 				pfd[i].fd = la->fd;
 				pfd[i].events = POLLIN;
 			} else
@@ -358,11 +360,11 @@ session_main(int debug, int verbose)
 			i++;
 		}
 		idx_listeners = i;
-		timeout = 240;	/* loop every 240s at least */
+		timeout = monotime_from_sec(MAX_TIMEOUT);
 
 		now = getmonotime();
 		RB_FOREACH(p, peer_head, &conf->peers) {
-			time_t	nextaction;
+			monotime_t nextaction;
 			struct timer *pt;
 
 			/* check timers */
@@ -420,9 +422,12 @@ session_main(int debug, int verbose)
 					fatalx("King Bula lost in time");
 				}
 			}
-			if ((nextaction = timer_nextduein(&p->timers,
-			    now)) != -1 && nextaction < timeout)
-				timeout = nextaction;
+			nextaction = timer_nextduein(&p->timers);
+			if (monotime_valid(nextaction)) {
+				nextaction = monotime_sub(nextaction, now);
+				if (monotime_cmp(nextaction, timeout) < 0)
+					timeout = nextaction;
+			}
 
 			/* are we waiting for a write? */
 			events = POLLIN;
@@ -431,7 +436,7 @@ session_main(int debug, int verbose)
 				events |= POLLOUT;
 			/* is there still work to do? */
 			if (p->rpending)
-				timeout = 0;
+				timeout = monotime_clear();
 
 			/* poll events */
 			if (p->fd != -1 && events != 0) {
@@ -459,11 +464,12 @@ session_main(int debug, int verbose)
 		if (i > pfd_elms)
 			fatalx("poll pfd overflow");
 
-		if (pauseaccept && timeout > 1)
-			timeout = 1;
-		if (timeout < 0)
-			timeout = 0;
-		if (poll(pfd, i, timeout * 1000) == -1) {
+		if (monotime_valid(pauseaccept) && monotime_cmp(timeout,
+		    monotime_from_sec(PAUSEACCEPT_TIMEOUT)) > 0)
+			timeout = monotime_from_sec(PAUSEACCEPT_TIMEOUT);
+		if (!monotime_valid(timeout))
+			timeout = monotime_clear();
+		if (poll(pfd, i, monotime_to_msec(timeout)) == -1) {
 			if (errno == EINTR)
 				continue;
 			fatal("poll error");
@@ -473,8 +479,10 @@ session_main(int debug, int verbose)
 		 * If we previously saw fd exhaustion, we stop accept()
 		 * for 1 second to throttle the accept() loop.
 		 */
-		if (pauseaccept && getmonotime() > pauseaccept + 1)
-			pauseaccept = 0;
+		if (monotime_valid(pauseaccept) &&
+		    monotime_cmp(getmonotime(), monotime_add(pauseaccept,
+		    monotime_from_sec(PAUSEACCEPT_TIMEOUT))) > 0)
+			pauseaccept = monotime_clear();
 
 		if (handle_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main) == -1) {
 			log_warnx("SE: Lost connection to parent");
@@ -876,7 +884,7 @@ session_close_connection(struct peer *pe
 {
 	if (peer->fd != -1) {
 		close(peer->fd);
-		pauseaccept = 0;
+		pauseaccept = monotime_clear();
 	}
 	peer->fd = -1;
 }
Index: bgpd/session.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.h,v
diff -u -p -r1.186 session.h
--- bgpd/session.h	18 Feb 2025 16:02:20 -0000	1.186
+++ bgpd/session.h	19 Feb 2025 16:10:07 -0000
@@ -144,9 +144,9 @@ struct peer_stats {
 	unsigned long long	 prefix_sent_update;
 	unsigned long long	 prefix_sent_withdraw;
 	unsigned long long	 prefix_sent_eor;
-	time_t			 last_updown;
-	time_t			 last_read;
-	time_t			 last_write;
+	monotime_t		 last_updown;
+	monotime_t		 last_read;
+	monotime_t		 last_write;
 	uint32_t		 msg_queue_len;
 	uint32_t		 prefix_cnt;
 	uint32_t		 prefix_out_cnt;
@@ -189,7 +189,7 @@ enum Timer {
 struct timer {
 	TAILQ_ENTRY(timer)	entry;
 	enum Timer		type;
-	time_t			val;
+	monotime_t		val;
 };
 
 TAILQ_HEAD(timer_head, timer);
@@ -233,11 +233,11 @@ struct peer {
 	uint8_t			 rdesession;
 };
 
-extern time_t		 pauseaccept;
+extern monotime_t		 pauseaccept;
 
 struct ctl_timer {
 	enum Timer	type;
-	time_t		val;
+	monotime_t	val;
 };
 
 /* carp.c */
@@ -301,7 +301,7 @@ void	rde_main(int, int);
 struct rtr_session;
 size_t			 rtr_count(void);
 void			 rtr_check_events(struct pollfd *, size_t);
-size_t			 rtr_poll_events(struct pollfd *, size_t, time_t *);
+size_t			 rtr_poll_events(struct pollfd *, size_t, monotime_t *);
 struct rtr_session	*rtr_new(uint32_t, struct rtr_config_msg *);
 struct rtr_session	*rtr_get(uint32_t);
 void			 rtr_free(struct rtr_session *);
@@ -346,9 +346,9 @@ struct bgpd_addr *session_localaddr(stru
 
 /* timer.c */
 struct timer	*timer_get(struct timer_head *, enum Timer);
-struct timer	*timer_nextisdue(struct timer_head *, time_t);
-time_t		 timer_nextduein(struct timer_head *, time_t);
-int		 timer_running(struct timer_head *, enum Timer, time_t *);
+struct timer	*timer_nextisdue(struct timer_head *, monotime_t);
+monotime_t	 timer_nextduein(struct timer_head *);
+int		 timer_running(struct timer_head *, enum Timer, monotime_t *);
 void		 timer_set(struct timer_head *, enum Timer, u_int);
 void		 timer_stop(struct timer_head *, enum Timer);
 void		 timer_remove(struct timer_head *, enum Timer);
Index: bgpd/timer.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/timer.c,v
diff -u -p -r1.19 timer.c
--- bgpd/timer.c	11 Dec 2020 12:00:01 -0000	1.19
+++ bgpd/timer.c	13 Feb 2025 15:50:51 -0000
@@ -23,19 +23,6 @@
 #include "session.h"
 #include "log.h"
 
-#define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
-
-time_t
-getmonotime(void)
-{
-	struct timespec	ts;
-
-	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
-		fatal("clock_gettime");
-
-	return (ts.tv_sec);
-}
-
 struct timer *
 timer_get(struct timer_head *th, enum Timer timer)
 {
@@ -49,34 +36,35 @@ timer_get(struct timer_head *th, enum Ti
 }
 
 struct timer *
-timer_nextisdue(struct timer_head *th, time_t now)
+timer_nextisdue(struct timer_head *th, monotime_t now)
 {
 	struct timer *t;
 
 	t = TAILQ_FIRST(th);
-	if (t != NULL && t->val > 0 && t->val <= now)
+	if (t != NULL && monotime_valid(t->val) &&
+	    monotime_cmp(t->val, now) <= 0)
 		return (t);
 	return (NULL);
 }
 
-time_t
-timer_nextduein(struct timer_head *th, time_t now)
+monotime_t
+timer_nextduein(struct timer_head *th)
 {
 	struct timer *t;
 
-	if ((t = TAILQ_FIRST(th)) != NULL && t->val > 0)
-		return (MAXIMUM(t->val - now, 0));
-	return (-1);
+	if ((t = TAILQ_FIRST(th)) != NULL && monotime_valid(t->val))
+		return t->val;
+	return monotime_clear();
 }
 
 int
-timer_running(struct timer_head *th, enum Timer timer, time_t *left)
+timer_running(struct timer_head *th, enum Timer timer, monotime_t *due)
 {
 	struct timer	*t = timer_get(th, timer);
 
-	if (t != NULL && t->val > 0) {
-		if (left != NULL)
-			*left = t->val - getmonotime();
+	if (t != NULL && monotime_valid(t->val)) {
+		if (due != NULL)
+			*due = t->val;
 		return (1);
 	}
 	return (0);
@@ -87,21 +75,26 @@ timer_set(struct timer_head *th, enum Ti
 {
 	struct timer	*t = timer_get(th, timer);
 	struct timer	*next;
+	monotime_t	 ms;
+
+	ms = monotime_from_sec(offset);
+	ms = monotime_add(ms, getmonotime());
 
 	if (t == NULL) {	/* have to create */
 		if ((t = malloc(sizeof(*t))) == NULL)
 			fatal("timer_set");
 		t->type = timer;
 	} else {
-		if (t->val == getmonotime() + (time_t)offset)
+		if (monotime_cmp(t->val, ms) == 0)
 			return;
 		TAILQ_REMOVE(th, t, entry);
 	}
 
-	t->val = getmonotime() + offset;
+	t->val = ms;
 
 	TAILQ_FOREACH(next, th, entry)
-		if (next->val == 0 || next->val > t->val)
+		if (!monotime_valid(next->val) ||
+		    monotime_cmp(next->val, t->val) > 0)
 			break;
 	if (next != NULL)
 		TAILQ_INSERT_BEFORE(next, t, entry);
@@ -115,7 +108,7 @@ timer_stop(struct timer_head *th, enum T
 	struct timer	*t = timer_get(th, timer);
 
 	if (t != NULL) {
-		t->val = 0;
+		t->val = monotime_clear();
 		TAILQ_REMOVE(th, t, entry);
 		TAILQ_INSERT_TAIL(th, t, entry);
 	}