commit f160c0f4e3e0c4ba49d2f5d13e18b17eab277007 Author: Wolfgang Hottgenroth Date: Fri Feb 14 17:39:57 2025 +0100 initial 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; +}