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