1108684Sphk/* 2108684Sphk * Copyright (c) 2003, Trent Nelson, <trent@arpa.com>. 3108684Sphk * All rights reserved. 4108684Sphk * 5108684Sphk * Redistribution and use in source and binary forms, with or without 6108684Sphk * modification, are permitted provided that the following conditions 7108684Sphk * are met: 8108684Sphk * 1. Redistributions of source code must retain the above copyright 9108684Sphk * notice, this list of conditions and the following disclaimer. 10108684Sphk * 2. Redistributions in binary form must reproduce the above copyright 11108684Sphk * notice, this list of conditions and the following disclaimer in the 12108684Sphk * documentation and/or other materials provided with the distribution. 13108684Sphk * 3. The name of the author may not be used to endorse or promote products 14108684Sphk * derived from this software without specific prior written permission. 15108684Sphk * 16108684Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17108684Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18108684Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19108684Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20108684Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21108684Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22108684Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTIFSTAT_ERRUPTION) 23108684Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24108684Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25108684Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26108684Sphk * SUCH DAMAGE. 27108684Sphk * 28108684Sphk * $FreeBSD$ 29108684Sphk */ 30108684Sphk 31108684Sphk#include <sys/types.h> 32108684Sphk#include <sys/socket.h> 33108684Sphk#include <sys/sysctl.h> 34108684Sphk#include <net/if.h> 35108684Sphk#include <net/if_mib.h> 36108684Sphk 37108684Sphk#include <stdlib.h> 38126775Sdwmalone#include <string.h> 39108684Sphk#include <err.h> 40175239Sdelphij#include <errno.h> 41108684Sphk 42108684Sphk#include "systat.h" 43108684Sphk#include "extern.h" 44108684Sphk#include "convtbl.h" 45108684Sphk 46231279Sed /* Column numbers */ 47108684Sphk 48108684Sphk#define C1 0 /* 0-19 */ 49158161Sbde#define C2 20 /* 20-39 */ 50108684Sphk#define C3 40 /* 40-59 */ 51108684Sphk#define C4 60 /* 60-80 */ 52108684Sphk#define C5 80 /* Used for label positioning. */ 53108684Sphk 54126775Sdwmalonestatic const int col0 = 0; 55126775Sdwmalonestatic const int col1 = C1; 56126775Sdwmalonestatic const int col2 = C2; 57126775Sdwmalonestatic const int col3 = C3; 58126775Sdwmalonestatic const int col4 = C4; 59126775Sdwmalonestatic const int col5 = C5; 60108684Sphk 61108684Sphk 62158161SbdeSLIST_HEAD(, if_stat) curlist; 63108684SphkSLIST_HEAD(, if_stat_disp) displist; 64108684Sphk 65108684Sphkstruct if_stat { 66108684Sphk SLIST_ENTRY(if_stat) link; 67108684Sphk char if_name[IF_NAMESIZE]; 68108684Sphk struct ifmibdata if_mib; 69108684Sphk struct timeval tv; 70108684Sphk struct timeval tv_lastchanged; 71108684Sphk u_long if_in_curtraffic; 72108684Sphk u_long if_out_curtraffic; 73108684Sphk u_long if_in_traffic_peak; 74108684Sphk u_long if_out_traffic_peak; 75108684Sphk u_int if_row; /* Index into ifmib sysctl */ 76108684Sphk u_int if_ypos; /* 0 if not being displayed */ 77108684Sphk u_int display; 78108684Sphk}; 79108684Sphk 80108684Sphkextern u_int curscale; 81108684Sphk 82164677Syarstatic void right_align_string(struct if_stat *); 83108684Sphkstatic void getifmibdata(const int, struct ifmibdata *); 84108684Sphkstatic void sort_interface_list(void); 85108684Sphkstatic u_int getifnum(void); 86108684Sphk 87108684Sphk#define IFSTAT_ERR(n, s) do { \ 88108684Sphk putchar(''); \ 89108684Sphk closeifstat(wnd); \ 90108684Sphk err((n), (s)); \ 91108684Sphk} while (0) 92108684Sphk 93158160Sbde#define TOPLINE 3 94108684Sphk#define TOPLABEL \ 95108684Sphk" Interface Traffic Peak Total" 96108684Sphk 97158160Sbde#define STARTING_ROW (TOPLINE + 1) 98158160Sbde#define ROW_SPACING (3) 99158160Sbde 100108684Sphk#define CLEAR_LINE(y, x) do { \ 101108684Sphk wmove(wnd, y, x); \ 102108684Sphk wclrtoeol(wnd); \ 103108684Sphk} while (0) 104108684Sphk 105108684Sphk#define IN_col2 (ifp->if_in_curtraffic) 106108684Sphk#define OUT_col2 (ifp->if_out_curtraffic) 107108684Sphk#define IN_col3 (ifp->if_in_traffic_peak) 108108684Sphk#define OUT_col3 (ifp->if_out_traffic_peak) 109108684Sphk#define IN_col4 (ifp->if_mib.ifmd_data.ifi_ibytes) 110108684Sphk#define OUT_col4 (ifp->if_mib.ifmd_data.ifi_obytes) 111108684Sphk 112108684Sphk#define EMPTY_COLUMN " " 113108684Sphk#define CLEAR_COLUMN(y, x) mvprintw((y), (x), "%20s", EMPTY_COLUMN); 114108684Sphk 115108684Sphk#define DOPUTRATE(c, r, d) do { \ 116108684Sphk CLEAR_COLUMN(r, c); \ 117108684Sphk mvprintw(r, (c), "%10.3f %s%s ", \ 118108684Sphk convert(d##_##c, curscale), \ 119108684Sphk get_string(d##_##c, curscale), \ 120108684Sphk "/s"); \ 121108684Sphk} while (0) 122108684Sphk 123108684Sphk#define DOPUTTOTAL(c, r, d) do { \ 124231279Sed CLEAR_COLUMN((r), (c)); \ 125231279Sed mvprintw((r), (c), "%12.3f %s ", \ 126231279Sed convert(d##_##c, SC_AUTO), \ 127108684Sphk get_string(d##_##c, SC_AUTO)); \ 128108684Sphk} while (0) 129108684Sphk 130108684Sphk#define PUTRATE(c, r) do { \ 131108684Sphk DOPUTRATE(c, (r), IN); \ 132108684Sphk DOPUTRATE(c, (r)+1, OUT); \ 133108684Sphk} while (0) 134108684Sphk 135108684Sphk#define PUTTOTAL(c, r) do { \ 136108684Sphk DOPUTTOTAL(c, (r), IN); \ 137108684Sphk DOPUTTOTAL(c, (r)+1, OUT); \ 138108684Sphk} while (0) 139108684Sphk 140108684Sphk#define PUTNAME(p) do { \ 141108684Sphk mvprintw(p->if_ypos, 0, "%s", p->if_name); \ 142108684Sphk mvprintw(p->if_ypos, col2-3, "%s", (const char *)"in"); \ 143108684Sphk mvprintw(p->if_ypos+1, col2-3, "%s", (const char *)"out"); \ 144108684Sphk} while (0) 145108684Sphk 146108684Sphk 147108684SphkWINDOW * 148108684Sphkopenifstat(void) 149108684Sphk{ 150158160Sbde return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); 151108684Sphk} 152108684Sphk 153108684Sphkvoid 154108684Sphkcloseifstat(WINDOW *w) 155108684Sphk{ 156108684Sphk struct if_stat *node = NULL; 157108684Sphk 158108684Sphk while (!SLIST_EMPTY(&curlist)) { 159108684Sphk node = SLIST_FIRST(&curlist); 160108684Sphk SLIST_REMOVE_HEAD(&curlist, link); 161108684Sphk free(node); 162108684Sphk } 163108684Sphk 164108684Sphk if (w != NULL) { 165108684Sphk wclear(w); 166108684Sphk wrefresh(w); 167108684Sphk delwin(w); 168108684Sphk } 169108684Sphk 170108684Sphk return; 171108684Sphk} 172108684Sphk 173108684Sphk 174108684Sphkvoid 175108684Sphklabelifstat(void) 176108684Sphk{ 177108684Sphk 178108684Sphk wmove(wnd, TOPLINE, 0); 179108684Sphk wclrtoeol(wnd); 180108684Sphk mvprintw(TOPLINE, 0, "%s", TOPLABEL); 181108684Sphk 182108684Sphk return; 183108684Sphk} 184108684Sphk 185108684Sphkvoid 186108684Sphkshowifstat(void) 187108684Sphk{ 188108684Sphk struct if_stat *ifp = NULL; 189108684Sphk SLIST_FOREACH(ifp, &curlist, link) { 190108684Sphk if (ifp->display == 0) 191108684Sphk continue; 192108684Sphk PUTNAME(ifp); 193108684Sphk PUTRATE(col2, ifp->if_ypos); 194108684Sphk PUTRATE(col3, ifp->if_ypos); 195108684Sphk PUTTOTAL(col4, ifp->if_ypos); 196108684Sphk } 197108684Sphk 198108684Sphk return; 199108684Sphk} 200108684Sphk 201158161Sbdeint 202108684Sphkinitifstat(void) 203108684Sphk{ 204108684Sphk struct if_stat *p = NULL; 205108684Sphk u_int n = 0, i = 0; 206108684Sphk 207108684Sphk n = getifnum(); 208108684Sphk if (n <= 0) 209108684Sphk return -1; 210108684Sphk 211108684Sphk SLIST_INIT(&curlist); 212108684Sphk 213108684Sphk for (i = 0; i < n; i++) { 214175239Sdelphij p = (struct if_stat *)calloc(1, sizeof(struct if_stat)); 215108684Sphk if (p == NULL) 216108684Sphk IFSTAT_ERR(1, "out of memory"); 217108684Sphk SLIST_INSERT_HEAD(&curlist, p, link); 218108684Sphk p->if_row = i+1; 219108684Sphk getifmibdata(p->if_row, &p->if_mib); 220108684Sphk right_align_string(p); 221108684Sphk 222158161Sbde /* 223108684Sphk * Initially, we only display interfaces that have 224108684Sphk * received some traffic. 225108684Sphk */ 226108684Sphk if (p->if_mib.ifmd_data.ifi_ibytes != 0) 227108684Sphk p->display = 1; 228108684Sphk } 229108684Sphk 230108684Sphk sort_interface_list(); 231108684Sphk 232108684Sphk return 1; 233108684Sphk} 234108684Sphk 235108684Sphkvoid 236108684Sphkfetchifstat(void) 237108684Sphk{ 238108684Sphk struct if_stat *ifp = NULL; 239108684Sphk struct timeval tv, new_tv, old_tv; 240108684Sphk double elapsed = 0.0; 241108684Sphk u_int new_inb, new_outb, old_inb, old_outb = 0; 242108684Sphk u_int we_need_to_sort_interface_list = 0; 243108684Sphk 244108684Sphk SLIST_FOREACH(ifp, &curlist, link) { 245158161Sbde /* 246108684Sphk * Grab a copy of the old input/output values before we 247108684Sphk * call getifmibdata(). 248108684Sphk */ 249158161Sbde old_inb = ifp->if_mib.ifmd_data.ifi_ibytes; 250108684Sphk old_outb = ifp->if_mib.ifmd_data.ifi_obytes; 251108684Sphk ifp->tv_lastchanged = ifp->if_mib.ifmd_data.ifi_lastchange; 252108684Sphk 253126775Sdwmalone if (gettimeofday(&new_tv, (struct timezone *)0) != 0) 254108684Sphk IFSTAT_ERR(2, "error getting time of day"); 255108684Sphk (void)getifmibdata(ifp->if_row, &ifp->if_mib); 256108684Sphk 257108684Sphk 258231279Sed new_inb = ifp->if_mib.ifmd_data.ifi_ibytes; 259231279Sed new_outb = ifp->if_mib.ifmd_data.ifi_obytes; 260108684Sphk 261108684Sphk /* Display interface if it's received some traffic. */ 262108684Sphk if (new_inb > 0 && old_inb == 0) { 263108684Sphk ifp->display = 1; 264108684Sphk we_need_to_sort_interface_list++; 265158161Sbde } 266108684Sphk 267108684Sphk /* 268108684Sphk * The rest is pretty trivial. Calculate the new values 269108684Sphk * for our current traffic rates, and while we're there, 270108684Sphk * see if we have new peak rates. 271108684Sphk */ 272231279Sed old_tv = ifp->tv; 273231279Sed timersub(&new_tv, &old_tv, &tv); 274231279Sed elapsed = tv.tv_sec + (tv.tv_usec * 1e-6); 275108684Sphk 276108684Sphk ifp->if_in_curtraffic = new_inb - old_inb; 277108684Sphk ifp->if_out_curtraffic = new_outb - old_outb; 278108684Sphk 279108684Sphk /* 280108684Sphk * Rather than divide by the time specified on the comm- 281108684Sphk * and line, we divide by ``elapsed'' as this is likely 282108684Sphk * to be more accurate. 283108684Sphk */ 284231279Sed ifp->if_in_curtraffic /= elapsed; 285231279Sed ifp->if_out_curtraffic /= elapsed; 286108684Sphk 287108684Sphk if (ifp->if_in_curtraffic > ifp->if_in_traffic_peak) 288108684Sphk ifp->if_in_traffic_peak = ifp->if_in_curtraffic; 289108684Sphk 290108684Sphk if (ifp->if_out_curtraffic > ifp->if_out_traffic_peak) 291108684Sphk ifp->if_out_traffic_peak = ifp->if_out_curtraffic; 292108684Sphk 293108684Sphk ifp->tv.tv_sec = new_tv.tv_sec; 294108684Sphk ifp->tv.tv_usec = new_tv.tv_usec; 295108684Sphk 296108684Sphk } 297108684Sphk 298108684Sphk if (we_need_to_sort_interface_list) 299108684Sphk sort_interface_list(); 300108684Sphk 301108684Sphk return; 302108684Sphk} 303108684Sphk 304158161Sbde/* 305108684Sphk * We want to right justify our interface names against the first column 306108684Sphk * (first sixteen or so characters), so we need to do some alignment. 307108684Sphk */ 308108684Sphkstatic void 309164677Syarright_align_string(struct if_stat *ifp) 310108684Sphk{ 311108684Sphk int str_len = 0, pad_len = 0; 312108684Sphk char *newstr = NULL, *ptr = NULL; 313108684Sphk 314108684Sphk if (ifp == NULL || ifp->if_mib.ifmd_name == NULL) 315108684Sphk return; 316108684Sphk else { 317108684Sphk /* string length + '\0' */ 318108684Sphk str_len = strlen(ifp->if_mib.ifmd_name)+1; 319108684Sphk pad_len = IF_NAMESIZE-(str_len); 320108684Sphk 321164677Syar newstr = ifp->if_name; 322108684Sphk ptr = newstr + pad_len; 323108684Sphk (void)memset((void *)newstr, (int)' ', IF_NAMESIZE); 324108684Sphk (void)strncpy(ptr, (const char *)&ifp->if_mib.ifmd_name, 325108684Sphk str_len); 326108684Sphk } 327108684Sphk 328108684Sphk return; 329108684Sphk} 330108684Sphk 331108684Sphk/* 332108684Sphk * This function iterates through our list of interfaces, identifying 333108684Sphk * those that are to be displayed (ifp->display = 1). For each interf- 334108684Sphk * rface that we're displaying, we generate an appropriate position for 335108684Sphk * it on the screen (ifp->if_ypos). 336108684Sphk * 337108684Sphk * This function is called any time a change is made to an interface's 338108684Sphk * ``display'' state. 339108684Sphk */ 340108684Sphkvoid 341108684Sphksort_interface_list(void) 342108684Sphk{ 343108684Sphk struct if_stat *ifp = NULL; 344158161Sbde u_int y = 0; 345108684Sphk 346108684Sphk y = STARTING_ROW; 347108684Sphk SLIST_FOREACH(ifp, &curlist, link) { 348108684Sphk if (ifp->display) { 349108684Sphk ifp->if_ypos = y; 350108684Sphk y += ROW_SPACING; 351108684Sphk } 352108684Sphk } 353108684Sphk} 354108684Sphk 355108684Sphkstatic 356108684Sphkunsigned int 357108684Sphkgetifnum(void) 358108684Sphk{ 359108684Sphk u_int data = 0; 360108684Sphk size_t datalen = 0; 361108684Sphk static int name[] = { CTL_NET, 362108684Sphk PF_LINK, 363108684Sphk NETLINK_GENERIC, 364108684Sphk IFMIB_SYSTEM, 365108684Sphk IFMIB_IFCOUNT }; 366108684Sphk 367108684Sphk datalen = sizeof(data); 368126775Sdwmalone if (sysctl(name, 5, (void *)&data, (size_t *)&datalen, (void *)NULL, 369126775Sdwmalone (size_t)0) != 0) 370108684Sphk IFSTAT_ERR(1, "sysctl error"); 371108684Sphk return data; 372108684Sphk} 373108684Sphk 374158161Sbdestatic void 375108684Sphkgetifmibdata(int row, struct ifmibdata *data) 376108684Sphk{ 377108684Sphk size_t datalen = 0; 378108684Sphk static int name[] = { CTL_NET, 379108684Sphk PF_LINK, 380108684Sphk NETLINK_GENERIC, 381108684Sphk IFMIB_IFDATA, 382108684Sphk 0, 383108684Sphk IFDATA_GENERAL }; 384108684Sphk datalen = sizeof(*data); 385108684Sphk name[4] = row; 386108684Sphk 387175239Sdelphij if ((sysctl(name, 6, (void *)data, (size_t *)&datalen, (void *)NULL, 388175239Sdelphij (size_t)0) != 0) && (errno != ENOENT)) 389108684Sphk IFSTAT_ERR(2, "sysctl error getting interface data"); 390108684Sphk} 391108684Sphk 392108684Sphkint 393108684Sphkcmdifstat(const char *cmd, const char *args) 394108684Sphk{ 395108684Sphk int retval = 0; 396108684Sphk 397108684Sphk retval = ifcmd(cmd, args); 398108684Sphk /* ifcmd() returns 1 on success */ 399108684Sphk if (retval == 1) { 400108684Sphk showifstat(); 401108684Sphk refresh(); 402108684Sphk } 403108684Sphk 404108684Sphk return retval; 405108684Sphk} 406