From f160c0f4e3e0c4ba49d2f5d13e18b17eab277007 Mon Sep 17 00:00:00 2001 From: Wolfgang Hottgenroth Date: Fri, 14 Feb 2025 17:39:57 +0100 Subject: [PATCH] initial --- Makefile | 15 +++ log.c | 218 +++++++++++++++++++++++++++++++++++++ log.h | 40 +++++++ mib.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ mib.h | 27 +++++ snmpd.h | 119 ++++++++++++++++++++ timer.c | 169 ++++++++++++++++++++++++++++ util.c | 229 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 1144 insertions(+) create mode 100644 Makefile create mode 100644 log.c create mode 100644 log.h create mode 100644 mib.c create mode 100644 mib.h create mode 100644 snmpd.h create mode 100644 timer.c create mode 100644 util.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..32ceeb4 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +PROG= snmpd_ntpd +SRCS= mib.c log.c timer.c util.c +MAN= + +CFLAGS+= -Wall -I${.CURDIR} +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare + +LDADD= -lagentx -levent -lkvm +DPADD= ${LIBAGENTX} ${LIBEVENT} ${LIBKVM} +BINDIR= /usr/libexec/snmpd/ + +.include diff --git a/log.c b/log.c new file mode 100644 index 0000000..3a322bf --- /dev/null +++ b/log.c @@ -0,0 +1,218 @@ +/* $OpenBSD: log.c,v 1.1.1.1 2022/09/01 14:20:34 martijn Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * 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 +#include +#include +#include +#include +#include + +static int debug; +static int verbose; +const char *log_procname; + +void log_init(int, int); +void log_procinit(const char *); +void log_setverbose(int); +int log_getverbose(void); +void log_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void logit(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void vlog(int, const char *, va_list) + __attribute__((__format__ (printf, 2, 0))); +__dead void fatal(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +__dead void fatalx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + +void +log_init(int n_debug, int facility) +{ + extern char *__progname; + + debug = n_debug; + verbose = n_debug; + log_procinit(__progname); + + if (!debug) + openlog(__progname, LOG_PID | LOG_NDELAY, facility); + + tzset(); +} + +void +log_procinit(const char *procname) +{ + if (procname != NULL) + log_procname = procname; +} + +void +log_setverbose(int v) +{ + verbose = v; +} + +int +log_getverbose(void) +{ + return (verbose); +} + +void +logit(int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(pri, fmt, ap); + va_end(ap); +} + +void +vlog(int pri, const char *fmt, va_list ap) +{ + char *nfmt; + int saved_errno = errno; + + if (debug) { + /* best effort in out of mem situations */ + if (asprintf(&nfmt, "%s\n", fmt) == -1) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else { + vfprintf(stderr, nfmt, ap); + free(nfmt); + } + fflush(stderr); + } else + vsyslog(pri, fmt, ap); + + errno = saved_errno; +} + +void +log_warn(const char *emsg, ...) +{ + char *nfmt; + va_list ap; + int saved_errno = errno; + + /* best effort to even work in out of memory situations */ + if (emsg == NULL) + logit(LOG_ERR, "%s", strerror(saved_errno)); + else { + va_start(ap, emsg); + + if (asprintf(&nfmt, "%s: %s", emsg, + strerror(saved_errno)) == -1) { + /* we tried it... */ + vlog(LOG_ERR, emsg, ap); + logit(LOG_ERR, "%s", strerror(saved_errno)); + } else { + vlog(LOG_ERR, nfmt, ap); + free(nfmt); + } + va_end(ap); + } + + errno = saved_errno; +} + +void +log_warnx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_ERR, emsg, ap); + va_end(ap); +} + +void +log_info(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_INFO, emsg, ap); + va_end(ap); +} + +void +log_debug(const char *emsg, ...) +{ + va_list ap; + + if (verbose > 1) { + va_start(ap, emsg); + vlog(LOG_DEBUG, emsg, ap); + va_end(ap); + } +} + +static void +vfatalc(int code, const char *emsg, va_list ap) +{ + static char s[BUFSIZ]; + const char *sep; + + if (emsg != NULL) { + (void)vsnprintf(s, sizeof(s), emsg, ap); + sep = ": "; + } else { + s[0] = '\0'; + sep = ""; + } + if (code) + logit(LOG_CRIT, "%s: %s%s%s", + log_procname, s, sep, strerror(code)); + else + logit(LOG_CRIT, "%s%s%s", log_procname, sep, s); +} + +void +fatal(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vfatalc(errno, emsg, ap); + va_end(ap); + exit(1); +} + +void +fatalx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vfatalc(0, emsg, ap); + va_end(ap); + exit(1); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..a02fe82 --- /dev/null +++ b/log.h @@ -0,0 +1,40 @@ +/* $OpenBSD: log.h,v 1.1.1.1 2022/09/01 14:20:34 martijn Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * 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 + +void log_init(int, int); +void log_procinit(const char *); +void log_setverbose(int); +int log_getverbose(void); +void log_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void logit(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void vlog(int, const char *, va_list) + __attribute__((__format__ (printf, 2, 0))); +__dead void fatal(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +__dead void fatalx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); diff --git a/mib.c b/mib.c new file mode 100644 index 0000000..6904aa0 --- /dev/null +++ b/mib.c @@ -0,0 +1,327 @@ +/* $OpenBSD: mib.c,v 1.9 2024/05/22 08:44:02 martijn Exp $ */ + +/* + * Copyright (c) 2022 Martijn van Duren + * Copyright (c) 2012 Joel Knight + * Copyright (c) 2007, 2008, 2012 Reyk Floeter + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "snmpd.h" +#include "mib.h" + +struct event connev; +const char *agentxsocket = NULL; +int agentxfd = -1; + +int pageshift; +#define pagetok(size) ((size) << pageshift) + +void pageshift_init(void); +void snmp_connect(struct agentx *, void *, int); +void snmp_tryconnect(int, short, void *); +void snmp_read(int, short, void *); + +struct agentx_context *sac; +struct snmpd *snmpd_env; + + +void mib_hrsystemuptime(struct agentx_varbind *); +void mib_hottisntpdcounter(struct agentx_varbind *); + + +void +mib_hrsystemuptime(struct agentx_varbind *vb) +{ + struct timespec uptime; + long long ticks; + + if (clock_gettime(CLOCK_BOOTTIME, &uptime) == -1) { + log_warn("clock_gettime"); + agentx_varbind_error(vb); + return; + } + ticks = uptime.tv_sec * 100 + uptime.tv_nsec / 10000000; + agentx_varbind_timeticks(vb, ticks); +} + +void +mib_hottisntpdcounter(struct agentx_varbind *vb) +{ + long long ticks = 200169; + + agentx_varbind_counter64(vb, ticks); +} + + +int +main(int argc, char *argv[]) +{ + static struct snmpd conf; + struct agentx *sa; + struct agentx_session *sas; + struct passwd *pw; + struct group *gr; + char agentxsocketdir[PATH_MAX]; + int ch; + int verbose = 0, daemonize = 1, debug = 0; + char *context = NULL; + const char *errstr; + /* HOST-RESOURCES-MIB */ + struct agentx_region *host; + struct agentx_object *hrSystemUptime; + + struct agentx_region *hottis; + struct agentx_object *hottisNtpdCounter; + + snmpd_env = &conf; + log_init(2, LOG_DAEMON); + + agentx_log_fatal = fatalx; + agentx_log_warn = log_warnx; + agentx_log_info = log_info; + agentx_log_debug = log_debug; + + while ((ch = getopt(argc, argv, "C:c:ds:vx:")) != -1) { + switch (ch) { + case 'C': + if (strcmp(optarg, "filter-routes") == 0) { + conf.sc_rtfilter = ROUTE_FILTER(RTM_NEWADDR) | + ROUTE_FILTER(RTM_DELADDR) | + ROUTE_FILTER(RTM_IFINFO) | + ROUTE_FILTER(RTM_IFANNOUNCE); + + } + break; + case 'c': + context = optarg; + break; + case 'd': + daemonize = 0; + debug = 1; + break; + case 's': + if (optarg[0] != '/') + fatalx("agentx socket path must be absolute"); + agentxsocket = optarg; + break; + case 'v': + verbose++; + break; + case 'x': + /* Undocumented flag for snmpd(8) spawning */ + agentxfd = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + fatalx("invalid agentx fd: %s", errstr); + daemonize = 0; + break; + default: + fatalx("usage: snmpd_metrics [-dv] [-C option] " + "[-c context] [-s master]\n"); + } + } + + if (agentxfd != -1 && !debug) + /* Initialize syslog logging asap for snmpd */ + log_init(0, LOG_DAEMON); + + if ((pw = getpwnam("_snmpd")) == NULL) + fatal("can't find _snmpd user"); + if ((gr = getgrnam("_agentx")) == NULL) + fatal("can't find _agentx group"); + + if (agentxfd != -1 && agentxsocket != NULL) + fatalx("-s and -x are mutually exclusive"); + if (agentxfd == -1 && agentxsocket == NULL) + agentxsocket = AGENTX_MASTER_PATH; + + event_init(); + + if ((sa = agentx(snmp_connect, NULL)) == NULL) + fatal("agentx"); + if ((sas = agentx_session(sa, NULL, 0, "OpenSNMPd metrics", 0)) == NULL) + fatal("agentx_session"); + if ((sac = agentx_context(sas, context)) == NULL) + fatal("agentx_context"); + + /* kr_init requires sac */ + pageshift_init(); + + if (agentxsocket != NULL) { + if (strlcpy(agentxsocketdir, agentxsocket, + sizeof(agentxsocketdir)) >= sizeof(agentxsocketdir)) { + errno = ENAMETOOLONG; + fatal("-s"); + } + if (unveil(dirname(agentxsocketdir), "r") == -1) + fatal("unveil"); + } + + /* Can't pledge: kvm_getfiles */ + if (unveil(NULL, NULL) == -1) + fatal("unveil"); + + if (setgid(gr->gr_gid) == -1) + fatal("setgid"); + if (setuid(pw->pw_uid) == -1) + fatal("setuid"); + + /* HOST-RESOURCES-MIB */ + if ((host = agentx_region(sac, AGENTX_OID(HOST), 0)) == NULL) + fatal("agentx_region"); + + if ((hrSystemUptime = agentx_object(host, AGENTX_OID(HRSYSTEMUPTIME), + NULL, 0, 0, mib_hrsystemuptime)) == NULL) + fatal("agentx_object"); + + if ((hottis = agentx_region(sac, AGENTX_OID(HOTTIS_MIB), 0)) == NULL) + fatal("agentx_region"); + + if ((hottisNtpdCounter = agentx_object(hottis, AGENTX_OID(HOTTIS_NTPD_COUNTER), + NULL, 0, 0, mib_hottisntpdcounter)) == NULL) + fatal("agentx_object"); + + + if (daemonize) { + log_init(0, LOG_DAEMON); + daemon(0, 0); + } + log_setverbose(verbose); + + event_dispatch(); +} + +#define LOG1024 10 +void +pageshift_init(void) +{ + long pagesize; + + if ((pagesize = sysconf(_SC_PAGESIZE)) == -1) + fatal("sysconf(_SC_PAGESIZE)"); + while (pagesize > 1) { + pageshift++; + pagesize >>= 1; + } + /* we only need the amount of log(2)1024 for our conversion */ + pageshift -= LOG1024; +} + +void +snmp_connect(struct agentx *sa, void *cookie, int close) +{ + static int init = 0; + + if (close) { + event_del(&connev); + return; + } + + if (agentxfd != -1) { + /* Exit if snmpd(8) leaves */ + if (init) + exit(0); + agentx_connect(sa, agentxfd); + event_set(&connev, agentxfd, EV_READ | EV_PERSIST, + snmp_read, sa); + event_add(&connev, NULL); + init = 1; + } else + snmp_tryconnect(-1, 0, sa); +} + +void +snmp_tryconnect(int fd, short event, void *cookie) +{ + struct timeval timeout = {3, 0}; + struct agentx *sa = cookie; + struct sockaddr_un sun; + + sun.sun_len = sizeof(sun); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, AGENTX_MASTER_PATH, sizeof(sun.sun_path)); + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 || + connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + if (fd != -1) + close(fd); + log_warn("Failed to connect to snmpd"); + evtimer_set(&connev, snmp_tryconnect, sa); + evtimer_add(&connev, &timeout); + return; + } + + event_set(&connev, fd, EV_READ | EV_PERSIST, snmp_read, sa); + event_add(&connev, NULL); + + agentx_connect(sa, fd); +} + +void +snmp_read(int fd, short event, void *cookie) +{ + struct agentx *sa = cookie; + + agentx_read(sa); +} + +u_long +smi_getticks(void) +{ + return agentx_context_uptime(sac); +} diff --git a/mib.h b/mib.h new file mode 100644 index 0000000..40f092e --- /dev/null +++ b/mib.h @@ -0,0 +1,27 @@ +/* $OpenBSD: mib.h,v 1.1.1.1 2022/09/01 14:20:34 martijn Exp $ */ + +/* + * Copyright (c) 2022 Martijn van Duren + * + * 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 + +#define HOST AGENTX_MIB2, 253 +#define HRSYSTEM HOST, 1 +#define HRSYSTEMUPTIME HRSYSTEM, 1 + +#define HOTTIS_MIB 1,3,6,1,4,1,9676 +#define HOTTIS_NTPD HOTTIS_MIB, 1 +#define HOTTIS_NTPD_COUNTER HOTTIS_NTPD, 1 diff --git a/snmpd.h b/snmpd.h new file mode 100644 index 0000000..6cde727 --- /dev/null +++ b/snmpd.h @@ -0,0 +1,119 @@ +/* $OpenBSD: snmpd.h,v 1.1.1.1 2022/09/01 14:20:33 martijn Exp $ */ + +/* + * Copyright (c) 2007, 2008, 2012 Reyk Floeter + * Copyright (c) 2003, 2004 Henning Brauer + * + * 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. + */ + +#ifndef SNMPD_H +#define SNMPD_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "log.h" + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +/* + * common definitions for snmpd + */ + +#define CONF_FILE "/etc/snmpd.conf" +#define SNMPD_SOCKET "/var/run/snmpd.sock" +#define SNMPD_USER "_snmpd" +#define SNMP_PORT "161" +#define SNMPTRAP_PORT "162" + +#define SNMPD_MAXSTRLEN 484 +#define SNMPD_MAXCOMMUNITYLEN SNMPD_MAXSTRLEN +#define SNMPD_MAXVARBIND 0x7fffffff +#define SNMPD_MAXVARBINDLEN 1210 +#define SNMPD_MAXENGINEIDLEN 32 +#define SNMPD_MAXUSERNAMELEN 32 +#define SNMPD_MAXCONTEXNAMELEN 32 + +#define SNMP_USM_MAXDIGESTLEN 48 +#define SNMP_USM_SALTLEN 8 +#define SNMP_USM_KEYLEN 64 +#define SNMP_CIPHER_KEYLEN 16 + +#define SMALL_READ_BUF_SIZE 1024 +#define READ_BUF_SIZE 65535 +#define RT_BUF_SIZE 16384 +#define MAX_RTSOCK_BUF (2 * 1024 * 1024) + +#define SNMP_ENGINEID_OLD 0x00 +#define SNMP_ENGINEID_NEW 0x80 /* RFC3411 */ + +#define SNMP_ENGINEID_FMT_IPv4 1 +#define SNMP_ENGINEID_FMT_IPv6 2 +#define SNMP_ENGINEID_FMT_MAC 3 +#define SNMP_ENGINEID_FMT_TEXT 4 +#define SNMP_ENGINEID_FMT_OCT 5 +#define SNMP_ENGINEID_FMT_HH 129 + +#define PEN_OPENBSD 30155 + +#if DEBUG +#define DPRINTF log_debug +#else +#define DPRINTF(x...) do {} while(0) +#endif + +/* + * daemon structures + */ + +struct snmpd { + int sc_ncpu; + int64_t *sc_cpustates; + int sc_rtfilter; +}; + +extern struct snmpd *snmpd_env; + +/* mib.c */ +u_long smi_getticks(void); + + +/* timer.c */ +void timer_init(void); + +/* util.c */ +ssize_t sendtofrom(int, void *, size_t, int, struct sockaddr *, + socklen_t, struct sockaddr *, socklen_t); +ssize_t recvfromto(int, void *, size_t, int, struct sockaddr *, + socklen_t *, struct sockaddr *, socklen_t *); +const char *log_in6addr(const struct in6_addr *); +const char *print_host(struct sockaddr_storage *, char *, size_t); +char *tohexstr(u_int8_t *, int); +uint8_t *fromhexstr(uint8_t *, const char *, size_t); + +#endif /* SNMPD_H */ diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..e0a70e9 --- /dev/null +++ b/timer.c @@ -0,0 +1,169 @@ +/* $OpenBSD: timer.c,v 1.1.1.1 2022/09/01 14:20:33 martijn Exp $ */ + +/* + * Copyright (c) 2008 Reyk Floeter + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snmpd.h" +#include "mib.h" + +void timer_cpu(int, short, void *); +int percentages(int, int64_t *, int64_t *, int64_t *, int64_t *); + +static int64_t **cp_time; +static int64_t **cp_old; +static int64_t **cp_diff; +struct event cpu_ev; + +void +timer_cpu(int fd, short event, void *arg) +{ + struct event *ev = (struct event *)arg; + struct timeval tv = { 60, 0 }; /* every 60 seconds */ + int mib[3] = { CTL_KERN, KERN_CPTIME2, 0 }, n; + size_t len; + int64_t *cptime2; + + len = CPUSTATES * sizeof(int64_t); + for (n = 0; n < snmpd_env->sc_ncpu; n++) { + mib[2] = n; + cptime2 = snmpd_env->sc_cpustates + (CPUSTATES * n); + if (sysctl(mib, 3, cp_time[n], &len, NULL, 0) == -1) + continue; + (void)percentages(CPUSTATES, cptime2, cp_time[n], + cp_old[n], cp_diff[n]); +#ifdef DEBUG + log_debug("timer_cpu: cpu%d %lld%% idle in %llds", n, + (cptime2[CP_IDLE] > 1000 ? + 1000 : (cptime2[CP_IDLE] / 10)), (long long) tv.tv_sec); +#endif + } + + evtimer_add(ev, &tv); +} + +void +timer_init(void) +{ + int mib[] = { CTL_HW, HW_NCPU }, i; + size_t len; + + len = sizeof(snmpd_env->sc_ncpu); + if (sysctl(mib, 2, &snmpd_env->sc_ncpu, &len, NULL, 0) == -1) + fatal("sysctl"); + + snmpd_env->sc_cpustates = calloc(snmpd_env->sc_ncpu, + CPUSTATES * sizeof(int64_t)); + cp_time = calloc(snmpd_env->sc_ncpu, sizeof(int64_t *)); + cp_old = calloc(snmpd_env->sc_ncpu, sizeof(int64_t *)); + cp_diff = calloc(snmpd_env->sc_ncpu, sizeof(int64_t *)); + if (snmpd_env->sc_cpustates == NULL || + cp_time == NULL || cp_old == NULL || cp_diff == NULL) + fatal("calloc"); + for (i = 0; i < snmpd_env->sc_ncpu; i++) { + cp_time[i] = calloc(CPUSTATES, sizeof(int64_t)); + cp_old[i] = calloc(CPUSTATES, sizeof(int64_t)); + cp_diff[i] = calloc(CPUSTATES, sizeof(int64_t)); + if (cp_time[i] == NULL || cp_old[i] == NULL || + cp_diff[i] == NULL) + fatal("calloc"); + } + + evtimer_set(&cpu_ev, timer_cpu, &cpu_ev); + timer_cpu(0, EV_TIMEOUT, &cpu_ev); +} + +/* + * percentages() function to calculate CPU utilization. + * Source code derived from the top(1) utility: + * + * Copyright (c) 1984, 1989, William LeFebvre, Rice University + * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +int +percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_t *diffs) +{ + int64_t change, total_change, *dp, half_total; + int i; + + /* initialization */ + total_change = 0; + dp = diffs; + + /* calculate changes for each state and the overall change */ + for (i = 0; i < cnt; i++) { + if ((change = *new - *old) < 0) { + /* this only happens when the counter wraps */ + change = (*new - *old); + } + total_change += (*dp++ = change); + *old++ = *new++; + } + + /* avoid divide by zero potential */ + if (total_change == 0) + total_change = 1; + + /* calculate percentages based on overall change, rounding up */ + half_total = total_change / 2l; + for (i = 0; i < cnt; i++) + *out++ = ((*diffs++ * 1000 + half_total) / total_change); + + /* return the total in case the caller wants to use it */ + return (total_change); +} diff --git a/util.c b/util.c new file mode 100644 index 0000000..bfb55b4 --- /dev/null +++ b/util.c @@ -0,0 +1,229 @@ +/* $OpenBSD: util.c,v 1.1.1.1 2022/09/01 14:20:33 martijn Exp $ */ +/* + * Copyright (c) 2014 Bret Stephen Lambert + * + * 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 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "snmpd.h" + +ssize_t +sendtofrom(int s, void *buf, size_t len, int flags, struct sockaddr *to, + socklen_t tolen, struct sockaddr *from, socklen_t fromlen) +{ + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct in6_pktinfo *pkt6; + struct sockaddr_in *in; + struct sockaddr_in6 *in6; + union { + struct cmsghdr hdr; + char inbuf[CMSG_SPACE(sizeof(struct in_addr))]; + char in6buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cmsgbuf; + + bzero(&msg, sizeof(msg)); + bzero(&cmsgbuf, sizeof(cmsgbuf)); + + iov.iov_base = buf; + iov.iov_len = len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = to; + msg.msg_namelen = tolen; + msg.msg_control = &cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + switch (to->sa_family) { + case AF_INET: + msg.msg_controllen = sizeof(cmsgbuf.inbuf); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + in = (struct sockaddr_in *)from; + memcpy(CMSG_DATA(cmsg), &in->sin_addr, sizeof(struct in_addr)); + break; + case AF_INET6: + msg.msg_controllen = sizeof(cmsgbuf.in6buf); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + in6 = (struct sockaddr_in6 *)from; + pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); + pkt6->ipi6_addr = in6->sin6_addr; + break; + } + + return sendmsg(s, &msg, flags); +} + +ssize_t +recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from, + socklen_t *fromlen, struct sockaddr *to, socklen_t *tolen) +{ + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct in6_pktinfo *pkt6; + struct sockaddr_in *in; + struct sockaddr_in6 *in6; + ssize_t ret; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))]; + } cmsgbuf; + + bzero(&msg, sizeof(msg)); + bzero(&cmsgbuf.buf, sizeof(cmsgbuf.buf)); + + iov.iov_base = buf; + iov.iov_len = len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = from; + msg.msg_namelen = *fromlen; + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + + if ((ret = recvmsg(s, &msg, flags)) == -1) + return (-1); + + *fromlen = from->sa_len; + *tolen = 0; + + if (getsockname(s, to, tolen) != 0) + *tolen = 0; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + switch (from->sa_family) { + case AF_INET: + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVDSTADDR) { + in = (struct sockaddr_in *)to; + in->sin_family = AF_INET; + in->sin_len = *tolen = sizeof(*in); + memcpy(&in->sin_addr, CMSG_DATA(cmsg), + sizeof(struct in_addr)); + } + break; + case AF_INET6: + if (cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_PKTINFO) { + in6 = (struct sockaddr_in6 *)to; + in6->sin6_family = AF_INET6; + in6->sin6_len = *tolen = sizeof(*in6); + pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); + memcpy(&in6->sin6_addr, &pkt6->ipi6_addr, + sizeof(struct in6_addr)); + if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) + in6->sin6_scope_id = + pkt6->ipi6_ifindex; + } + break; + } + } + + return (ret); +} + +const char * +log_in6addr(const struct in6_addr *addr) +{ + static char buf[NI_MAXHOST]; + struct sockaddr_in6 sa_in6; + u_int16_t tmp16; + + bzero(&sa_in6, sizeof(sa_in6)); + sa_in6.sin6_len = sizeof(sa_in6); + sa_in6.sin6_family = AF_INET6; + memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr)); + + /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */ + if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) { + memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16)); + sa_in6.sin6_scope_id = ntohs(tmp16); + sa_in6.sin6_addr.s6_addr[2] = 0; + sa_in6.sin6_addr.s6_addr[3] = 0; + } + + return (print_host((struct sockaddr_storage *)&sa_in6, buf, + NI_MAXHOST)); +} + +const char * +print_host(struct sockaddr_storage *ss, char *buf, size_t len) +{ + if (getnameinfo((struct sockaddr *)ss, ss->ss_len, + buf, len, NULL, 0, NI_NUMERICHOST) != 0) { + buf[0] = '\0'; + return (NULL); + } + return (buf); +} + +char * +tohexstr(uint8_t *bstr, int len) +{ +#define MAXHEXSTRLEN 256 + static char hstr[2 * MAXHEXSTRLEN + 1]; + static const char hex[] = "0123456789abcdef"; + int i; + + if (len > MAXHEXSTRLEN) + len = MAXHEXSTRLEN; /* truncate */ + for (i = 0; i < len; i++) { + hstr[i + i] = hex[bstr[i] >> 4]; + hstr[i + i + 1] = hex[bstr[i] & 0x0f]; + } + hstr[i + i] = '\0'; + return hstr; +} + +uint8_t * +fromhexstr(uint8_t *bstr, const char *hstr, size_t len) +{ + size_t i; + char hex[3]; + + if (len % 2 != 0) + return NULL; + + hex[2] = '\0'; + for (i = 0; i < len; i += 2) { + if (!isxdigit(hstr[i]) || !isxdigit(hstr[i + 1])) + return NULL; + hex[0] = hstr[i]; + hex[1] = hstr[i + 1]; + bstr[i / 2] = strtol(hex, NULL, 16); + } + + return bstr; +}