/* $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); }