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