FreeBSD-5.3/sbin/atm/atm/atm_show.c

Compare this file to the similar file:
Show the results in this format:

/*
 *
 * ===================================
 * HARP  |  Host ATM Research Platform
 * ===================================
 *
 *
 * This Host ATM Research Platform ("HARP") file (the "Software") is
 * made available by Network Computing Services, Inc. ("NetworkCS")
 * "AS IS".  NetworkCS does not provide maintenance, improvements or
 * support of any kind.
 *
 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
 * In no event shall NetworkCS be responsible for any damages, including
 * but not limited to consequential damages, arising from or relating to
 * any use of the Software or related support.
 *
 * Copyright 1994-1998 Network Computing Services, Inc.
 *
 * Copies of this Software may be made, however, the above copyright
 * notice must be reproduced on all copies.
 */

/*
 * User configuration and display program
 * --------------------------------------
 *
 * Routines for "show" subcommand
 *
 */

#include <sys/param.h>  
#include <sys/socket.h> 
#include <net/if.h>
#include <netinet/in.h>
#include <netatm/port.h>
#include <netatm/atm.h>
#include <netatm/atm_if.h> 
#include <netatm/atm_sap.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_vc.h>
#include <netatm/atm_ioctl.h>

#include <errno.h>
#include <libatm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "atm.h"

#ifndef lint
__RCSID("@(#) $FreeBSD: src/sbin/atm/atm/atm_show.c,v 1.10 2004/02/10 20:48:08 cperciva Exp $");
#endif


/*
 * Local functions
 */
static int	vcc_compare(const void *, const void *);
static int	ip_vcc_compare(const void *, const void *);
static int	arp_compare(const void *, const void *);


/*
 * Process show ARP command
 * 
 * Command format: 
 *	atm show ARP [<ip-addr>]
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_arp(int argc, char **argv, const struct cmd *cmdp __unused)
{
	size_t arp_info_len;
	struct atminfreq	air;
	struct air_arp_rsp	*arp_info, *arp_info_base;
	struct sockaddr_in	*sain;
	union {
		struct sockaddr_in	sain;
		struct sockaddr		sa;
	} host_addr;

	/*
	 * Get IP address of specified host name
	 */
	bzero(&host_addr, sizeof(host_addr));
	host_addr.sa.sa_family = AF_INET;
	if (argc) {
		sain = get_ip_addr(argv[0]);
		if (!sain) {
			fprintf(stderr, "%s: host \'%s\' not found\n",
					prog, argv[0]);
			exit(1);
		}
		host_addr.sain.sin_addr.s_addr = sain->sin_addr.s_addr;
	} else {
		host_addr.sain.sin_addr.s_addr = INADDR_ANY;
	}

	/*
	 * Get ARP information from the kernel
	 */
	bzero(&air, sizeof(air));
	air.air_opcode = AIOCS_INF_ARP;
	air.air_arp_addr = host_addr.sa;
	arp_info_len = do_info_ioctl(&air, sizeof(struct air_arp_rsp) * 10);
	if ((ssize_t)arp_info_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "not an ATM device\n");
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}
	arp_info_base = arp_info =
			(struct air_arp_rsp *) air.air_buf_addr;

	/*
	 * Sort the ARP table
	 */
	qsort((void *) arp_info,
			arp_info_len / sizeof(struct air_arp_rsp),
			sizeof(struct air_arp_rsp),
			arp_compare);

	/*
	 * Print the relevant information
	 */
	while (arp_info_len >= sizeof(struct air_arp_rsp)) {
		print_arp_info(arp_info);
		arp_info++;
		arp_info_len -= sizeof(struct air_arp_rsp);
	}

	/*
	 * Release the information from the kernel
	 */
	free(arp_info_base);
}


/*
 * Process show ATM ARP server command
 * 
 * Command format: 
 *	atm show arpserver [<interface-name>]
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_arpserv(int argc, char **argv, const struct cmd *cmdp __unused)
{
	size_t buf_len, asrv_info_len;
	struct atminfreq	air;
	struct air_asrv_rsp	*asrv_info, *asrv_info_base;

	/*
	 * Validate interface name
	 */
	bzero(air.air_int_intf, sizeof(air.air_int_intf));
	if (argc) {
		if (strlen(argv[0]) > IFNAMSIZ - 1) {
			fprintf(stderr, "%s: Illegal interface name\n",
					prog);
			exit(1);
		}
		strcpy(air.air_int_intf, argv[0]);
		argc--; argv++;
	}

	/*
	 * Get interface information from the kernel
	 */
	air.air_opcode = AIOCS_INF_ASV;
	buf_len = do_info_ioctl(&air, sizeof(struct air_asrv_rsp) * 3);
	if ((ssize_t)buf_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "%s is not an ATM device\n",
					argv[0]);
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}

	/*
	 * Print the interface information
	 */
	asrv_info_base = asrv_info =
			(struct air_asrv_rsp *)(void *)air.air_buf_addr;
	while (buf_len >= sizeof(struct air_asrv_rsp)) {
		print_asrv_info(asrv_info);
		asrv_info_len = sizeof(struct air_asrv_rsp) +
		  asrv_info->asp_nprefix * sizeof(struct in_addr) * 2;
		asrv_info = (struct air_asrv_rsp *)(void *)
		    ((char *)asrv_info + asrv_info_len);
		buf_len -= asrv_info_len;
	}
	free(asrv_info_base);
}


/*
 * Process show ATM adapter configuration command
 * 
 * Command format: 
 *	atm show config [<interface-name>]
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_config(int argc, char **argv, const struct cmd *cmdp __unused)
{
	size_t buf_len;
	struct atminfreq	air;
	struct air_cfg_rsp	*cfg_info, *cfg_info_base;

	/*
	 * Validate interface name
	 */
	bzero(air.air_cfg_intf, sizeof(air.air_cfg_intf));
	if (argc) {
		if (strlen(argv[0]) > IFNAMSIZ - 1) {
			fprintf(stderr, "%s: Illegal interface name\n",
					prog);
			exit(1);
		}
		strcpy(air.air_cfg_intf, argv[0]);
		argc--; argv++;
	}

	/*
	 * Get configuration information from the kernel
	 */
	air.air_opcode = AIOCS_INF_CFG;
	buf_len = do_info_ioctl(&air, sizeof(struct air_asrv_rsp) * 3);
	if ((ssize_t)buf_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "%s is not an ATM device\n",
					argv[0]);
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}

	/*
	 * Print the interface information
	 */
	cfg_info_base = cfg_info =
			(struct air_cfg_rsp *)(void *)air.air_buf_addr;
	for (; buf_len >= sizeof(struct air_cfg_rsp); cfg_info++,
			buf_len -= sizeof(struct air_cfg_rsp)) {
		print_cfg_info(cfg_info);
	}
	free(cfg_info_base);
}


/*
 * Process show interface command
 * 
 * Command format: 
 *	atm show interface [<interface-name>]
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_intf(int argc, char **argv, const struct cmd *cmdp __unused)
{
	size_t buf_len;
	struct atminfreq	air;
	struct air_int_rsp	*int_info, *int_info_base;

	/*
	 * Validate interface name
	 */
	bzero(&air, sizeof(air));
	if (argc) {
		if (strlen(argv[0]) > IFNAMSIZ - 1) {
			fprintf(stderr, "%s: Illegal interface name\n",
					prog);
			exit(1);
		}
		strcpy(air.air_int_intf, argv[0]);
		argc--; argv++;
	}

	/*
	 * Get interface information from the kernel
	 */
	air.air_opcode = AIOCS_INF_INT;
	buf_len = do_info_ioctl(&air, sizeof(struct air_int_rsp) * 3);
	if ((ssize_t)buf_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "%s is not an ATM device\n",
					argv[0]);
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}

	/*
	 * Print the interface information
	 */
	int_info_base = int_info =
			(struct air_int_rsp *)(void *)air.air_buf_addr;
	for (; buf_len >= sizeof(struct air_int_rsp); int_info++,
			buf_len -= sizeof(struct air_int_rsp)) {
		print_intf_info(int_info);
	}
	free(int_info_base);
}


/*
 * Process show IP VCCs command
 * 
 * Command format: 
 *	atm show ipvcc [<ip-addr>]
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_ip_vcc(int argc, char **argv, const struct cmd *cmdp __unused)
{
	int rc;
	size_t ip_info_len;
	char			*if_name = (char *)0;
	struct atminfreq	air;
	struct air_ip_vcc_rsp	*ip_info, *ip_info_base;
	struct sockaddr_in	*sain;
	union {
		struct sockaddr_in	sain;
		struct sockaddr		sa;
	} host_addr;

	/*
	 * First parameter can be a netif name, an IP host name, or
	 * an IP address.  Figure out which it is.
	 */
	bzero(&host_addr, sizeof(host_addr));
	host_addr.sa.sa_family = AF_INET;
	if (argc) {
		rc = verify_nif_name(argv[0]);
		if (rc < 0) {
			/*
			 * Error occured
			 */
			fprintf(stderr, "%s: ", prog);
			switch (errno) {
			case ENOPROTOOPT:
			case EOPNOTSUPP:
				perror("Internal error");
				break;
			case ENXIO:
				fprintf(stderr, "%s is not an ATM device\n",
						argv[0]);
				break;
			default:
				perror("ioctl (AIOCINFO)");
				break;
			}
			exit(1);
		} else if (rc > 0) {
			/*
			 * Parameter is a valid netif name
			 */
			if_name = argv[0];
		} else {
			/*
			 * Get IP address of specified host name
			 */
			sain = get_ip_addr(argv[0]);
			host_addr.sain.sin_addr.s_addr =
					sain->sin_addr.s_addr;
		}
	} else {
		host_addr.sain.sin_addr.s_addr = INADDR_ANY;
	}

	/*
	 * Get IP map information from the kernel
	 */
	air.air_opcode = AIOCS_INF_IPM;
	air.air_ip_addr = host_addr.sa;
	ip_info_len = do_info_ioctl(&air, sizeof(struct air_ip_vcc_rsp) * 10);
	if ((ssize_t)ip_info_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "not an ATM device\n");
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}
	ip_info_base = ip_info =
			(struct air_ip_vcc_rsp *)(void *)air.air_buf_addr;

	/*
	 * Sort the information
	 */
	qsort((void *) ip_info,
			ip_info_len / sizeof(struct air_ip_vcc_rsp),
			sizeof(struct air_ip_vcc_rsp),
			ip_vcc_compare);

	/*
	 * Print the relevant information
	 */
	while (ip_info_len >= sizeof(struct air_ip_vcc_rsp)) {
		if (!if_name || !strcmp(if_name, ip_info->aip_intf)) {
			print_ip_vcc_info(ip_info);
		}
		ip_info++;
		ip_info_len -= sizeof(struct air_ip_vcc_rsp);
	}

	/*
	 * Release the information from the kernel
	 */
	free(ip_info_base);
}


/*
 * Process show network interface command
 * 
 * Command format: 
 *	atm show netif [<netif>]
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_netif(int argc, char **argv, const struct cmd *cmdp __unused)
{
	size_t buf_len;
	struct atminfreq	air;
	struct air_netif_rsp	*int_info, *int_info_base;

	/*
	 * Validate network interface name
	 */
	bzero(air.air_int_intf, sizeof(air.air_int_intf));
	if (argc) {
		if (strlen(argv[0]) > IFNAMSIZ - 1) {
			fprintf(stderr, "%s: Illegal interface name\n", prog);
			exit(1);
		}
		strcpy(air.air_int_intf, argv[0]);
		argc--; argv++;
	}

	/*
	 * Get network interface information from the kernel
	 */
	air.air_opcode = AIOCS_INF_NIF;
	buf_len = do_info_ioctl(&air, sizeof(struct air_netif_rsp) * 3);
	if ((ssize_t)buf_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "%s is not an ATM device\n",
					argv[0]);
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}

	/*
	 * Print the network interface information
	 */
	int_info_base = int_info =
			(struct air_netif_rsp *) air.air_buf_addr;
	for (; buf_len >= sizeof(struct air_netif_rsp); int_info++,
			buf_len -= sizeof(struct air_netif_rsp)) {
		print_netif_info(int_info);
	}
	free(int_info_base);
}


/*
 * Process interface statistics command
 * 
 * Command format: 
 *	atm show stats interface [<interface-name>]
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_intf_stats(int argc, char **argv, const struct cmd *cmdp __unused)
{
	size_t buf_len;
	char			intf[IFNAMSIZ];
	struct atminfreq	air;
	struct air_phy_stat_rsp	*pstat_info, *pstat_info_base;
	struct air_cfg_rsp	*cfg_info;

	/*
	 * Validate interface name
	 */
	bzero(intf, sizeof(intf));
	if (argc) {
		if (strlen(argv[0]) > IFNAMSIZ - 1) {
			fprintf(stderr, "%s: Illegal interface name\n",
					prog);
			exit(1);
		}
		strcpy(intf, argv[0]);
		argc--; argv++;
	}

	/*
	 * If there are parameters remaining, the request is for
	 * vendor-specific adaptor statistics
	 */
	if (argc) {
		/*
		 * Get adapter configuration information
		 */
		air.air_opcode = AIOCS_INF_CFG;
		strcpy(air.air_cfg_intf, intf);
		buf_len = do_info_ioctl(&air, sizeof(struct air_cfg_rsp));
		if ((ssize_t)buf_len == -1) {
			fprintf(stderr, "%s: ", prog);
			switch (errno) {
			case ENOPROTOOPT:
			case EOPNOTSUPP:
				perror("Internal error");
				break;
			case ENXIO:
				fprintf(stderr, "%s is not an ATM device\n",
						intf);
				break;
			default:
				perror("ioctl (AIOCINFO)");
				break;
			}
			exit(1);
		}
		cfg_info = (struct air_cfg_rsp *)(void *)air.air_buf_addr;

		/*
		 * Call the appropriate vendor-specific routine
		 */
		switch(cfg_info->acp_vendor) {
		case VENDOR_FORE:
			show_fore200_stats(intf, argc, argv);
			break;
		default:
			fprintf(stderr, "%s: Unknown adapter vendor\n",
					prog);
			break;
		}

		free(cfg_info);
	} else {
		/*
		 * Get generic interface statistics
		 */
		air.air_opcode = AIOCS_INF_PIS;
		strcpy(air.air_physt_intf, intf);
		buf_len = do_info_ioctl(&air,
		    sizeof(struct air_phy_stat_rsp) * 3);
		if ((ssize_t)buf_len == -1) {
			fprintf(stderr, "%s: ", prog);
			switch (errno) {
			case ENOPROTOOPT:
			case EOPNOTSUPP:
				perror("Internal error");
				break;
			case ENXIO:
				fprintf(stderr, "%s is not an ATM device\n",
						intf);
				break;
			default:
				perror("ioctl (AIOCINFO)");
				break;
			}
			exit(1);
		}

		/*
		 * Display the interface statistics
		 */
		pstat_info_base = pstat_info = (struct air_phy_stat_rsp *)
		    (void *)air.air_buf_addr;
		for (; buf_len >= sizeof(struct air_phy_stat_rsp);
				pstat_info++,
				buf_len-=sizeof(struct air_phy_stat_rsp)) {
			print_intf_stats(pstat_info);
		}
		free((caddr_t)pstat_info_base);
	}
}


/*
 * Process VCC statistics command
 * 
 * Command format: 
 *	atm show stats VCC [<interface-name> [<vpi> [<vci>]]] 
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_vcc_stats(int argc, char **argv, const struct cmd *cmdp __unused)
{
	size_t vcc_info_len;
	int	vpi = -1, vci = -1;
	char	*cp, *intf = NULL;
	struct air_vcc_rsp	*vcc_info, *vcc_info_base;

	/*
	 * Validate interface name
	 */
	if (argc) {
		if (strlen(argv[0]) > IFNAMSIZ - 1) {
			fprintf(stderr, "%s: Illegal interface name\n",
					prog);
			exit(1);
		}
		intf = argv[0];
		argc--; argv++;
	}

	/*
	 * Validate VPI value
	 */
	if (argc) {
		vpi = strtol(argv[0], &cp, 0);
		if ((*cp != '\0') || (vpi < 0) || (vpi >= 1 << 8)) {
			fprintf(stderr, "%s: Invalid VPI value\n", prog);
			exit(1);
		}
		argc--; argv++;
	}

	/*
	 * Validate VCI value
	 */
	if (argc) {
		vci = strtol(argv[0], &cp, 0);
		if ((*cp != '\0') || (vci <= 0) || (vci >= 1 << 16)) {
			fprintf(stderr, "%s: Invalid VCI value\n",
					prog);
			exit(1);
		}
		argc--; argv++;
	}

	/*
	 * Get VCC information
	 */
	vcc_info_len = get_vcc_info(intf, &vcc_info);
	if (vcc_info_len == 0)
		exit(1);
	else if ((ssize_t)vcc_info_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "Not an ATM device\n");
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}

	/*
	 * Sort the VCC information
	 */
	qsort((void *) vcc_info,
			vcc_info_len / sizeof(struct air_vcc_rsp),
			sizeof(struct air_vcc_rsp),
			vcc_compare);

	/*
	 * Display the VCC statistics
	 */
	vcc_info_base = vcc_info;
	for (; vcc_info_len >= sizeof(struct air_vcc_rsp);
			vcc_info_len-=sizeof(struct air_vcc_rsp),
			vcc_info++) {
		if (vpi != -1 && vcc_info->avp_vpi != vpi)
			continue;
		if (vci != -1 && vcc_info->avp_vci != vci)
			continue;
		print_vcc_stats(vcc_info);
	}
	free(vcc_info_base);
}


/*
 * Process VCC information command
 * 
 * Command format: 
 *	atm show VCC [<interface-name> [<vpi> [<vci>] | PVC | SVC]] 
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_vcc(int argc, char **argv, const struct cmd *cmdp __unused)
{
	size_t vcc_info_len;
	int	vpi = -1, vci = -1, show_pvc = 0, show_svc = 0;
	char	*cp, *intf = NULL;
	struct air_vcc_rsp	*vcc_info, *vcc_info_base;

	/*
	 * Validate interface name
	 */
	if (argc) {
		if (strlen(argv[0]) > IFNAMSIZ - 1) {
			fprintf(stderr, "%s: Illegal interface name\n",
					prog);
			exit(1);
		}
		intf = argv[0];
		argc--; argv++;
	}

	/*
	 * Validate VPI value
	 */
	if (argc) {
		if (strcasecmp(argv[0], "pvc"))
			show_pvc = 1;
		else if (strcasecmp(argv[0], "svc"))
			show_svc = 1;
		else {
			vpi = strtol(argv[0], &cp, 0);
			if ((*cp != '\0') || (vpi < 0) ||
					(vpi >= 1 << 8)) {
				fprintf(stderr, "%s: Invalid VPI value\n", prog);
				exit(1);
			}
		}
		argc--; argv++;
	}

	/*
	 * Validate VCI value
	 */
	if (argc) {
		vci = strtol(argv[0], &cp, 0);
		if ((*cp != '\0') || (vci <= 0) || (vci >= 1 << 16)) {
			fprintf(stderr, "%s: Invalid VCI value\n",
					prog);
			exit(1);
		}
		argc--; argv++;
	}

	/*
	 * Get VCC information
	 */
	vcc_info_len = get_vcc_info(intf, &vcc_info);
	if (vcc_info_len == 0)
		exit(1);
	else if ((ssize_t)vcc_info_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "Not an ATM device\n");
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}

	/*
	 * Sort the VCC information
	 */
	qsort((void *) vcc_info,
			vcc_info_len/sizeof(struct air_vcc_rsp),
			sizeof(struct air_vcc_rsp),
			vcc_compare);

	/*
	 * Display the VCC information
	 */
	vcc_info_base = vcc_info;
	for (; vcc_info_len >= sizeof(struct air_vcc_rsp);
			vcc_info_len-=sizeof(struct air_vcc_rsp),
			vcc_info++) {
		if (vpi != -1 && vcc_info->avp_vpi != vpi)
			continue;
		if (vci != -1 && vcc_info->avp_vci != vci)
			continue;
		if (show_pvc && vcc_info->avp_type & VCC_PVC)
			continue;
		if (show_svc && vcc_info->avp_type & VCC_SVC)
			continue;
		print_vcc_info(vcc_info);
	}
	free(vcc_info_base);
}


/*
 * Process version command
 * 
 * Command format: 
 *	atm show version
 *
 * Arguments:
 *	argc	number of remaining arguments to command
 *	argv	pointer to remaining argument strings
 *	cmdp	pointer to command description 
 *
 * Returns:
 *	none
 *
 */
void
show_version(int argc __unused, char **argv __unused,
    const struct cmd *cmdp __unused)
{
	size_t buf_len;
	struct atminfreq	air;
	struct air_version_rsp	*ver_info, *ver_info_base;

	/*
	 * Get network interface information from the kernel
	 */
	air.air_opcode = AIOCS_INF_VER;
	buf_len = do_info_ioctl(&air, sizeof(struct air_version_rsp));
	if ((ssize_t)buf_len == -1) {
		fprintf(stderr, "%s: ", prog);
		switch (errno) {
		case ENOPROTOOPT:
		case EOPNOTSUPP:
			perror("Internal error");
			break;
		case ENXIO:
			fprintf(stderr, "Not an ATM device\n");
			break;
		default:
			perror("ioctl (AIOCINFO)");
			break;
		}
		exit(1);
	}

	/*
	 * Print the network interface information
	 */
	ver_info_base = ver_info =
			(struct air_version_rsp *)(void *)air.air_buf_addr;
	for (; buf_len >= sizeof(struct air_version_rsp); ver_info++,
			buf_len -= sizeof(struct air_version_rsp)) {
		print_version_info(ver_info);
	}
	free(ver_info_base);
}


/*
 * Comparison function for qsort
 * 
 * Arguments:
 *	p1	pointer to the first VCC response
 *	p2	pointer to the second VCC response
 *
 * Returns:
 *	int	a number less than, greater than, or equal to zero,
 *		depending on whether *p1 is less than, greater than, or
 *		equal to *p2
 *
 */
static int
vcc_compare(p1, p2)
	const void *p1, *p2;
{
	int rc;
	const struct air_vcc_rsp	*c1, *c2;

	c1 = (const struct air_vcc_rsp *) p1;
	c2 = (const struct air_vcc_rsp *) p2;

	/*
	 * Compare the interface names
	 */
	rc = strcmp(c1->avp_intf, c2->avp_intf);
	if (rc)
		return(rc);

	/*
	 * Compare the VPI values
	 */
	rc = c1->avp_vpi - c2->avp_vpi;
	if (rc)
		return(rc);

	/*
	 * Compare the VCI values
	 */
	rc = c1->avp_vci - c2->avp_vci;
	if (rc)
		return(rc);

	/*
	 * Compare the types
	 */
	rc = c1->avp_type - c2->avp_type;
	return(rc);
}


/*
 * Comparison function for qsort
 * 
 * Arguments:
 *	p1	pointer to the first VCC response
 *	p2	pointer to the second VCC response
 *
 * Returns:
 *	int	a number less than, greater than, or equal to zero,
 *		depending on whether *p1 is less than, greater than, or
 *		equal to *p2
 *
 */
static int
ip_vcc_compare(p1, p2)
	const void *p1, *p2;
{
	int rc;
	const struct air_ip_vcc_rsp	*c1, *c2;

	c1 = (const struct air_ip_vcc_rsp *) p1;
	c2 = (const struct air_ip_vcc_rsp *) p2;

	/*
	 * Compare the interface names
	 */
	rc = strcmp(c1->aip_intf, c2->aip_intf);
	if (rc)
		return(rc);

	/*
	 * Compare the VPI values
	 */
	rc = c1->aip_vpi - c2->aip_vpi;
	if (rc)
		return(rc);

	/*
	 * Compare the VCI values
	 */
	rc = c1->aip_vci - c2->aip_vci;
	return(rc);
}


/*
 * Comparison function for qsort
 * 
 * Arguments:
 *	p1	pointer to the first ARP or IP map entry
 *	p2	pointer to the second ARP or IP map entry
 *
 * Returns:
 *	int	a number less than, greater than, or equal to zero,
 *		depending on whether *p1 is less than, greater than, or
 *		equal to *p2
 *
 */
static int
arp_compare(p1, p2)
	const void *p1, *p2;
{
	int rc;
	const struct air_arp_rsp	*c1, *c2;
	const struct sockaddr_in	*sin1, *sin2;

	c1 = (const struct air_arp_rsp *)p1;
	c2 = (const struct air_arp_rsp *)p2;
	sin1 = (const struct sockaddr_in *)(const void *)&c1->aap_arp_addr;
	sin2 = (const struct sockaddr_in *)(const void *)&c2->aap_arp_addr;

	/*
	 * Compare the IP addresses
	 */
	if ((rc = sin1->sin_family - sin2->sin_family) != 0)
		return(rc);
	if ((rc = sin1->sin_addr.s_addr - sin2->sin_addr.s_addr) != 0)
		return(rc);

	/*
	 * Compare the ATM addresses
	 */
	if ((rc = c1->aap_addr.address_format - c2->aap_addr.address_format) != 0)
		return(rc);
	if ((rc = c1->aap_addr.address_length - c2->aap_addr.address_length) != 0)
		return(rc);
	switch(c1->aap_addr.address_format) {
	case T_ATM_ABSENT:
		rc = 0;
		break;
	case T_ATM_ENDSYS_ADDR:
		rc = bcmp(c1->aap_addr.address, c2->aap_addr.address,
		    sizeof(Atm_addr_nsap));
		break;
	case T_ATM_E164_ADDR:
		rc = bcmp(c1->aap_addr.address, c2->aap_addr.address,
		    sizeof(Atm_addr_e164));
		break;
	case T_ATM_SPANS_ADDR:
		rc = bcmp(c1->aap_addr.address, c2->aap_addr.address,
		    sizeof(Atm_addr_spans));
		break;
	}

	return(rc);
}