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> 41247036Smelifaro#include <fnmatch.h> 42108684Sphk 43108684Sphk#include "systat.h" 44108684Sphk#include "extern.h" 45108684Sphk#include "convtbl.h" 46108684Sphk 47226396Sed /* Column numbers */ 48108684Sphk 49108684Sphk#define C1 0 /* 0-19 */ 50158161Sbde#define C2 20 /* 20-39 */ 51108684Sphk#define C3 40 /* 40-59 */ 52108684Sphk#define C4 60 /* 60-80 */ 53108684Sphk#define C5 80 /* Used for label positioning. */ 54108684Sphk 55126775Sdwmalonestatic const int col0 = 0; 56126775Sdwmalonestatic const int col1 = C1; 57126775Sdwmalonestatic const int col2 = C2; 58126775Sdwmalonestatic const int col3 = C3; 59126775Sdwmalonestatic const int col4 = C4; 60126775Sdwmalonestatic const int col5 = C5; 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; 75247036Smelifaro u_long if_in_curpps; 76247036Smelifaro u_long if_out_curpps; 77247036Smelifaro u_long if_in_pps_peak; 78247036Smelifaro u_long if_out_pps_peak; 79108684Sphk u_int if_row; /* Index into ifmib sysctl */ 80108684Sphk u_int if_ypos; /* 0 if not being displayed */ 81108684Sphk u_int display; 82247036Smelifaro u_int match; 83108684Sphk}; 84108684Sphk 85247036Smelifaroextern int curscale; 86247036Smelifaroextern char *matchline; 87247036Smelifaroextern int showpps; 88247036Smelifaroextern int needsort; 89108684Sphk 90247036Smelifarostatic int needclear = 0; 91247036Smelifaro 92164677Syarstatic void right_align_string(struct if_stat *); 93108684Sphkstatic void getifmibdata(const int, struct ifmibdata *); 94108684Sphkstatic void sort_interface_list(void); 95108684Sphkstatic u_int getifnum(void); 96108684Sphk 97108684Sphk#define IFSTAT_ERR(n, s) do { \ 98231011Sed putchar('\014'); \ 99108684Sphk closeifstat(wnd); \ 100108684Sphk err((n), (s)); \ 101108684Sphk} while (0) 102108684Sphk 103158160Sbde#define TOPLINE 3 104108684Sphk#define TOPLABEL \ 105108684Sphk" Interface Traffic Peak Total" 106108684Sphk 107158160Sbde#define STARTING_ROW (TOPLINE + 1) 108158160Sbde#define ROW_SPACING (3) 109158160Sbde 110247036Smelifaro#define IN_col2 (showpps ? ifp->if_in_curpps : ifp->if_in_curtraffic) 111247036Smelifaro#define OUT_col2 (showpps ? ifp->if_out_curpps : ifp->if_out_curtraffic) 112247036Smelifaro#define IN_col3 (showpps ? \ 113247036Smelifaro ifp->if_in_pps_peak : ifp->if_in_traffic_peak) 114247036Smelifaro#define OUT_col3 (showpps ? \ 115247036Smelifaro ifp->if_out_pps_peak : ifp->if_out_traffic_peak) 116247036Smelifaro#define IN_col4 (showpps ? \ 117247036Smelifaro ifp->if_mib.ifmd_data.ifi_ipackets : ifp->if_mib.ifmd_data.ifi_ibytes) 118247036Smelifaro#define OUT_col4 (showpps ? \ 119247036Smelifaro ifp->if_mib.ifmd_data.ifi_opackets : ifp->if_mib.ifmd_data.ifi_obytes) 120108684Sphk 121108684Sphk#define EMPTY_COLUMN " " 122108684Sphk#define CLEAR_COLUMN(y, x) mvprintw((y), (x), "%20s", EMPTY_COLUMN); 123108684Sphk 124108684Sphk#define DOPUTRATE(c, r, d) do { \ 125108684Sphk CLEAR_COLUMN(r, c); \ 126247036Smelifaro if (showpps) { \ 127247036Smelifaro mvprintw(r, (c), "%10.3f %cp%s ", \ 128247036Smelifaro convert(d##_##c, curscale), \ 129247036Smelifaro *get_string(d##_##c, curscale), \ 130247036Smelifaro "/s"); \ 131247036Smelifaro } \ 132247036Smelifaro else { \ 133247036Smelifaro mvprintw(r, (c), "%10.3f %s%s ", \ 134247036Smelifaro convert(d##_##c, curscale), \ 135247036Smelifaro get_string(d##_##c, curscale), \ 136247036Smelifaro "/s"); \ 137247036Smelifaro } \ 138108684Sphk} while (0) 139108684Sphk 140108684Sphk#define DOPUTTOTAL(c, r, d) do { \ 141226396Sed CLEAR_COLUMN((r), (c)); \ 142247036Smelifaro if (showpps) { \ 143247036Smelifaro mvprintw((r), (c), "%12.3f %cp ", \ 144247036Smelifaro convert(d##_##c, SC_AUTO), \ 145247036Smelifaro *get_string(d##_##c, SC_AUTO)); \ 146247036Smelifaro } \ 147247036Smelifaro else { \ 148247036Smelifaro mvprintw((r), (c), "%12.3f %s ", \ 149247036Smelifaro convert(d##_##c, SC_AUTO), \ 150247036Smelifaro get_string(d##_##c, SC_AUTO)); \ 151247036Smelifaro } \ 152108684Sphk} while (0) 153108684Sphk 154108684Sphk#define PUTRATE(c, r) do { \ 155108684Sphk DOPUTRATE(c, (r), IN); \ 156108684Sphk DOPUTRATE(c, (r)+1, OUT); \ 157108684Sphk} while (0) 158108684Sphk 159108684Sphk#define PUTTOTAL(c, r) do { \ 160108684Sphk DOPUTTOTAL(c, (r), IN); \ 161108684Sphk DOPUTTOTAL(c, (r)+1, OUT); \ 162108684Sphk} while (0) 163108684Sphk 164108684Sphk#define PUTNAME(p) do { \ 165108684Sphk mvprintw(p->if_ypos, 0, "%s", p->if_name); \ 166108684Sphk mvprintw(p->if_ypos, col2-3, "%s", (const char *)"in"); \ 167108684Sphk mvprintw(p->if_ypos+1, col2-3, "%s", (const char *)"out"); \ 168108684Sphk} while (0) 169108684Sphk 170108684SphkWINDOW * 171108684Sphkopenifstat(void) 172108684Sphk{ 173158160Sbde return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); 174108684Sphk} 175108684Sphk 176108684Sphkvoid 177108684Sphkcloseifstat(WINDOW *w) 178108684Sphk{ 179108684Sphk struct if_stat *node = NULL; 180108684Sphk 181108684Sphk while (!SLIST_EMPTY(&curlist)) { 182108684Sphk node = SLIST_FIRST(&curlist); 183108684Sphk SLIST_REMOVE_HEAD(&curlist, link); 184108684Sphk free(node); 185108684Sphk } 186108684Sphk 187108684Sphk if (w != NULL) { 188108684Sphk wclear(w); 189108684Sphk wrefresh(w); 190108684Sphk delwin(w); 191108684Sphk } 192108684Sphk 193108684Sphk return; 194108684Sphk} 195108684Sphk 196108684Sphkvoid 197108684Sphklabelifstat(void) 198108684Sphk{ 199108684Sphk 200108684Sphk wmove(wnd, TOPLINE, 0); 201108684Sphk wclrtoeol(wnd); 202108684Sphk mvprintw(TOPLINE, 0, "%s", TOPLABEL); 203108684Sphk 204108684Sphk return; 205108684Sphk} 206108684Sphk 207108684Sphkvoid 208108684Sphkshowifstat(void) 209108684Sphk{ 210108684Sphk struct if_stat *ifp = NULL; 211247036Smelifaro 212108684Sphk SLIST_FOREACH(ifp, &curlist, link) { 213247036Smelifaro if (ifp->display == 0 || (ifp->match == 0) || 214247036Smelifaro ifp->if_ypos > LINES - 3 - 1) 215108684Sphk continue; 216108684Sphk PUTNAME(ifp); 217108684Sphk PUTRATE(col2, ifp->if_ypos); 218108684Sphk PUTRATE(col3, ifp->if_ypos); 219108684Sphk PUTTOTAL(col4, ifp->if_ypos); 220108684Sphk } 221108684Sphk 222108684Sphk return; 223108684Sphk} 224108684Sphk 225158161Sbdeint 226108684Sphkinitifstat(void) 227108684Sphk{ 228108684Sphk struct if_stat *p = NULL; 229108684Sphk u_int n = 0, i = 0; 230108684Sphk 231108684Sphk n = getifnum(); 232108684Sphk if (n <= 0) 233247037Smelifaro return (-1); 234108684Sphk 235108684Sphk SLIST_INIT(&curlist); 236108684Sphk 237108684Sphk for (i = 0; i < n; i++) { 238175239Sdelphij p = (struct if_stat *)calloc(1, sizeof(struct if_stat)); 239108684Sphk if (p == NULL) 240108684Sphk IFSTAT_ERR(1, "out of memory"); 241108684Sphk SLIST_INSERT_HEAD(&curlist, p, link); 242108684Sphk p->if_row = i+1; 243108684Sphk getifmibdata(p->if_row, &p->if_mib); 244108684Sphk right_align_string(p); 245247036Smelifaro p->match = 1; 246108684Sphk 247158161Sbde /* 248108684Sphk * Initially, we only display interfaces that have 249108684Sphk * received some traffic. 250108684Sphk */ 251108684Sphk if (p->if_mib.ifmd_data.ifi_ibytes != 0) 252108684Sphk p->display = 1; 253108684Sphk } 254108684Sphk 255108684Sphk sort_interface_list(); 256108684Sphk 257247037Smelifaro return (1); 258108684Sphk} 259108684Sphk 260108684Sphkvoid 261108684Sphkfetchifstat(void) 262108684Sphk{ 263108684Sphk struct if_stat *ifp = NULL; 264108684Sphk struct timeval tv, new_tv, old_tv; 265108684Sphk double elapsed = 0.0; 266108684Sphk u_int new_inb, new_outb, old_inb, old_outb = 0; 267247036Smelifaro u_int new_inp, new_outp, old_inp, old_outp = 0; 268108684Sphk 269108684Sphk SLIST_FOREACH(ifp, &curlist, link) { 270158161Sbde /* 271108684Sphk * Grab a copy of the old input/output values before we 272108684Sphk * call getifmibdata(). 273108684Sphk */ 274158161Sbde old_inb = ifp->if_mib.ifmd_data.ifi_ibytes; 275108684Sphk old_outb = ifp->if_mib.ifmd_data.ifi_obytes; 276247036Smelifaro old_inp = ifp->if_mib.ifmd_data.ifi_ipackets; 277247036Smelifaro old_outp = ifp->if_mib.ifmd_data.ifi_opackets; 278108684Sphk ifp->tv_lastchanged = ifp->if_mib.ifmd_data.ifi_lastchange; 279108684Sphk 280239991Sed (void)gettimeofday(&new_tv, NULL); 281108684Sphk (void)getifmibdata(ifp->if_row, &ifp->if_mib); 282108684Sphk 283226396Sed new_inb = ifp->if_mib.ifmd_data.ifi_ibytes; 284226396Sed new_outb = ifp->if_mib.ifmd_data.ifi_obytes; 285247036Smelifaro new_inp = ifp->if_mib.ifmd_data.ifi_ipackets; 286247036Smelifaro new_outp = ifp->if_mib.ifmd_data.ifi_opackets; 287108684Sphk 288108684Sphk /* Display interface if it's received some traffic. */ 289108684Sphk if (new_inb > 0 && old_inb == 0) { 290108684Sphk ifp->display = 1; 291247036Smelifaro needsort = 1; 292158161Sbde } 293108684Sphk 294108684Sphk /* 295108684Sphk * The rest is pretty trivial. Calculate the new values 296108684Sphk * for our current traffic rates, and while we're there, 297108684Sphk * see if we have new peak rates. 298108684Sphk */ 299226396Sed old_tv = ifp->tv; 300226396Sed timersub(&new_tv, &old_tv, &tv); 301226396Sed elapsed = tv.tv_sec + (tv.tv_usec * 1e-6); 302108684Sphk 303108684Sphk ifp->if_in_curtraffic = new_inb - old_inb; 304108684Sphk ifp->if_out_curtraffic = new_outb - old_outb; 305108684Sphk 306247036Smelifaro ifp->if_in_curpps = new_inp - old_inp; 307247036Smelifaro ifp->if_out_curpps = new_outp - old_outp; 308247036Smelifaro 309108684Sphk /* 310108684Sphk * Rather than divide by the time specified on the comm- 311108684Sphk * and line, we divide by ``elapsed'' as this is likely 312108684Sphk * to be more accurate. 313108684Sphk */ 314226396Sed ifp->if_in_curtraffic /= elapsed; 315226396Sed ifp->if_out_curtraffic /= elapsed; 316247036Smelifaro ifp->if_in_curpps /= elapsed; 317247036Smelifaro ifp->if_out_curpps /= elapsed; 318108684Sphk 319108684Sphk if (ifp->if_in_curtraffic > ifp->if_in_traffic_peak) 320108684Sphk ifp->if_in_traffic_peak = ifp->if_in_curtraffic; 321108684Sphk 322108684Sphk if (ifp->if_out_curtraffic > ifp->if_out_traffic_peak) 323108684Sphk ifp->if_out_traffic_peak = ifp->if_out_curtraffic; 324108684Sphk 325247036Smelifaro if (ifp->if_in_curpps > ifp->if_in_pps_peak) 326247036Smelifaro ifp->if_in_pps_peak = ifp->if_in_curpps; 327247036Smelifaro 328247036Smelifaro if (ifp->if_out_curpps > ifp->if_out_pps_peak) 329247036Smelifaro ifp->if_out_pps_peak = ifp->if_out_curpps; 330247036Smelifaro 331108684Sphk ifp->tv.tv_sec = new_tv.tv_sec; 332108684Sphk ifp->tv.tv_usec = new_tv.tv_usec; 333108684Sphk 334108684Sphk } 335108684Sphk 336247036Smelifaro if (needsort) 337108684Sphk sort_interface_list(); 338108684Sphk 339108684Sphk return; 340108684Sphk} 341108684Sphk 342158161Sbde/* 343108684Sphk * We want to right justify our interface names against the first column 344108684Sphk * (first sixteen or so characters), so we need to do some alignment. 345108684Sphk */ 346108684Sphkstatic void 347164677Syarright_align_string(struct if_stat *ifp) 348108684Sphk{ 349108684Sphk int str_len = 0, pad_len = 0; 350108684Sphk char *newstr = NULL, *ptr = NULL; 351108684Sphk 352108684Sphk if (ifp == NULL || ifp->if_mib.ifmd_name == NULL) 353108684Sphk return; 354108684Sphk else { 355108684Sphk /* string length + '\0' */ 356108684Sphk str_len = strlen(ifp->if_mib.ifmd_name)+1; 357108684Sphk pad_len = IF_NAMESIZE-(str_len); 358108684Sphk 359164677Syar newstr = ifp->if_name; 360108684Sphk ptr = newstr + pad_len; 361108684Sphk (void)memset((void *)newstr, (int)' ', IF_NAMESIZE); 362108684Sphk (void)strncpy(ptr, (const char *)&ifp->if_mib.ifmd_name, 363108684Sphk str_len); 364108684Sphk } 365108684Sphk 366108684Sphk return; 367108684Sphk} 368108684Sphk 369247036Smelifarostatic int 370247036Smelifarocheck_match(const char *ifname) 371247036Smelifaro{ 372247036Smelifaro char *p = matchline, *c, t; 373247036Smelifaro int match = 0, mlen; 374247036Smelifaro 375247037Smelifaro if (matchline == NULL) 376247037Smelifaro return (0); 377247037Smelifaro 378247036Smelifaro /* Strip leading whitespaces */ 379247036Smelifaro while (*p == ' ') 380247036Smelifaro p ++; 381247036Smelifaro 382247036Smelifaro c = p; 383247036Smelifaro while ((mlen = strcspn(c, " ;,")) != 0) { 384247036Smelifaro p = c + mlen; 385247036Smelifaro t = *p; 386247036Smelifaro if (p - c > 0) { 387247036Smelifaro *p = '\0'; 388247036Smelifaro if (fnmatch(c, ifname, FNM_CASEFOLD) == 0) { 389247036Smelifaro *p = t; 390247037Smelifaro return (1); 391247036Smelifaro } 392247036Smelifaro *p = t; 393247036Smelifaro c = p + strspn(p, " ;,"); 394247036Smelifaro } 395247036Smelifaro else { 396247036Smelifaro c = p + strspn(p, " ;,"); 397247036Smelifaro } 398247036Smelifaro } 399247036Smelifaro 400247037Smelifaro return (match); 401247036Smelifaro} 402247036Smelifaro 403108684Sphk/* 404108684Sphk * This function iterates through our list of interfaces, identifying 405108684Sphk * those that are to be displayed (ifp->display = 1). For each interf- 406108684Sphk * rface that we're displaying, we generate an appropriate position for 407108684Sphk * it on the screen (ifp->if_ypos). 408108684Sphk * 409108684Sphk * This function is called any time a change is made to an interface's 410108684Sphk * ``display'' state. 411108684Sphk */ 412108684Sphkvoid 413108684Sphksort_interface_list(void) 414108684Sphk{ 415108684Sphk struct if_stat *ifp = NULL; 416158161Sbde u_int y = 0; 417108684Sphk 418108684Sphk y = STARTING_ROW; 419108684Sphk SLIST_FOREACH(ifp, &curlist, link) { 420247036Smelifaro if (matchline && !check_match(ifp->if_mib.ifmd_name)) 421247036Smelifaro ifp->match = 0; 422247036Smelifaro else 423247036Smelifaro ifp->match = 1; 424247036Smelifaro if (ifp->display && ifp->match) { 425108684Sphk ifp->if_ypos = y; 426108684Sphk y += ROW_SPACING; 427108684Sphk } 428108684Sphk } 429247036Smelifaro 430247036Smelifaro needsort = 0; 431247036Smelifaro needclear = 1; 432108684Sphk} 433108684Sphk 434108684Sphkstatic 435108684Sphkunsigned int 436108684Sphkgetifnum(void) 437108684Sphk{ 438108684Sphk u_int data = 0; 439108684Sphk size_t datalen = 0; 440108684Sphk static int name[] = { CTL_NET, 441108684Sphk PF_LINK, 442108684Sphk NETLINK_GENERIC, 443108684Sphk IFMIB_SYSTEM, 444108684Sphk IFMIB_IFCOUNT }; 445108684Sphk 446108684Sphk datalen = sizeof(data); 447126775Sdwmalone if (sysctl(name, 5, (void *)&data, (size_t *)&datalen, (void *)NULL, 448126775Sdwmalone (size_t)0) != 0) 449108684Sphk IFSTAT_ERR(1, "sysctl error"); 450247037Smelifaro return (data); 451108684Sphk} 452108684Sphk 453158161Sbdestatic void 454108684Sphkgetifmibdata(int row, struct ifmibdata *data) 455108684Sphk{ 456108684Sphk size_t datalen = 0; 457108684Sphk static int name[] = { CTL_NET, 458108684Sphk PF_LINK, 459108684Sphk NETLINK_GENERIC, 460108684Sphk IFMIB_IFDATA, 461108684Sphk 0, 462108684Sphk IFDATA_GENERAL }; 463108684Sphk datalen = sizeof(*data); 464108684Sphk name[4] = row; 465108684Sphk 466175239Sdelphij if ((sysctl(name, 6, (void *)data, (size_t *)&datalen, (void *)NULL, 467175239Sdelphij (size_t)0) != 0) && (errno != ENOENT)) 468108684Sphk IFSTAT_ERR(2, "sysctl error getting interface data"); 469108684Sphk} 470108684Sphk 471108684Sphkint 472108684Sphkcmdifstat(const char *cmd, const char *args) 473108684Sphk{ 474108684Sphk int retval = 0; 475108684Sphk 476108684Sphk retval = ifcmd(cmd, args); 477108684Sphk /* ifcmd() returns 1 on success */ 478108684Sphk if (retval == 1) { 479108684Sphk showifstat(); 480108684Sphk refresh(); 481247036Smelifaro if (needclear) { 482247036Smelifaro werase(wnd); 483247036Smelifaro labelifstat(); 484247036Smelifaro needclear = 0; 485247036Smelifaro } 486108684Sphk } 487108684Sphk 488247037Smelifaro return (retval); 489108684Sphk} 490