sbm

simple bandwidth monitor
git clone git://git.2f30.org/sbm.git
Log | Files | Refs | LICENSE

commit fece505991314d54fa4ed5279e8057940a70c0ce
Author: sin <sin@2f30.org>
Date:   Thu Feb 11 17:30:45 +0000

Initial commit

Diffstat:
Makefile | 29+++++++++++++++++++++++++++++
arg.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbm.1 | 23+++++++++++++++++++++++
sbm.c | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 302 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile @@ -0,0 +1,29 @@ +VERSION = 0.1 + +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/man + +CFLAGS = -Wall + +OBJ = sbm.o +BIN = sbm + +all: $(BIN) + +$(BIN): $(OBJ) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(OBJ) $(LDFLAGS) + +sbm.o: arg.h + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + cp -f $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) + rm -f $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1 + +clean: + rm -f $(BIN) $(OBJ) diff --git a/arg.h b/arg.h @@ -0,0 +1,65 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + +#endif diff --git a/sbm.1 b/sbm.1 @@ -0,0 +1,23 @@ +.Dd Feb 11, 2016 +.Dt SBM 1 +.Os +.Sh NAME +.Nm sbm +.Nd simple bandwidth monitor +.Sh SYNOPSIS +.Nm sbm +.Op Fl i Ar interface +.Op Fl d Ar delay +.Sh DESCRIPTION +.Nm +is a simple bandwidth monitor for OpenBSD. +.Sh OPTIONS +.Bl -tag -width "-i interface" +.It Fl i Ar interface +Monitor the selected interface. If not provided, the first +enumerated interface will be used. +.It Fl d Ar delay +Specify the sampling delay in milliseconds. Defaults to 1 second. +.El +.Sh AUTHORS +.An Dimitris Papastamos Aq Mt sin@2f30.org diff --git a/sbm.c b/sbm.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2016 Dimitris Papastamos <sin@2f30.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> + +#include <err.h> +#include <ifaddrs.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "arg.h" + +char *argv0; + +void +scan(char *ifname) +{ + struct ifaddrs *ifas, *ifa; + int found = 0; + + if (getifaddrs(&ifas) == -1) + err(1, "getifaddrs"); + if (*ifname) { + for (ifa = ifas; ifa; ifa = ifa->ifa_next) { + if (!strncmp(ifname, ifa->ifa_name, IFNAMSIZ)) { + found = 1; + break; + } + } + } else { + for (ifa = ifas; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_flags & IFF_LOOPBACK) + continue; + if (ifa->ifa_flags & IFF_RUNNING) { + if (ifa->ifa_flags & IFF_UP) { + strlcpy(ifname, ifa->ifa_name, IFNAMSIZ); + found = 1; + break; + } + } + } + } + freeifaddrs(ifas); + if (!found) + errx(1, "no usable interface found"); +} + +void +sample(char *ifname, unsigned long long *rx, unsigned long long *tx) +{ + int mib[6]; + char *buf, *next; + size_t size; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl = NULL; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; + if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) + errx(1, "sysctl failed"); + buf = malloc(size); + if (!buf) + err(1, "malloc"); + if (sysctl(mib, 6, buf, &size, NULL, 0) < 0) + errx(1, "sysctl failed"); + for (next = buf; next < buf + size; next += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)next; + if (ifm->ifm_type != RTM_NEWADDR) { + if (ifm->ifm_flags & IFF_UP) { + sdl = (struct sockaddr_dl *)(ifm + 1); + if (sdl->sdl_family != AF_LINK) + continue; + if (strncmp(sdl->sdl_data, ifname, sdl->sdl_nlen) != 0) + continue; + *rx = ifm->ifm_data.ifi_ibytes; + *tx = ifm->ifm_data.ifi_obytes; + break; + } + } + } + free(buf); + if (!sdl) + errx(1, "interface %s cannot be sampled", ifname); +} + +void +scale(char **suffix, unsigned long long *bits) +{ + static char *suffixes[] = { "bps", "Kbps", "Mbps" }; + + *suffix = suffixes[0]; + if (*bits >= 1000 && *bits < 1000 * 1000) { + *suffix = suffixes[1]; + *bits /= 1000; + } else if (*bits >= 1000 * 1000) { + *suffix = suffixes[2]; + *bits /= 1000 * 1000; + } +} + +void +print(char *ifname, unsigned long long rxbits, unsigned long long txbits) +{ + char *rxsuffix, *txsuffix; + + scale(&rxsuffix, &rxbits); + scale(&txsuffix, &txbits); + printf("%s: down %lld %s, up %lld %s\n", + ifname, rxbits, rxsuffix, txbits, txsuffix); +} + +void +loop(char *ifname, long long delay) +{ + unsigned long long oldrxbytes, rxbytes; + unsigned long long oldtxbytes, txbytes; + + sample(ifname, &oldrxbytes, &oldtxbytes); + for (;;) { + sample(ifname, &rxbytes, &txbytes); + print(ifname, + (rxbytes - oldrxbytes) * 8, + (txbytes - oldtxbytes) * 8); + oldrxbytes = rxbytes; + oldtxbytes = txbytes; + usleep(delay * 1000); + } +} + +void +usage(void) +{ + fprintf(stderr, "usage: %s [-i interface] [-d delay]\n", argv0); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + char ifname[IFNAMSIZ] = "", *arg; + const char *errstr; + long long delay = 1000; + + ARGBEGIN { + case 'i': + strlcpy(ifname, EARGF(usage()), sizeof(ifname)); + break; + case 'd': + arg = EARGF(usage()); + delay = strtonum(arg, 100, 60000, &errstr); + if (errstr) + errx(1, "%s: %s", arg, errstr); + break; + default: + usage(); + } ARGEND + + scan(ifname); + loop(ifname, delay); + return 0; +}