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