natm.c revision 118824
1/*
2 * Copyright (c) 2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 */
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sbin/atm/atmconfig/natm.c 118824 2003-08-12 14:25:57Z harti $");
31
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/sysctl.h>
35#include <net/if.h>
36#include <net/if_var.h>
37#include <net/if_mib.h>
38#include <net/if_atm.h>
39#include <net/if_dl.h>
40#include <net/route.h>
41#include <netinet/in.h>
42#include <arpa/inet.h>
43#include <netdb.h>
44#include "atmconfig.h"
45#include "private.h"
46#include "diag.h"
47
48static void natm_add(int, char *[]);
49static void natm_delete(int, char *[]);
50static void natm_show(int, char *[]);
51
52const struct cmdtab natm_tab[] = {
53	{ "add",	NULL,		natm_add },
54	{ "delete",	NULL,		natm_delete },
55	{ "show",	NULL,		natm_show },
56	{ NULL, 	NULL, 		NULL }
57};
58
59/*
60 * Structure to hold a route
61 */
62struct natm_route {
63	TAILQ_ENTRY(natm_route) link;
64	struct in_addr	host;
65	struct diagif	*aif;
66	u_int		flags;
67	int		llcsnap;
68	u_int		vpi, vci;
69	u_int		traffic;
70	u_int		pcr, scr, mbs, icr, mcr;
71	u_int		tbe, nrm, trm, adtf, rif, rdf, cdf;
72};
73static TAILQ_HEAD(, natm_route) natm_route_list =
74    TAILQ_HEAD_INITIALIZER(natm_route_list);
75
76static void
77store_route(struct rt_msghdr *rtm)
78{
79	u_int i;
80	struct natm_route *r;
81	char *cp;
82	struct sockaddr *sa;
83	struct sockaddr_in *sain;
84	struct sockaddr_dl *sdl;
85	struct diagif *aif;
86	u_int n;
87
88	r = malloc(sizeof(*r));
89	if (r == NULL)
90		err(1, "allocate route");
91
92	r->flags = rtm->rtm_flags;
93	cp = (char *)(rtm + 1);
94	for (i = 1; i != 0; i <<= 1) {
95		if (rtm->rtm_addrs & i) {
96			sa = (struct sockaddr *)cp;
97			cp += roundup(sa->sa_len, sizeof(long));
98			switch (i) {
99
100			  case RTA_DST:
101				if (sa->sa_family != AF_INET) {
102					warnx("RTA_DST not AF_INET %u", sa->sa_family);
103					goto fail;
104				}
105				sain = (struct sockaddr_in *)(void *)sa;
106				if (sain->sin_len < 4)
107					r->host.s_addr = INADDR_ANY;
108				else
109					r->host = sain->sin_addr;
110				break;
111
112			  case RTA_GATEWAY:
113				if (sa->sa_family != AF_LINK) {
114					warnx("RTA_GATEWAY not AF_LINK");
115					goto fail;
116				}
117				sdl = (struct sockaddr_dl *)(void *)sa;
118				TAILQ_FOREACH(aif, &diagif_list, link)
119					if (strlen(aif->ifname) ==
120					    sdl->sdl_nlen &&
121					    strncmp(aif->ifname, sdl->sdl_data,
122					    sdl->sdl_nlen) == 0)
123						break;
124				if (aif == NULL) {
125					warnx("interface '%.*s' not found",
126					    sdl->sdl_nlen, sdl->sdl_data);
127					goto fail;
128				}
129				r->aif = aif;
130
131				/* parse ATM stuff */
132
133#define	GET3()	(((sdl->sdl_data[n] & 0xff) << 16) |	\
134		 ((sdl->sdl_data[n + 1] & 0xff) << 8) |	\
135		 ((sdl->sdl_data[n + 2] & 0xff) << 0))
136#define	GET2()	(((sdl->sdl_data[n] & 0xff) << 8) |	\
137		 ((sdl->sdl_data[n + 1] & 0xff) << 0))
138#define	GET1()	(((sdl->sdl_data[n] & 0xff) << 0))
139
140				n = sdl->sdl_nlen;
141				if (sdl->sdl_alen < 4) {
142					warnx("RTA_GATEWAY alen too short");
143					goto fail;
144				}
145				r->llcsnap = GET1() & ATM_PH_LLCSNAP;
146				n++;
147				r->vpi = GET1();
148				n++;
149				r->vci = GET2();
150				n += 2;
151				if (sdl->sdl_alen == 4) {
152					/* old address */
153					r->traffic = ATMIO_TRAFFIC_UBR;
154					r->pcr = 0;
155					break;
156				}
157				/* new address */
158				r->traffic = GET1();
159				n++;
160				switch (r->traffic) {
161
162				  case ATMIO_TRAFFIC_UBR:
163					if (sdl->sdl_alen >= 5 + 3) {
164						r->pcr = GET3();
165						n += 3;
166					} else
167						r->pcr = 0;
168					break;
169
170				  case ATMIO_TRAFFIC_CBR:
171					if (sdl->sdl_alen < 5 + 3) {
172						warnx("CBR address too short");
173						goto fail;
174					}
175					r->pcr = GET3();
176					n += 3;
177					break;
178
179				  case ATMIO_TRAFFIC_VBR:
180					if (sdl->sdl_alen < 5 + 3 * 3) {
181						warnx("VBR address too short");
182						goto fail;
183					}
184					r->pcr = GET3();
185					n += 3;
186					r->scr = GET3();
187					n += 3;
188					r->mbs = GET3();
189					n += 3;
190					break;
191
192				  case ATMIO_TRAFFIC_ABR:
193					if (sdl->sdl_alen < 5 + 4 * 3 + 2 +
194					    1 * 2 + 3) {
195						warnx("ABR address too short");
196						goto fail;
197					}
198					r->pcr = GET3();
199					n += 3;
200					r->mcr = GET3();
201					n += 3;
202					r->icr = GET3();
203					n += 3;
204					r->tbe = GET3();
205					n += 3;
206					r->nrm = GET1();
207					n++;
208					r->trm = GET1();
209					n++;
210					r->adtf = GET2();
211					n += 2;
212					r->rif = GET1();
213					n++;
214					r->rdf = GET1();
215					n++;
216					r->cdf = GET1();
217					n++;
218					break;
219
220				  default:
221					goto fail;
222				}
223				break;
224			}
225		}
226	}
227
228	TAILQ_INSERT_TAIL(&natm_route_list, r, link);
229
230	return;
231  fail:
232	free(r);
233}
234
235/*
236 * Fetch the INET routes that a ours
237 */
238static void
239natm_route_fetch(void)
240{
241	int name[6];
242	size_t needed;
243	u_char *buf, *next;
244	struct rt_msghdr *rtm;
245
246	name[0] = CTL_NET;
247	name[1] = PF_ROUTE;
248	name[2] = 0;
249	name[3] = AF_INET;
250	name[4] = NET_RT_DUMP;
251	name[5] = 0;
252
253	if (sysctl(name, 6, NULL, &needed, NULL, 0) == -1)
254		err(1, "rtable estimate");
255	needed *= 2;
256	if ((buf = malloc(needed)) == NULL)
257		err(1, "rtable buffer (%zu)", needed);
258	if (sysctl(name, 6, buf, &needed, NULL, 0) == -1)
259		err(1, "rtable get");
260
261	next = buf;
262	while (next < buf + needed) {
263		rtm = (struct rt_msghdr *)(void *)next;
264		next += rtm->rtm_msglen;
265
266		if (rtm->rtm_type == RTM_GET) {
267			if ((rtm->rtm_flags & (RTF_UP | RTF_HOST |
268			    RTF_STATIC)) == (RTF_UP | RTF_HOST | RTF_STATIC) &&
269			    (rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY |
270			    RTA_IFP)) == (RTA_DST | RTA_GATEWAY | RTA_IFP))
271				store_route(rtm);
272		}
273	}
274}
275
276static u_long
277parse_num(const char *arg, const char *name, u_long limit)
278{
279	u_long res;
280	char *end;
281
282	errno = 0;
283	res = strtoul(arg, &end, 10);
284	if (*end != '\0' || end == arg || errno != 0)
285		errx(1, "cannot parse %s '%s'", name, arg);
286	if (res > limit)
287		errx(1, "%s out of range (0...%lu)", name, limit);
288	return (res);
289}
290
291static void
292do_route(u_int type, u_int flags, const struct sockaddr_in *sain,
293    const struct sockaddr_dl *sdl)
294{
295	struct {
296		struct rt_msghdr h;
297		char	space[512];
298	} msg;
299	char *ptr;
300	int s;
301	ssize_t rlen;
302
303	/* create routing message */
304	bzero(&msg, sizeof(msg));
305	msg.h.rtm_msglen = sizeof(msg.h);
306	msg.h.rtm_version = RTM_VERSION;
307	msg.h.rtm_type = type;
308	msg.h.rtm_index = 0;
309	msg.h.rtm_flags = flags;
310	msg.h.rtm_addrs = RTA_DST | (sdl != NULL ? RTA_GATEWAY : 0);
311	msg.h.rtm_pid = getpid();
312
313	ptr = (char *)&msg + sizeof(msg.h);
314	memcpy(ptr, sain, sain->sin_len);
315	ptr += roundup(sain->sin_len, sizeof(long));
316	msg.h.rtm_msglen += roundup(sain->sin_len, sizeof(long));
317
318	if (sdl != NULL) {
319		memcpy(ptr, sdl, sdl->sdl_len);
320		ptr += roundup(sdl->sdl_len, sizeof(long));
321		msg.h.rtm_msglen += roundup(sdl->sdl_len, sizeof(long));
322	}
323
324	/* open socket */
325	s = socket(PF_ROUTE, SOCK_RAW, AF_INET);
326	if (s == -1)
327		err(1, "cannot open routing socket");
328
329	rlen = write(s, &msg, msg.h.rtm_msglen);
330	if (rlen == -1)
331		err(1, "writing to routing socket");
332	if ((size_t)rlen != msg.h.rtm_msglen)
333		errx(1, "short write to routing socket: %zu %u",
334		    (size_t)rlen, msg.h.rtm_msglen);
335	close(s);
336}
337
338/*
339 * Add a new NATM route
340 */
341static void
342natm_add(int argc, char *argv[])
343{
344	int opt;
345	struct hostent *hp;
346	struct sockaddr_in sain;
347	struct sockaddr_dl sdl;
348	struct diagif *aif;
349	u_long num, num1;
350	u_int idx;
351
352	static int printonly;
353
354	static const struct option opts[] = {
355	    { "printonly", OPT_SIMPLE, &printonly },
356	    { NULL, 0, NULL }
357	};
358
359	while ((opt = parse_options(&argc, &argv, opts)) != -1)
360		switch (opt) {
361		}
362
363	if (argc < 5)
364		errx(1, "missing arguments for 'natm add'");
365
366	memset(&sdl, 0, sizeof(sdl));
367	sdl.sdl_len = sizeof(sdl);
368	sdl.sdl_family = AF_LINK;
369
370	/* get the IP address for <dest> */
371	memset(&sain, 0, sizeof(sain));
372	hp = gethostbyname(argv[0]);
373	if (hp == NULL)
374		errx(1, "bad hostname %s: %s", argv[0], hstrerror(h_errno));
375	if (hp->h_addrtype != AF_INET)
376		errx(1, "bad address type for %s", argv[0]);
377	sain.sin_len = sizeof(sain);
378	sain.sin_family = AF_INET;
379	memcpy(&sain.sin_addr, hp->h_addr, sizeof(sain.sin_addr));
380
381	/* find interface */
382	diagif_fetch();
383	TAILQ_FOREACH(aif, &diagif_list, link)
384		if (strcmp(aif->ifname, argv[1]) == 0)
385			break;
386	if (aif == NULL)
387		errx(1, "unknown ATM interface '%s'", argv[1]);
388	sdl.sdl_index = aif->index;
389	strcpy(sdl.sdl_data, aif->ifname);
390	idx = sdl.sdl_nlen = strlen(aif->ifname);
391	idx++;
392
393	/* verify VPI/VCI */
394	num = parse_num(argv[2], "VPI", (1U << aif->mib.vpi_bits));
395	sdl.sdl_data[idx++] = num & 0xff;
396	num = parse_num(argv[3], "VCI", (1U << aif->mib.vci_bits));
397	if (num == 0)
398		errx(1, "VCI may not be 0");
399	sdl.sdl_data[idx++] = (num >> 8) & 0xff;
400	sdl.sdl_data[idx++] = num & 0xff;
401
402	/* encapsulation */
403	if (strcasecmp(argv[4], "llc/snap") == 0) {
404		sdl.sdl_data[sdl.sdl_nlen] = ATM_PH_LLCSNAP;
405	} else if (strcasecmp(argv[4], "aal5") == 0) {
406		sdl.sdl_data[sdl.sdl_nlen] = 0;
407	} else
408		errx(1, "bad encapsulation type '%s'", argv[4]);
409
410	/* look at the traffic */
411	argc -= 5;
412	argv += 5;
413
414	if (argc != 0) {
415		if (strcasecmp(argv[0], "ubr") == 0) {
416			sdl.sdl_data[idx++] = ATMIO_TRAFFIC_UBR;
417			if (argc == 1)
418				/* ok */;
419			else if (argc == 2) {
420				num = parse_num(argv[1], "PCR", aif->mib.pcr);
421				sdl.sdl_data[idx++] = (num >> 16) & 0xff;
422				sdl.sdl_data[idx++] = (num >>  8) & 0xff;
423				sdl.sdl_data[idx++] = (num >>  0) & 0xff;
424			} else
425				errx(1, "too many parameters for UBR");
426
427		} else if (strcasecmp(argv[0], "cbr") == 0) {
428			sdl.sdl_data[idx++] = ATMIO_TRAFFIC_CBR;
429			if (argc == 1)
430				errx(1, "missing PCR for CBR");
431			if (argc > 2)
432				errx(1, "too many parameters for CBR");
433			num = parse_num(argv[1], "PCR", aif->mib.pcr);
434			sdl.sdl_data[idx++] = (num >> 16) & 0xff;
435			sdl.sdl_data[idx++] = (num >>  8) & 0xff;
436			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
437
438		} else if (strcasecmp(argv[0], "vbr") == 0) {
439			sdl.sdl_data[idx++] = ATMIO_TRAFFIC_VBR;
440
441			if (argc < 4)
442				errx(1, "missing arg(s) for VBR");
443			if (argc > 4)
444				errx(1, "too many parameters for VBR");
445
446			num = parse_num(argv[1], "PCR", aif->mib.pcr);
447			sdl.sdl_data[idx++] = (num >> 16) & 0xff;
448			sdl.sdl_data[idx++] = (num >>  8) & 0xff;
449			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
450			num = parse_num(argv[2], "SCR", num);
451			sdl.sdl_data[idx++] = (num >> 16) & 0xff;
452			sdl.sdl_data[idx++] = (num >>  8) & 0xff;
453			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
454			num = parse_num(argv[3], "MBS", 0xffffffLU);
455			sdl.sdl_data[idx++] = (num >> 16) & 0xff;
456			sdl.sdl_data[idx++] = (num >>  8) & 0xff;
457			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
458
459		} else if (strcasecmp(argv[0], "abr") == 0) {
460			sdl.sdl_data[idx++] = ATMIO_TRAFFIC_ABR;
461			if (argc < 11)
462				errx(1, "missing arg(s) for ABR");
463			if (argc > 11)
464				errx(1, "too many parameters for ABR");
465
466			num = parse_num(argv[1], "PCR", aif->mib.pcr);
467			sdl.sdl_data[idx++] = (num >> 16) & 0xff;
468			sdl.sdl_data[idx++] = (num >>  8) & 0xff;
469			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
470
471			num1 = parse_num(argv[2], "MCR", num);
472			sdl.sdl_data[idx++] = (num1 >> 16) & 0xff;
473			sdl.sdl_data[idx++] = (num1 >>  8) & 0xff;
474			sdl.sdl_data[idx++] = (num1 >>  0) & 0xff;
475
476			num = parse_num(argv[3], "ICR", num);
477			sdl.sdl_data[idx++] = (num >> 16) & 0xff;
478			sdl.sdl_data[idx++] = (num >>  8) & 0xff;
479			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
480
481			if (num < num1)
482				errx(1, "ICR must be >= MCR");
483
484			num = parse_num(argv[4], "TBE", 0xffffffUL);
485			sdl.sdl_data[idx++] = (num >> 16) & 0xff;
486			sdl.sdl_data[idx++] = (num >>  8) & 0xff;
487			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
488
489			num = parse_num(argv[5], "NRM", 0x7UL);
490			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
491
492			num = parse_num(argv[6], "TRM", 0x7UL);
493			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
494
495			num = parse_num(argv[7], "ADTF", 0x3ffUL);
496			sdl.sdl_data[idx++] = (num >>  8) & 0xff;
497			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
498
499			num = parse_num(argv[8], "RIF", 0xfUL);
500			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
501
502			num = parse_num(argv[9], "RDF", 0xfUL);
503			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
504
505			num = parse_num(argv[10], "CDF", 0x7UL);
506			sdl.sdl_data[idx++] = (num >>  0) & 0xff;
507
508		} else
509			errx(1, "bad traffic type '%s'", argv[0]);
510	} else
511		sdl.sdl_data[idx++] = ATMIO_TRAFFIC_UBR;
512
513	sdl.sdl_alen = idx - sdl.sdl_nlen;
514	sdl.sdl_len += sdl.sdl_nlen + sdl.sdl_alen;
515
516	if (printonly) {
517		printf("route add -iface %s -link %.*s",
518		    inet_ntoa(sain.sin_addr), sdl.sdl_nlen, sdl.sdl_data);
519		for (idx = 0; idx < sdl.sdl_alen; idx++)
520			printf("%c%x", ".:"[idx == 0],
521			    (u_int)sdl.sdl_data[sdl.sdl_nlen + idx] & 0xffU);
522		printf("\n");
523		exit(0);
524	}
525
526	do_route(RTM_ADD, RTF_HOST | RTF_STATIC | RTF_UP, &sain, &sdl);
527}
528
529/*
530 * Delete an NATM route
531 */
532static void
533natm_delete(int argc, char *argv[])
534{
535	int opt;
536	struct hostent *hp;
537	struct sockaddr_in sain;
538	u_int vpi, vci;
539	struct diagif *aif;
540	struct natm_route *r;
541
542	static int printonly;
543
544	static const struct option opts[] = {
545	    { "printonly", OPT_SIMPLE, &printonly },
546	    { NULL, 0, NULL }
547	};
548
549	while ((opt = parse_options(&argc, &argv, opts)) != -1)
550		switch (opt) {
551		}
552
553	diagif_fetch();
554	natm_route_fetch();
555
556	memset(&sain, 0, sizeof(sain));
557	sain.sin_len = sizeof(sain);
558	sain.sin_family = AF_INET;
559
560	if (argc == 1) {
561		/* get the IP address for <dest> */
562		hp = gethostbyname(argv[0]);
563		if (hp == NULL)
564			errx(1, "bad hostname %s: %s", argv[0],
565			    hstrerror(h_errno));
566		if (hp->h_addrtype != AF_INET)
567			errx(1, "bad address type for %s", argv[0]);
568		memcpy(&sain.sin_addr, hp->h_addr, sizeof(sain.sin_addr));
569
570		TAILQ_FOREACH(r, &natm_route_list, link)
571			if (r->host.s_addr == sain.sin_addr.s_addr)
572				break;
573		if (r == NULL)
574			errx(1, "no NATM route to host '%s' (%s)", argv[0],
575			    inet_ntoa(sain.sin_addr));
576
577	} else if (argc == 3) {
578		TAILQ_FOREACH(aif, &diagif_list, link)
579			if (strcmp(aif->ifname, argv[0]) == 0)
580				break;
581		if (aif == 0)
582			errx(1, "no such interface '%s'", argv[0]);
583
584		vpi = parse_num(argv[1], "VPI", 0xff);
585		vci = parse_num(argv[2], "VCI", 0xffff);
586
587		TAILQ_FOREACH(r, &natm_route_list, link)
588			if (r->aif == aif && r->vpi == vpi && r->vci == vci)
589				break;
590		if (r == NULL)
591			errx(1, "no such NATM route %s %u %u", argv[0],
592			    vpi, vci);
593		sain.sin_addr = r->host;
594
595	} else
596		errx(1, "bad number of arguments for 'natm delete'");
597
598	if (printonly) {
599		printf("route delete %s\n", inet_ntoa(r->host));
600		exit(0);
601	}
602
603	do_route(RTM_DELETE, r->flags, &sain, NULL);
604}
605
606/*
607 * Show NATM routes
608 */
609static void
610natm_show(int argc, char *argv[])
611{
612	int opt;
613	struct natm_route *r;
614	struct hostent *hp;
615
616	static const char *const traffics[] = {
617		[ATMIO_TRAFFIC_UBR] = "UBR",
618		[ATMIO_TRAFFIC_CBR] = "CBR",
619		[ATMIO_TRAFFIC_VBR] = "VBR",
620		[ATMIO_TRAFFIC_ABR] = "ABR"
621	};
622
623	static int numeric, abr;
624
625	static const struct option opts[] = {
626	    { "abr", OPT_SIMPLE, &abr },
627	    { "numeric", OPT_SIMPLE, &numeric },
628	    { NULL, 0, NULL }
629	};
630
631	static const char head[] =
632	    "Destination         Iface       VPI VCI   Encaps   Trf PCR     "
633	    "SCR/MCR MBS/ICR\n";
634	static const char head_abr[] =
635	    "Destination         Iface       VPI VCI   Encaps   Trf PCR     "
636	    "SCR/MCR MBS/ICR TBE     NRM TRM ADTF RIF RDF CDF\n";
637
638	while ((opt = parse_options(&argc, &argv, opts)) != -1)
639		switch (opt) {
640		}
641
642	diagif_fetch();
643	natm_route_fetch();
644
645	heading_init();
646	TAILQ_FOREACH(r, &natm_route_list, link) {
647		heading(abr ? head_abr : head);
648		if (numeric)
649			printf("%-20s", inet_ntoa(r->host));
650		else if (r->host.s_addr == INADDR_ANY)
651			printf("%-20s", "default");
652		else {
653			hp = gethostbyaddr((char *)&r->host, sizeof(r->host),
654			    AF_INET);
655			if (hp != NULL)
656				printf("%-20s", hp->h_name);
657			else
658				printf("%-20s", inet_ntoa(r->host));
659		}
660		printf("%-12s%-4u%-6u%-9s%-4s", r->aif->ifname, r->vpi, r->vci,
661		    r->llcsnap ? "LLC/SNAP" : "AAL5", traffics[r->traffic]);
662		switch (r->traffic) {
663
664		  case ATMIO_TRAFFIC_UBR:
665		  case ATMIO_TRAFFIC_CBR:
666			printf("%-8u", r->pcr);
667			break;
668
669		  case ATMIO_TRAFFIC_VBR:
670			printf("%-8u%-8u%-8u", r->pcr, r->scr, r->mbs);
671			break;
672
673		  case ATMIO_TRAFFIC_ABR:
674			printf("%-8u%-8u%-8u", r->pcr, r->mcr, r->icr);
675			if (abr)
676				printf("%-8u%-4u%-4u%-5u%-4u%-4u%-4u",
677				    r->tbe, r->nrm, r->trm, r->adtf,
678				    r->rif, r->rdf, r->cdf);
679			break;
680		}
681		printf("\n");
682	}
683}
684