xref: /openbsd-src/usr.bin/systat/if.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: if.c,v 1.13 2009/04/03 20:29:21 deraadt Exp $ */
2 /*
3  * Copyright (c) 2004 Markus Friedl <markus@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <sys/param.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/sysctl.h>
21 #include <net/if.h>
22 #include <net/if_dl.h>
23 #include <net/route.h>
24 #include <sys/sockio.h>
25 
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "systat.h"
30 
31 static  enum state { BOOT, TIME, RUN } state = TIME;
32 
33 struct ifcount {
34 	u_int64_t	ifc_ib;			/* input bytes */
35 	u_int64_t	ifc_ip;			/* input packets */
36 	u_int64_t	ifc_ie;			/* input errors */
37 	u_int64_t	ifc_ob;			/* output bytes */
38 	u_int64_t	ifc_op;			/* output packets */
39 	u_int64_t	ifc_oe;			/* output errors */
40 	u_int64_t	ifc_co;			/* collisions */
41 	int		ifc_flags;		/* up / down */
42 	int		ifc_state;		/* link state */
43 } sum;
44 
45 struct ifstat {
46 	char		ifs_name[IFNAMSIZ];	/* interface name */
47 	char		ifs_description[IFDESCRSIZE];
48 	struct ifcount	ifs_cur;
49 	struct ifcount	ifs_old;
50 	struct ifcount	ifs_now;
51 } *ifstats;
52 
53 static	int nifs = 0;
54 static int num_ifs = 0;
55 
56 void print_if(void);
57 int read_if(void);
58 int select_if(void);
59 int if_keyboard_callback(int);
60 
61 static void fetchifstat(void);
62 static void showifstat(struct ifstat *);
63 static void showtotal(void);
64 
65 
66 /* Define fields */
67 field_def fields_if[] = {
68 	{"IFACE", 8, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
69 	{"STATE", 4, 6, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
70 	{"IPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
71 	{"IBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
72 	{"IERRS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
73 	{"OPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
74 	{"OBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
75 	{"OERRS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
76 	{"COLLS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
77 	{"DESC", 14, 64, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
78 };
79 
80 
81 #define FIELD_ADDR(x) (&fields_if[x])
82 
83 #define FLD_IF_IFACE	FIELD_ADDR(0)
84 #define FLD_IF_STATE	FIELD_ADDR(1)
85 #define FLD_IF_IPKTS	FIELD_ADDR(2)
86 #define FLD_IF_IBYTES	FIELD_ADDR(3)
87 #define FLD_IF_IERRS	FIELD_ADDR(4)
88 #define FLD_IF_OPKTS	FIELD_ADDR(5)
89 #define FLD_IF_OBYTES	FIELD_ADDR(6)
90 #define FLD_IF_OERRS	FIELD_ADDR(7)
91 #define FLD_IF_COLLS	FIELD_ADDR(8)
92 #define FLD_IF_DESC	FIELD_ADDR(9)
93 
94 
95 /* Define views */
96 field_def *view_if_0[] = {
97 	FLD_IF_IFACE, FLD_IF_STATE, FLD_IF_DESC, FLD_IF_IPKTS,
98 	FLD_IF_IBYTES, FLD_IF_IERRS, FLD_IF_OPKTS, FLD_IF_OBYTES,
99 	FLD_IF_OERRS, FLD_IF_COLLS, NULL
100 };
101 
102 /* Define view managers */
103 
104 struct view_manager ifstat_mgr = {
105 	"Ifstat", select_if, read_if, NULL, print_header,
106 	print_if, if_keyboard_callback, NULL, NULL
107 };
108 
109 field_view views_if[] = {
110 	{view_if_0, "ifstat", '1', &ifstat_mgr},
111 	{NULL, NULL, 0, NULL}
112 };
113 
114 
115 int
116 initifstat(void)
117 {
118 	field_view *v;
119 	read_if();
120 	for (v = views_if; v->name != NULL; v++)
121 		add_view(v);
122 
123 	return(1);
124 }
125 
126 #define UPDATE(x, y) do { \
127 		ifs->ifs_now.x = ifm.y; \
128 		ifs->ifs_cur.x = ifs->ifs_now.x - ifs->ifs_old.x; \
129 		if (state == TIME) {\
130 			ifs->ifs_old.x = ifs->ifs_now.x; \
131 			ifs->ifs_cur.x /= naptime; \
132 		} \
133 		sum.x += ifs->ifs_cur.x; \
134 	} while(0)
135 
136 
137 void
138 rt_getaddrinfo(struct sockaddr *sa, int addrs, struct sockaddr **info)
139 {
140 	int i;
141 
142 	for (i = 0; i < RTAX_MAX; i++) {
143 		if (addrs & (1 << i)) {
144 			info[i] = sa;
145 			sa = (struct sockaddr *) ((char *)(sa) +
146 			    roundup(sa->sa_len, sizeof(long)));
147 		} else
148 			info[i] = NULL;
149 	}
150 }
151 
152 
153 
154 int
155 select_if(void)
156 {
157 	num_disp = num_ifs + 1;
158 	return (0);
159 }
160 
161 int
162 read_if(void)
163 {
164 	fetchifstat();
165 	num_disp = num_ifs + 1;
166 
167 	return 0;
168 }
169 
170 void
171 print_if(void)
172 {
173 	int n, i, count = 0;
174 
175 	for (n = 0, i = 0; n < nifs; n++) {
176 		if (ifstats[n].ifs_name[0] == '\0')
177 			continue;
178 		if (i++ < dispstart)
179 			continue;
180 		if (i == num_disp)
181 			break;
182 		showifstat(ifstats + n);
183 		if (maxprint > 0 && ++count >= maxprint)
184 			return;
185 	}
186 	showtotal();
187 }
188 
189 
190 static void
191 fetchifstat(void)
192 {
193 	struct ifstat *newstats, *ifs;
194 	struct if_msghdr ifm;
195 	struct sockaddr *info[RTAX_MAX];
196 	struct sockaddr_dl *sdl;
197 	char *buf, *next, *lim;
198 	static int s = -1;
199 	int mib[6];
200 	size_t need;
201 
202 	mib[0] = CTL_NET;
203 	mib[1] = AF_ROUTE;
204 	mib[2] = 0;
205 	mib[3] = 0;
206 	mib[4] = NET_RT_IFLIST;
207 	mib[5] = 0;
208 
209 	if (sysctl(mib, 6, NULL, &need, NULL, 0) == -1)
210 		return;
211 	if ((buf = malloc(need)) == NULL)
212 		return;
213 	if (sysctl(mib, 6, buf, &need, NULL, 0) == -1) {
214 		free(buf);
215 		return;
216 	}
217 
218 	bzero(&sum, sizeof(sum));
219 	num_ifs = 0;
220 
221 	lim = buf + need;
222 	for (next = buf; next < lim; next += ifm.ifm_msglen) {
223 		bcopy(next, &ifm, sizeof ifm);
224 		if (ifm.ifm_type != RTM_IFINFO ||
225 		   !(ifm.ifm_addrs & RTA_IFP))
226 			continue;
227 		if (ifm.ifm_index >= nifs) {
228 			if ((newstats = realloc(ifstats, (ifm.ifm_index + 4)
229 			    * sizeof(struct ifstat))) == NULL)
230 				continue;
231 			ifstats = newstats;
232 			for (; nifs < ifm.ifm_index + 4; nifs++)
233 				bzero(&ifstats[nifs], sizeof(*ifstats));
234 		}
235 		ifs = &ifstats[ifm.ifm_index];
236 		if (ifs->ifs_name[0] == '\0') {
237 			bzero(&info, sizeof(info));
238 			rt_getaddrinfo(
239 			    (struct sockaddr *)((struct if_msghdr *)next + 1),
240 			    ifm.ifm_addrs, info);
241 			sdl = (struct sockaddr_dl *)info[RTAX_IFP];
242 
243 			if (sdl && sdl->sdl_family == AF_LINK &&
244 			    sdl->sdl_nlen > 0) {
245 				struct ifreq ifrdesc;
246 				char ifdescr[IFDESCRSIZE];
247 				int s;
248 
249 				bcopy(sdl->sdl_data, ifs->ifs_name,
250 				      sdl->sdl_nlen);
251 				ifs->ifs_name[sdl->sdl_nlen] = '\0';
252 
253 				/* Get the interface description */
254 				memset(&ifrdesc, 0, sizeof(ifrdesc));
255 				strlcpy(ifrdesc.ifr_name, ifs->ifs_name,
256 					sizeof(ifrdesc.ifr_name));
257 				ifrdesc.ifr_data = (caddr_t)&ifdescr;
258 
259 				s = socket(AF_INET, SOCK_DGRAM, 0);
260 				if (s != -1) {
261 					if (ioctl(s, SIOCGIFDESCR, &ifrdesc) == 0)
262 						strlcpy(ifs->ifs_description,
263 						    ifrdesc.ifr_data,
264 						    sizeof(ifs->ifs_description));
265 					close(s);
266 				}
267 			}
268 			if (ifs->ifs_name[0] == '\0')
269 				continue;
270 		}
271 		num_ifs++;
272 		UPDATE(ifc_ip, ifm_data.ifi_ipackets);
273 		UPDATE(ifc_ib, ifm_data.ifi_ibytes);
274 		UPDATE(ifc_ie, ifm_data.ifi_ierrors);
275 		UPDATE(ifc_op, ifm_data.ifi_opackets);
276 		UPDATE(ifc_ob, ifm_data.ifi_obytes);
277 		UPDATE(ifc_oe, ifm_data.ifi_oerrors);
278 		UPDATE(ifc_co, ifm_data.ifi_collisions);
279 		ifs->ifs_cur.ifc_flags = ifm.ifm_flags;
280 		ifs->ifs_cur.ifc_state = ifm.ifm_data.ifi_link_state;
281 	}
282 	free(buf);
283 }
284 
285 
286 static void
287 showifstat(struct ifstat *ifs)
288 {
289 	print_fld_str(FLD_IF_IFACE, ifs->ifs_name);
290 
291 	tb_start();
292 	tbprintf("%s", ifs->ifs_cur.ifc_flags & IFF_UP ?
293 		 "up" : "dn");
294 
295 	switch (ifs->ifs_cur.ifc_state) {
296 	case LINK_STATE_UP:
297 	case LINK_STATE_HALF_DUPLEX:
298 	case LINK_STATE_FULL_DUPLEX:
299 		tbprintf(":U");
300 		break;
301 	case LINK_STATE_DOWN:
302 		tbprintf (":D");
303 		break;
304 	}
305 
306 	print_fld_tb(FLD_IF_STATE);
307 
308 	print_fld_str(FLD_IF_DESC, ifs->ifs_description);
309 
310 	print_fld_size(FLD_IF_IBYTES, ifs->ifs_cur.ifc_ib);
311 	print_fld_size(FLD_IF_IPKTS, ifs->ifs_cur.ifc_ip);
312 	print_fld_size(FLD_IF_IERRS, ifs->ifs_cur.ifc_ie);
313 
314 	print_fld_size(FLD_IF_OBYTES, ifs->ifs_cur.ifc_ob);
315 	print_fld_size(FLD_IF_OPKTS, ifs->ifs_cur.ifc_op);
316 	print_fld_size(FLD_IF_OERRS, ifs->ifs_cur.ifc_oe);
317 
318 	print_fld_size(FLD_IF_COLLS, ifs->ifs_cur.ifc_co);
319 
320 	end_line();
321 }
322 
323 static void
324 showtotal(void)
325 {
326 	print_fld_str(FLD_IF_IFACE, "Totals");
327 
328 	print_fld_size(FLD_IF_IBYTES, sum.ifc_ib);
329 	print_fld_size(FLD_IF_IPKTS, sum.ifc_ip);
330 	print_fld_size(FLD_IF_IERRS, sum.ifc_ie);
331 
332 	print_fld_size(FLD_IF_OBYTES, sum.ifc_ob);
333 	print_fld_size(FLD_IF_OPKTS, sum.ifc_op);
334 	print_fld_size(FLD_IF_OERRS, sum.ifc_oe);
335 
336 	print_fld_size(FLD_IF_COLLS, sum.ifc_co);
337 
338 	end_line();
339 
340 }
341 
342 int
343 if_keyboard_callback(int ch)
344 {
345 	struct ifstat *ifs;
346 
347 	switch (ch) {
348 	case 'r':
349 		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
350 			ifs->ifs_old = ifs->ifs_now;
351 		state = RUN;
352 		gotsig_alarm = 1;
353 
354 		break;
355 	case 'b':
356 		state = BOOT;
357 		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
358 			bzero(&ifs->ifs_old, sizeof(ifs->ifs_old));
359 		gotsig_alarm = 1;
360 		break;
361 	case 't':
362 		state = TIME;
363 		gotsig_alarm = 1;
364 		break;
365 	default:
366 		return keyboard_callback(ch);
367 	};
368 
369 	return 1;
370 }
371 
372