From: Claudio Jeker Subject: bgpd: better monotime handling To: tech@openbsd.org Date: Thu, 20 Feb 2025 13:50:32 +0100 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 #include #include - #include +#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 + * + * 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 + +#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 + * + * 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 + +/* + * 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); }