sbm.c (7707B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <sys/types.h> 3 #include <sys/time.h> 4 #include <sys/socket.h> 5 6 #include <net/if.h> 7 #ifndef __linux__ 8 #include <sys/sysctl.h> 9 #include <net/if_dl.h> 10 #include <net/route.h> 11 #endif 12 13 #include <err.h> 14 #include <errno.h> 15 #include <ifaddrs.h> 16 #include <limits.h> 17 #include <signal.h> 18 #include <stdint.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <time.h> 23 #include <unistd.h> 24 25 #ifdef __linux__ 26 #define SIGINFO SIGUSR1 27 #endif 28 29 #include "arg.h" 30 31 char *argv0; 32 char *ifname; 33 uint64_t origrxbytes, origtxbytes, rxbytes, txbytes; 34 struct timeval begin; 35 volatile sig_atomic_t interrupted; 36 int Bflag; 37 int tflag; 38 39 void 40 sighdlr(int signo) 41 { 42 (void)signo; 43 interrupted = 1; 44 } 45 46 void 47 scan(void) 48 { 49 struct ifaddrs *ifas, *ifa; 50 int found = 0; 51 52 if (getifaddrs(&ifas) < 0) 53 err(1, "getifaddrs"); 54 if (*ifname) { 55 for (ifa = ifas; ifa; ifa = ifa->ifa_next) { 56 if (!strncmp(ifname, ifa->ifa_name, IF_NAMESIZE)) { 57 found = 1; 58 break; 59 } 60 } 61 } else { 62 for (ifa = ifas; ifa; ifa = ifa->ifa_next) { 63 if (ifa->ifa_flags & IFF_LOOPBACK || 64 !(ifa->ifa_flags & IFF_UP) || 65 !(ifa->ifa_flags & IFF_RUNNING)) 66 continue; 67 strncpy(ifname, ifa->ifa_name, IF_NAMESIZE); 68 ifname[IF_NAMESIZE - 1] = '\0'; 69 found = 1; 70 break; 71 } 72 } 73 freeifaddrs(ifas); 74 if (!found) 75 errx(1, "no usable interface found"); 76 } 77 78 #ifdef __linux__ 79 int 80 readcounter(char *filename, uint64_t *v) 81 { 82 char path[PATH_MAX]; 83 char buf[BUFSIZ]; 84 char *endptr; 85 FILE *fp; 86 87 snprintf(path, sizeof(path), 88 "/sys/class/net/%s/statistics/%s", 89 ifname, filename); 90 fp = fopen(path, "r"); 91 if (fp == NULL) { 92 *v = 0; 93 return -1; 94 } 95 if (fgets(buf, sizeof(buf), fp) == NULL) { 96 fclose(fp); 97 *v = 0; 98 return -1; 99 } 100 fclose(fp); 101 buf[strcspn(buf, "\n")] = '\0'; 102 errno = 0; 103 *v = strtoull(buf, &endptr, 10); 104 if (*endptr != '\0' || errno != 0) { 105 *v = 0; 106 return -1; 107 } 108 return 0; 109 } 110 111 void 112 sample(uint64_t *rxbytes, uint64_t *txbytes, 113 uint64_t *rxpps, uint64_t *txpps) 114 { 115 readcounter("rx_bytes", rxbytes); 116 readcounter("tx_bytes", txbytes); 117 readcounter("rx_packets", rxpps); 118 readcounter("tx_packets", txpps); 119 } 120 #else 121 void 122 sample(uint64_t *rxbytes, uint64_t *txbytes, 123 uint64_t *rxpps, uint64_t *txpps) 124 { 125 int mib[6]; 126 char *buf, *next; 127 size_t size; 128 struct if_msghdr *ifm; 129 struct sockaddr_dl *sdl = NULL; 130 131 *rxbytes = *txbytes = *rxpps = *txpps = 0; 132 mib[0] = CTL_NET; 133 mib[1] = PF_ROUTE; 134 mib[2] = 0; 135 mib[3] = 0; 136 mib[4] = NET_RT_IFLIST; 137 mib[5] = 0; 138 if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) 139 errx(1, "sysctl failed"); 140 buf = malloc(size); 141 if (!buf) 142 err(1, "malloc"); 143 if (sysctl(mib, 6, buf, &size, NULL, 0) < 0) 144 errx(1, "sysctl failed"); 145 for (next = buf; next < buf + size; next += ifm->ifm_msglen) { 146 ifm = (struct if_msghdr *)next; 147 if (ifm->ifm_type == RTM_NEWADDR || 148 !(ifm->ifm_flags & IFF_UP)) 149 continue; 150 sdl = (struct sockaddr_dl *)(ifm + 1); 151 if (sdl->sdl_family != AF_LINK || 152 strncmp(sdl->sdl_data, ifname, sdl->sdl_nlen)) 153 continue; 154 *rxbytes = ifm->ifm_data.ifi_ibytes; 155 *txbytes = ifm->ifm_data.ifi_obytes; 156 *rxpps = ifm->ifm_data.ifi_ipackets; 157 *txpps = ifm->ifm_data.ifi_opackets; 158 break; 159 } 160 free(buf); 161 if (!sdl) 162 errx(1, "interface %s cannot be sampled", ifname); 163 } 164 #endif 165 166 int 167 getmonotime(struct timeval *tv) 168 { 169 struct timespec ts; 170 int ret; 171 172 ret = clock_gettime(CLOCK_MONOTONIC, &ts); 173 if (ret < 0) 174 return -1; 175 tv->tv_sec = ts.tv_sec; 176 tv->tv_usec = ts.tv_nsec / 1000; 177 return ret; 178 } 179 180 void 181 ms2tv(struct timeval *tv, long ms) 182 { 183 tv->tv_sec = ms / 1000; 184 tv->tv_usec = (ms % 1000) * 1000; 185 } 186 187 long 188 tv2ms(struct timeval *tv) 189 { 190 return (tv->tv_sec * 1000) + ((tv->tv_usec + 500) / 1000); 191 } 192 193 int 194 tvsleep(struct timeval *tv) 195 { 196 struct timespec ts; 197 198 ts.tv_sec = tv->tv_sec; 199 ts.tv_nsec = tv->tv_usec * 1000; 200 return nanosleep(&ts, NULL); 201 } 202 203 double 204 scale(char **suffix, uint64_t bits) 205 { 206 static char *suffixes[][5] = { { "b", "kb", "Mb", "Gb", "Tb" }, 207 { "B", "kB", "MB", "GB", "TB" } }; 208 double rounded = bits; 209 int unit; 210 211 if (Bflag) 212 rounded /= 8; 213 for (unit = 0; rounded >= 1000 && unit < 4; ++unit) 214 rounded /= 1000; 215 *suffix = suffixes[Bflag][unit]; 216 return rounded; 217 } 218 219 void 220 print(uint64_t rxbits, uint64_t txbits, uint64_t rxpps, uint64_t txpps) 221 { 222 char *rxsuffix, *txsuffix; 223 double rxround, txround; 224 225 if (!tflag) { 226 rxround = scale(&rxsuffix, rxbits); 227 txround = scale(&txsuffix, txbits); 228 printf("%s: %6.2f %2sps Rx %6.2f %2sps Tx %5llu pps Rx %5llu pps Tx\n", 229 ifname, 230 rxround, 231 rxsuffix, 232 txround, 233 txsuffix, 234 (unsigned long long)rxpps, 235 (unsigned long long)txpps); 236 } else { 237 printf("%llu %llu %llu %llu\n", 238 (unsigned long long)rxbits, 239 (unsigned long long)txbits, 240 (unsigned long long)rxpps, 241 (unsigned long long)txpps); 242 } 243 fflush(stdout); 244 } 245 246 void 247 printrxtx(void) 248 { 249 struct timeval now, uptime; 250 char *rxsuffix, *txsuffix; 251 double rxround, txround; 252 uint64_t rxbits, txbits; 253 unsigned int days, hours, minutes, seconds; 254 255 getmonotime(&now); 256 timersub(&now, &begin, &uptime); 257 seconds = uptime.tv_sec % 60; 258 uptime.tv_sec /= 60; 259 minutes = uptime.tv_sec % 60; 260 uptime.tv_sec /= 60; 261 hours = uptime.tv_sec % 24; 262 days = uptime.tv_sec / 24; 263 264 rxbits = (rxbytes - origrxbytes) * 8; 265 txbits = (txbytes - origtxbytes) * 8; 266 rxround = scale(&rxsuffix, rxbits); 267 txround = scale(&txsuffix, txbits); 268 269 printf("%*s %6.2f %2s Rx %6.2f %2s Tx in ", 270 (int)strlen(ifname) + 1, "", 271 rxround, rxsuffix, txround, txsuffix); 272 if (days != 0) 273 printf("%dd ", days); 274 if (hours != 0) 275 printf("%dh ", hours); 276 if (minutes != 0) 277 printf("%dm ", minutes); 278 if (seconds != 0) 279 printf("%ds", seconds); 280 putchar('\n'); 281 } 282 283 void 284 loop(long count, struct timeval *delay) 285 { 286 struct timeval old, now, diff; 287 uint64_t oldrxbytes, diffrxbits; 288 uint64_t oldtxbytes, difftxbits; 289 uint64_t oldrxpps, rxpps, diffrxpps; 290 uint64_t oldtxpps, txpps, difftxpps; 291 long n = 0; 292 293 getmonotime(&old); 294 sample(&oldrxbytes, &oldtxbytes, &oldrxpps, &oldtxpps); 295 origrxbytes = oldrxbytes, origtxbytes = oldtxbytes; 296 for (;;) { 297 tvsleep(delay); 298 if (interrupted) { 299 printrxtx(); 300 interrupted = 0; 301 } 302 sample(&rxbytes, &txbytes, &rxpps, &txpps); 303 getmonotime(&now); 304 timersub(&now, &old, &diff); 305 getmonotime(&old); 306 diffrxbits = (rxbytes - oldrxbytes) * 8; 307 difftxbits = (txbytes - oldtxbytes) * 8; 308 diffrxbits = diffrxbits * 1000 / tv2ms(&diff); 309 difftxbits = difftxbits * 1000 / tv2ms(&diff); 310 diffrxpps = (rxpps - oldrxpps) * 1000 / tv2ms(&diff); 311 difftxpps = (txpps - oldtxpps) * 1000 / tv2ms(&diff); 312 print(diffrxbits, difftxbits, diffrxpps, difftxpps); 313 oldrxbytes = rxbytes, oldtxbytes = txbytes; 314 oldrxpps = rxpps, oldtxpps = txpps; 315 if (count && ++n >= count) 316 break; 317 } 318 } 319 320 void 321 usage(void) 322 { 323 fprintf(stderr, "usage: %s [-B | -t] [-c count] [-d delay] [-i interface]\n", argv0); 324 exit(1); 325 } 326 327 int 328 main(int argc, char *argv[]) 329 { 330 char ifn[IF_NAMESIZE] = ""; 331 char *end; 332 struct timeval tv; 333 long count = 0, delay = 1000; 334 335 ARGBEGIN { 336 case 'B': 337 Bflag = 1; 338 break; 339 case 'c': 340 errno = 0; 341 count = strtol(EARGF(usage()), &end, 10); 342 if (*end != '\0' || errno) 343 errx(1, "invalid count"); 344 break; 345 case 'd': 346 errno = 0; 347 delay = strtol(EARGF(usage()), &end, 10); 348 if (*end != '\0' || errno) 349 errx(1, "invalid delay"); 350 break; 351 case 'i': 352 strncpy(ifn, EARGF(usage()), sizeof(ifn)); 353 ifn[IF_NAMESIZE - 1] = '\0'; 354 break; 355 case 't': 356 tflag = 1; 357 break; 358 default: 359 usage(); 360 } ARGEND 361 362 getmonotime(&begin); 363 ifname = ifn; 364 signal(SIGINFO, sighdlr); 365 ms2tv(&tv, delay); 366 scan(); 367 loop(count, &tv); 368 return 0; 369 }