xref: /openbsd-src/usr.bin/netstat/if.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: if.c,v 1.60 2009/02/07 15:06:04 chl Exp $	*/
2 /*	$NetBSD: if.c,v 1.16.4.2 1996/06/07 21:46:46 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/protosw.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
38 
39 #include <net/if.h>
40 #include <net/if_dl.h>
41 #include <net/if_types.h>
42 #include <net/route.h>
43 #include <netinet/in.h>
44 #include <netinet/in_var.h>
45 #include <netinet/if_ether.h>
46 #include <arpa/inet.h>
47 
48 #include <err.h>
49 #include <limits.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 
56 #include "netstat.h"
57 
58 static void print_addr(struct sockaddr *, struct sockaddr **, struct if_data *);
59 static void sidewaysintpr(u_int);
60 static void catchalarm(int);
61 static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
62 static void fetchifs(void);
63 
64 /*
65  * Print a description of the network interfaces.
66  * NOTE: ifnetaddr is the location of the kernel global "ifnet",
67  * which is a TAILQ_HEAD.
68  */
69 void
70 intpr(int interval)
71 {
72 	struct if_msghdr ifm;
73 	int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
74 	char name[IFNAMSIZ + 1];	/* + 1 for the '*' */
75 	char *buf, *next, *lim, *cp;
76 	struct rt_msghdr *rtm;
77 	struct ifa_msghdr *ifam;
78 	struct if_data *ifd;
79 	struct sockaddr *sa, *rti_info[RTAX_MAX];
80 	struct sockaddr_dl *sdl;
81 	u_int64_t total = 0;
82 	size_t len;
83 
84 	if (interval) {
85 		sidewaysintpr((unsigned)interval);
86 		return;
87 	}
88 
89 	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
90 		err(1, "sysctl");
91 	if ((buf = malloc(len)) == NULL)
92 		err(1, NULL);
93 	if (sysctl(mib, 6, buf, &len, NULL, 0) == -1)
94 		err(1, "sysctl");
95 
96 	printf("%-7.7s %-5.5s %-11.11s %-17.17s ",
97 	    "Name", "Mtu", "Network", "Address");
98 	if (bflag)
99 		printf("%10.10s %10.10s", "Ibytes", "Obytes");
100 	else
101 		printf("%8.8s %5.5s %8.8s %5.5s %5.5s",
102 		    "Ipkts", "Ierrs", "Opkts", "Oerrs", "Colls");
103 	if (tflag)
104 		printf(" %s", "Time");
105 	if (dflag)
106 		printf(" %s", "Drop");
107 	putchar('\n');
108 
109 	lim = buf + len;
110 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
111 		rtm = (struct rt_msghdr *)next;
112 		if (rtm->rtm_version != RTM_VERSION)
113 			continue;
114 		switch (rtm->rtm_type) {
115 		case RTM_IFINFO:
116 			total = 0;
117 			bcopy(next, &ifm, sizeof ifm);
118 			ifd = &ifm.ifm_data;
119 
120 			sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
121 			get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
122 
123 			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
124 			if (sdl == NULL || sdl->sdl_family != AF_LINK)
125 				continue;
126 			bzero(name, sizeof(name));
127 			if (sdl->sdl_nlen >= IFNAMSIZ)
128 				memcpy(name, sdl->sdl_data, IFNAMSIZ - 1);
129 			else if (sdl->sdl_nlen > 0)
130 				memcpy(name, sdl->sdl_data, sdl->sdl_nlen);
131 
132 			if (interface != 0 && strcmp(name, interface) != 0)
133 				continue;
134 
135 			/* mark inactive interfaces with a '*' */
136 			cp = strchr(name, '\0');
137 			if ((ifm.ifm_flags & IFF_UP) == 0)
138 				*cp++ = '*';
139 			*cp = '\0';
140 
141 			if (qflag) {
142 				total = ifd->ifi_ibytes + ifd->ifi_obytes +
143 				    ifd->ifi_ipackets + ifd->ifi_ierrors +
144 				    ifd->ifi_opackets + ifd->ifi_oerrors +
145 				    ifd->ifi_collisions;
146 				if (tflag)
147 					total += 0; // XXX ifnet.if_timer;
148 				if (dflag)
149 					total += 0; // XXX ifnet.if_snd.ifq_drops;
150 				if (total == 0)
151 					continue;
152 			}
153 
154 			printf("%-7s %-5ld ", name, ifd->ifi_mtu);
155 			print_addr(rti_info[RTAX_IFP], rti_info, ifd);
156 			break;
157 		case RTM_NEWADDR:
158 			if (qflag && total == 0)
159 				continue;
160 			if (interface != 0 && strcmp(name, interface) != 0)
161 				continue;
162 
163 			ifam = (struct ifa_msghdr *)next;
164 			if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
165 			    RTA_BRD)) == 0)
166 				break;
167 
168 			sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
169 			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
170 
171 			printf("%-7s %-5ld ", name, ifd->ifi_mtu);
172 			print_addr(rti_info[RTAX_IFA], rti_info, ifd);
173 			break;
174 		}
175 	}
176 	free(buf);
177 }
178 
179 static void
180 print_addr(struct sockaddr *sa, struct sockaddr **rtinfo, struct if_data *ifd)
181 {
182 	struct sockaddr_dl *sdl;
183 	struct sockaddr_in *sin;
184 	struct sockaddr_in6 *sin6;
185 	char *cp;
186 	int m, n;
187 
188 	switch (sa->sa_family) {
189 	case AF_UNSPEC:
190 		printf("%-11.11s ", "none");
191 		printf("%-17.17s ", "none");
192 		break;
193 	case AF_INET:
194 		sin = (struct sockaddr_in *)sa;
195 		cp = netname4(sin->sin_addr.s_addr,
196 		    ((struct sockaddr_in *)rtinfo[RTAX_NETMASK])->sin_addr.s_addr);
197 		if (vflag)
198 			n = strlen(cp) < 11 ? 11 : strlen(cp);
199 		else
200 			n = 11;
201 		printf("%-*.*s ", n, n, cp);
202 		cp = routename4(sin->sin_addr.s_addr);
203 		if (vflag)
204 			n = strlen(cp) < 17 ? 17 : strlen(cp);
205 		else
206 			n = 17;
207 		printf("%-*.*s ", n, n, cp);
208 
209 #if 0
210 		if (aflag) {
211 			u_long multiaddr;
212 			struct in_multi inm;
213 
214 			multiaddr = (u_long)LIST_FIRST(&ifaddr.in.ia_multiaddrs);
215 			while (multiaddr != 0) {
216 				kread(multiaddr, &inm, sizeof inm);
217 				printf("\n%25s %-17.17s ", "",
218 				    routename4(inm.inm_addr.s_addr));
219 				multiaddr = (u_long)LIST_NEXT(&inm, inm_list);
220 			}
221 		}
222 #endif
223 		break;
224 	case AF_INET6:
225 		sin6 = (struct sockaddr_in6 *)sa;
226 #ifdef __KAME__
227 		if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
228 			sin6->sin6_scope_id =
229 			    ntohs(*(u_int16_t *)
230 			    &sin6->sin6_addr.s6_addr[2]);
231 			sin6->sin6_addr.s6_addr[2] = 0;
232 			sin6->sin6_addr.s6_addr[3] = 0;
233 		}
234 #endif
235 		cp = netname6(sin6,
236 		    (struct sockaddr_in6 *)rtinfo[RTAX_NETMASK]);
237 		if (vflag)
238 			n = strlen(cp) < 11 ? 11 : strlen(cp);
239 		else
240 			n = 11;
241 		printf("%-*.*s ", n, n, cp);
242 		cp = routename6(sin6);
243 		if (vflag)
244 			n = strlen(cp) < 17 ? 17 : strlen(cp);
245 		else
246 			n = 17;
247 		printf("%-*.*s ", n, n, cp);
248 #if 0
249 		if (aflag) {
250 			u_long multiaddr;
251 			struct in6_multi inm;
252 			struct sockaddr_in6 m6;
253 
254 			multiaddr = (u_long)LIST_FIRST(&ifaddr.in6.ia6_multiaddrs);
255 			while (multiaddr != 0) {
256 				kread(multiaddr, &inm, sizeof inm);
257 				memset(&m6, 0, sizeof(m6));
258 				m6.sin6_len = sizeof(struct sockaddr_in6);
259 				m6.sin6_family = AF_INET6;
260 				m6.sin6_addr = inm.in6m_addr;
261 #ifdef __KAME__
262 				if (IN6_IS_ADDR_MC_LINKLOCAL(&m6.sin6_addr) ||
263 				    IN6_IS_ADDR_MC_INTFACELOCAL(&m6.sin6_addr)) {
264 					m6.sin6_scope_id =
265 					    ntohs(*(u_int16_t *)
266 					    &m6.sin6_addr.s6_addr[2]);
267 					m6.sin6_addr.s6_addr[2] = 0;
268 					m6.sin6_addr.s6_addr[3] = 0;
269 				}
270 #endif
271 				cp = routename6(&m6);
272 				if (vflag)
273 					n = strlen(cp) < 17 ? 17 : strlen(cp);
274 				else
275 					n = 17;
276 				printf("\n%25s %-*.*s ", "",
277 				    n, n, cp);
278 				multiaddr = (u_long)LIST_NEXT(&inm, in6m_entry);
279 			}
280 		}
281 #endif
282 		break;
283 	case AF_APPLETALK:
284 		printf("atlk:%-12s",atalk_print(sa,0x10) );
285 		printf("%-12s ",atalk_print(sa,0x0b) );
286 		break;
287 	case AF_LINK:
288 		sdl = (struct sockaddr_dl *)sa;
289 		m = printf("%-11.11s ", "<Link>");
290 		if (sdl->sdl_type == IFT_ETHER ||
291 		    sdl->sdl_type == IFT_CARP ||
292 		    sdl->sdl_type == IFT_FDDI ||
293 		    sdl->sdl_type == IFT_ISO88025)
294 			printf("%-17.17s ",
295 			    ether_ntoa((struct ether_addr *)LLADDR(sdl)));
296 		else {
297 			cp = (char *)LLADDR(sdl);
298 			n = sdl->sdl_alen;
299 			goto hexprint;
300 		}
301 		break;
302 	default:
303 		m = printf("(%d)", sa->sa_family);
304 		for (cp = sa->sa_len + (char *)sa;
305 			--cp > sa->sa_data && (*cp == 0);) {}
306 		n = cp - sa->sa_data + 1;
307 		cp = sa->sa_data;
308 hexprint:
309 		while (--n >= 0)
310 			m += printf("%x%c", *cp++ & 0xff,
311 				    n > 0 ? '.' : ' ');
312 		m = 30 - m;
313 		while (m-- > 0)
314 			putchar(' ');
315 		break;
316 	}
317 	if (bflag)
318 		printf("%10llu %10llu",
319 		    ifd->ifi_ibytes, ifd->ifi_obytes);
320 	else
321 		printf("%8llu %5llu %8llu %5llu %5llu",
322 		    ifd->ifi_ipackets, ifd->ifi_ierrors,
323 		    ifd->ifi_opackets, ifd->ifi_oerrors,
324 		    ifd->ifi_collisions);
325 	if (tflag)
326 		printf(" %4d", 0 /* XXX ifnet.if_timer */);
327 	if (dflag)
328 		printf(" %4d", 0 /* XXX ifnet.if_snd.ifq_drops */);
329 	putchar('\n');
330 }
331 
332 struct	iftot {
333 	char	ift_name[IFNAMSIZ];	/* interface name */
334 	u_int64_t ift_ip;		/* input packets */
335 	u_int64_t ift_ib;		/* input bytes */
336 	u_int64_t ift_ie;		/* input errors */
337 	u_int64_t ift_op;		/* output packets */
338 	u_int64_t ift_ob;		/* output bytes */
339 	u_int64_t ift_oe;		/* output errors */
340 	u_int64_t ift_co;		/* collisions */
341 	u_int64_t ift_dr;		/* drops */
342 } ip_cur, ip_old, sum_cur, sum_old;
343 
344 volatile sig_atomic_t signalled;	/* set if alarm goes off "early" */
345 
346 /*
347  * Print a running summary of interface statistics.
348  * Repeat display every interval seconds, showing statistics
349  * collected over that interval.  Assumes that interval is non-zero.
350  * First line printed at top of screen is always cumulative.
351  */
352 static void
353 sidewaysintpr(unsigned int interval)
354 {
355 	struct ifnet ifnet;
356 	sigset_t emptyset;
357 	int line;
358 
359 	fetchifs();
360 	if (ip_cur.ift_name[0] == '\0') {
361 		fprintf(stderr, "%s: %s: unknown interface\n",
362 		    __progname, interface);
363 		exit(1);
364 	}
365 
366 	(void)signal(SIGALRM, catchalarm);
367 	signalled = 0;
368 	(void)alarm(interval);
369 banner:
370 	if (bflag)
371 		printf("%7.7s in %8.8s %6.6s out %5.5s",
372 		    ip_cur.ift_name, " ",
373 		    ip_cur.ift_name, " ");
374 	else
375 		printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s",
376 		    ip_cur.ift_name, " ",
377 		    ip_cur.ift_name, " ", " ");
378 	if (dflag)
379 		printf(" %5.5s", " ");
380 
381 	if (bflag)
382 		printf("  %7.7s in %8.8s %6.6s out %5.5s",
383 		    "total", " ", "total", " ");
384 	else
385 		printf("  %5.5s in %5.5s%5.5s out %5.5s %5.5s",
386 		    "total", " ", "total", " ", " ");
387 	if (dflag)
388 		printf(" %5.5s", " ");
389 	putchar('\n');
390 	if (bflag)
391 		printf("%10.10s %8.8s %10.10s %5.5s",
392 		    "bytes", " ", "bytes", " ");
393 	else
394 		printf("%8.8s %5.5s %8.8s %5.5s %5.5s",
395 		    "packets", "errs", "packets", "errs", "colls");
396 	if (dflag)
397 		printf(" %5.5s", "drops");
398 
399 	if (bflag)
400 		printf("  %10.10s %8.8s %10.10s %5.5s",
401 		    "bytes", " ", "bytes", " ");
402 	else
403 		printf("  %8.8s %5.5s %8.8s %5.5s %5.5s",
404 		    "packets", "errs", "packets", "errs", "colls");
405 	if (dflag)
406 		printf(" %5.5s", "drops");
407 	putchar('\n');
408 	fflush(stdout);
409 	line = 0;
410 	bzero(&ip_old, sizeof(ip_old));
411 	bzero(&sum_old, sizeof(sum_old));
412 loop:
413 	bzero(&sum_cur, sizeof(sum_cur));
414 
415 	fetchifs();
416 
417 	if (bflag)
418 		printf("%10llu %8.8s %10llu %5.5s",
419 		    ip_cur.ift_ib - ip_old.ift_ib, " ",
420 		    ip_cur.ift_ob - ip_old.ift_ob, " ");
421 	else
422 		printf("%8llu %5llu %8llu %5llu %5llu",
423 		    ip_cur.ift_ip - ip_old.ift_ip,
424 		    ip_cur.ift_ie - ip_old.ift_ie,
425 		    ip_cur.ift_op - ip_old.ift_op,
426 		    ip_cur.ift_oe - ip_old.ift_oe,
427 		    ip_cur.ift_co - ip_old.ift_co);
428 	if (dflag)
429 		printf(" %5llu",
430 		    /* XXX ifnet.if_snd.ifq_drops - ip->ift_dr); */
431 		    0);
432 
433 	ip_old = ip_cur;
434 
435 	if (bflag)
436 		printf("  %10llu %8.8s %10llu %5.5s",
437 		    sum_cur.ift_ib - sum_old.ift_ib, " ",
438 		    sum_cur.ift_ob - sum_old.ift_ob, " ");
439 	else
440 		printf("  %8llu %5llu %8llu %5llu %5llu",
441 		    sum_cur.ift_ip - sum_old.ift_ip,
442 		    sum_cur.ift_ie - sum_old.ift_ie,
443 		    sum_cur.ift_op - sum_old.ift_op,
444 		    sum_cur.ift_oe - sum_old.ift_oe,
445 		    sum_cur.ift_co - sum_old.ift_co);
446 	if (dflag)
447 		printf(" %5llu", sum_cur.ift_dr - sum_old.ift_dr);
448 
449 	sum_old = sum_cur;
450 
451 	putchar('\n');
452 	fflush(stdout);
453 	line++;
454 	sigemptyset(&emptyset);
455 	if (!signalled)
456 		sigsuspend(&emptyset);
457 	signalled = 0;
458 	(void)alarm(interval);
459 	if (line == 21)
460 		goto banner;
461 	goto loop;
462 	/*NOTREACHED*/
463 }
464 
465 /*
466  * Called if an interval expires before sidewaysintpr has completed a loop.
467  * Sets a flag to not wait for the alarm.
468  */
469 /* ARGSUSED */
470 static void
471 catchalarm(int signo)
472 {
473 	signalled = 1;
474 }
475 
476 static void
477 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
478 {
479 	int i;
480 
481 	for (i = 0; i < RTAX_MAX; i++) {
482 		if (addrs & (1 << i)) {
483 			rti_info[i] = sa;
484 			sa = (struct sockaddr *)((char *)(sa) +
485 			    roundup(sa->sa_len, sizeof(long)));
486 		} else
487 			rti_info[i] = NULL;
488 	}
489 }
490 
491 static void
492 fetchifs(void)
493 {
494 	struct if_msghdr ifm;
495 	int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
496 	struct rt_msghdr *rtm;
497 	struct if_data *ifd;
498 	struct sockaddr *sa, *rti_info[RTAX_MAX];
499 	struct sockaddr_dl *sdl;
500 	char *buf, *next, *lim;
501 	char name[IFNAMSIZ];
502 	size_t len;
503 
504 	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
505 		err(1, "sysctl");
506 	if ((buf = malloc(len)) == NULL)
507 		err(1, NULL);
508 	if (sysctl(mib, 6, buf, &len, NULL, 0) == -1)
509 		err(1, "sysctl");
510 
511 	lim = buf + len;
512 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
513 		rtm = (struct rt_msghdr *)next;
514 		if (rtm->rtm_version != RTM_VERSION)
515 			continue;
516 		switch (rtm->rtm_type) {
517 		case RTM_IFINFO:
518 			bcopy(next, &ifm, sizeof ifm);
519 			ifd = &ifm.ifm_data;
520 
521 			sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
522 			get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
523 
524 			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
525 			if (sdl == NULL || sdl->sdl_family != AF_LINK)
526 				continue;
527 			bzero(name, sizeof(name));
528 			if (sdl->sdl_nlen >= IFNAMSIZ)
529 				memcpy(name, sdl->sdl_data, IFNAMSIZ - 1);
530 			else if (sdl->sdl_nlen > 0)
531 				memcpy(name, sdl->sdl_data, sdl->sdl_nlen);
532 
533 			if (interface != NULL && !strcmp(name, interface)) {
534 				strlcpy(ip_cur.ift_name, name,
535 				    sizeof(ip_cur.ift_name));
536 				ip_cur.ift_ip = ifd->ifi_ipackets;
537 				ip_cur.ift_ib = ifd->ifi_ibytes;
538 				ip_cur.ift_ie = ifd->ifi_ierrors;
539 				ip_cur.ift_op = ifd->ifi_opackets;
540 				ip_cur.ift_ob = ifd->ifi_obytes;
541 				ip_cur.ift_oe = ifd->ifi_oerrors;
542 				ip_cur.ift_co = ifd->ifi_collisions;
543 				ip_cur.ift_dr = 0;
544 				    /* XXX ifnet.if_snd.ifq_drops */
545 			}
546 
547 			sum_cur.ift_ip += ifd->ifi_ipackets;
548 			sum_cur.ift_ib += ifd->ifi_ibytes;
549 			sum_cur.ift_ie += ifd->ifi_ierrors;
550 			sum_cur.ift_op += ifd->ifi_opackets;
551 			sum_cur.ift_ob += ifd->ifi_obytes;
552 			sum_cur.ift_oe += ifd->ifi_oerrors;
553 			sum_cur.ift_co += ifd->ifi_collisions;
554 			sum_cur.ift_dr += 0; /* XXX ifnet.if_snd.ifq_drops */
555 			break;
556 		}
557 	}
558 	if (interface == NULL) {
559 		strlcpy(ip_cur.ift_name, name,
560 		    sizeof(ip_cur.ift_name));
561 		ip_cur.ift_ip = ifd->ifi_ipackets;
562 		ip_cur.ift_ib = ifd->ifi_ibytes;
563 		ip_cur.ift_ie = ifd->ifi_ierrors;
564 		ip_cur.ift_op = ifd->ifi_opackets;
565 		ip_cur.ift_ob = ifd->ifi_obytes;
566 		ip_cur.ift_oe = ifd->ifi_oerrors;
567 		ip_cur.ift_co = ifd->ifi_collisions;
568 		ip_cur.ift_dr = 0;
569 		    /* XXX ifnet.if_snd.ifq_drops */
570 	}
571 	free(buf);
572 }
573