#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <mach/boolean.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <netat/appletalk.h>
#include <netat/lap.h>
#include <netat/ddp.h>
#include <netat/nbp.h>
#include <netat/at_var.h>
#include <netat/routing_tables.h>
#include <AppleTalk/at_proto.h>
#include <AppleTalk/at_paths.h>
#define SET_ERRNO(e) errno = e
typedef struct if_cfg {
at_ifnames_t used;
char home_if[IFNAMESIZ];
} if_cfg_t;
#define MSGSTR(num,str) str
#define MAX_ZONES 50
#define MAX_LINE_LENGTH 240
#define INVALID_ZIP_CHARS "=@*:\377"
#define MAX_NET_NO 0xFEFF
#define COMMENT_CHAR '#'
#define ZONE_IFS_PER_LINE 6
#define Z_MAX_PRINT 15
at_ifnames_t seed_names;
int seed_ind;
#define IF_NO(c) (atoi(&c[2]))
char homePort[5];
extern int in_list(char *, at_ifnames_t *);
void showCfg();
static FILE *STDOUT = stdout;
#define PRAM_FILE NVRAM
#define NVRAMSIZE 256
#define INTERFACE_OFFSET 1
#define ADDRESS_OFFSET (INTERFACE_OFFSET+IFNAMESIZ)
#define ZONENAME_OFFSET (ADDRESS_OFFSET+sizeof(struct at_addr))
char pram_file[80];
static int readxpram(if_name, buf, count, offset)
char *if_name, *buf;
int count;
int offset;
{
int fd;
sprintf(pram_file, "%s.%s", PRAM_FILE, if_name);
if ((fd = open(pram_file, O_RDONLY)) == -1) {
return(-1);
}
if ((int)lseek(fd, (off_t)offset, SEEK_SET) != offset) {
close(fd);
return(-1);
}
if (read(fd, buf, count) != count) {
close(fd);
return(-1);
}
#ifdef COMMENT_OUT
printf("readxpram: read %s %d bytes offset %d: %s\n",
pram_file, count, offset,
(offset==ZONENAME_OFFSET)? ((at_nvestr_t *)buf)->str: "" );
#endif
close(fd);
return(0);
}
static int writexpram(if_name, buf, count, offset)
char *if_name, *buf;
int count;
int offset;
{
int fd;
char buffer[NVRAMSIZE];
sprintf(pram_file, "%s.%s", PRAM_FILE, if_name);
if ((fd = open(pram_file, O_RDWR)) == -1) {
if (errno == ENOENT) {
fd = open(pram_file, O_RDWR|O_CREAT, 0644);
if (fd == -1)
return(-1);
memset(buffer, 0, sizeof(buffer));
if (write(fd, buffer, sizeof(buffer))
!= sizeof(buffer)) {
close(fd);
unlink(pram_file);
return(-1);
}
(void) lseek(fd, (off_t)0, 0);
}
else
return(0);
}
if ((int)lseek(fd, (off_t)offset, SEEK_SET) != offset) {
close(fd);
return(-1);
}
#ifdef COMMENT_OUT
printf("writexpram: wrote %s %d bytes offset %d\n", pram_file,
count, offset);
#endif
if (write(fd, buf, count) != count) {
close(fd);
return(-1);
}
close(fd);
return(0);
}
int at_savecfgdefaults(fd, ifName)
int fd;
char *ifName;
{
int tmp_fd = 0;
at_if_cfg_t cfg;
if (!fd) {
if ((tmp_fd = socket(AF_APPLETALK, SOCK_RAW, 0)) < 0)
return(-1);
fd = tmp_fd;
}
if (ifName && strlen(ifName))
strncpy(cfg.ifr_name, ifName, sizeof(cfg.ifr_name));
else
cfg.ifr_name[0] = '\0';
if (ioctl(fd, AIOCGETIFCFG, (caddr_t)&cfg) < 0) {
fprintf(stderr, "AIOCGETIFCFG failed for %s (%d)\n",
cfg.ifr_name, errno);
if (tmp_fd)
(void)close(tmp_fd);
return(-1);
} else {
(void)at_setdefaultaddr(cfg.ifr_name, &cfg.node);
(void)at_setdefaultzone(cfg.ifr_name, &cfg.zonename);
(void)writexpram(cfg.ifr_name, ifName, IFNAMESIZ,
INTERFACE_OFFSET);
}
if (tmp_fd)
(void)close(tmp_fd);
return(0);
}
int at_getdefaultaddr(ifName, init_address)
char *ifName;
struct at_addr *init_address;
{
if (!ifName || !strlen(ifName))
return(-1);
if (!readxpram(ifName, init_address, sizeof(struct at_addr),
ADDRESS_OFFSET)) {
return(0);
}
else
return (-1);
}
int at_setdefaultaddr(ifName, init_address)
char *ifName;
struct at_addr *init_address;
{
if (!ifName || !strlen(ifName))
return(-1);
return(writexpram(ifName, init_address, sizeof(struct at_addr),
ADDRESS_OFFSET));
}
int at_getdefaultzone(ifName, zone_name)
char *ifName;
at_nvestr_t *zone_name;
{
if (!ifName || !strlen(ifName))
return(-1);
return(readxpram(ifName, zone_name, sizeof(at_nvestr_t),
ZONENAME_OFFSET));
}
int at_setdefaultzone(ifName, zone_name)
char *ifName;
at_nvestr_t *zone_name;
{
if (!ifName || !strlen(ifName))
return(-1);
if (DEFAULT_ZONE(zone_name)) {
return(0);
}
return(writexpram(ifName, zone_name, sizeof(at_nvestr_t),
ZONENAME_OFFSET));
}
int getConfigInfo(elapcfgp, zonep, cfgFileName, checkCfg, displayCfg, mh)
at_if_cfg_t elapcfgp[];
zone_usage_t zonep[];
char *cfgFileName;
short checkCfg;
short displayCfg;
short mh;
{
if_cfg_t filecfg;
if (getIFInfo(elapcfgp, &filecfg, cfgFileName, checkCfg, displayCfg, mh))
return(-1);
if (!elapcfgp->ifr_name[0]) {
fprintf(stderr,
MSGSTR(M_NO_IF_CFG,
"There are no interfaces configured\n"));
return(-1);
}
if (!mh)
if (getZoneInfo(zonep, &filecfg, cfgFileName, checkCfg) ||
checkSeeds(elapcfgp, zonep))
return(-1);
if (displayCfg)
showCfg(elapcfgp, zonep);
return(0);
}
int getIFInfo(elapcfgp, filecfgp, cfgFileName, checkCfg, displayCfg, mh)
at_if_cfg_t elapcfgp[];
if_cfg_t *filecfgp;
char *cfgFileName;
short checkCfg;
short displayCfg;
short mh;
{
FILE *pFin;
char linein[MAX_LINE_LENGTH],
buf[MAX_LINE_LENGTH],
buf1[MAX_LINE_LENGTH],
*pc1,
errbuf[100];
int i,x;
int ifno=0;
int parmno;
int lineno=0;
int used_ind = 0;
int home = FALSE;
int bad = TRUE;
int gotNetStart;
int done;
int error = FALSE;
if (!(pFin = fopen(cfgFileName, "r"))) {
fprintf(stderr, MSGSTR(M_OPEN_CFG,
"error opening configuration file: %s\n"), cfgFileName);
return(-1);
}
filecfgp->home_if[0] = '\0';
bzero(&seed_names, sizeof(seed_names));
seed_ind = 0;
while (!feof(pFin)) {
if (!fgets(linein, sizeof(linein)-1, pFin)) {
continue;
}
lineno++;
if (linein[0] == COMMENT_CHAR)
continue;
if (linein[0] == ':')
continue;
strcpy(buf,linein);
pc1 = strtok(buf,":");
parmno=0;
bad = FALSE;
gotNetStart = FALSE;
done = FALSE;
if (pc1) do {
if (sscanf(pc1," %s",buf1) != 1)
break;
switch(parmno) {
case 0:
if (in_list(buf1, &filecfgp->used)) {
sprintf(errbuf, MSGSTR(M_IF_USED,
"interface %s already used"), buf1);
bad = TRUE;
}
if (!bad)
strncpy(elapcfgp[ifno].ifr_name, buf1, 4);
break;
case 1:
case 2:
if (sscanf(buf1,"%d",&x) != 1){
if (buf1[0] == '*') {
if (home++) {
sprintf(errbuf,
MSGSTR(M_MULT_HOME,
"multiple home designations (%d) "), parmno);
bad = TRUE;
}
elapcfgp[ifno].flags = ELAP_CFG_HOME;
done = TRUE;
sprintf(homePort,elapcfgp[ifno].ifr_name);
}
else {
sprintf(errbuf, MSGSTR(M_BAD_FMT, "invalid format "));
bad = TRUE;
}
} else {
if (x <= 0 || x >= MAX_NET_NO) {
sprintf(errbuf, MSGSTR(M_BAD_NET,
"invalid net:%d "),x);
bad = TRUE;
}
if (parmno == 1) {
elapcfgp[ifno].netStart = x;
gotNetStart = TRUE;
}
else {
if (elapcfgp[ifno].netStart > x) {
sprintf(errbuf,
MSGSTR(M_END_LT_START,
"ending net less than start"));
bad = TRUE;
}
else
elapcfgp[ifno].netEnd = x;
}
}
break;
case 3:
if (buf1[0] == '*') {
if (home++) {
sprintf(errbuf,
MSGSTR(M_MULT_HOME,
"multiple home designations (%d) "), parmno);
bad = TRUE;
}
elapcfgp[ifno].flags = ELAP_CFG_HOME;
sprintf(homePort,elapcfgp[ifno].ifr_name);
done = TRUE;
break;
}
default:
fprintf(stderr,
"extra input ignored:(%s)\n", buf1);
done = TRUE;
}
if (bad)
break;
parmno++;
} while(!done && pc1 && (pc1 = strtok(NULL,":")));
if (!bad) {
if (elapcfgp[ifno].ifr_name[0]) {
if (gotNetStart) {
if (!elapcfgp[ifno].netEnd)
elapcfgp[ifno].netEnd = elapcfgp[ifno].netStart;
for (i=0; i<ifno; i++) {
if ((elapcfgp[ifno].netEnd >= elapcfgp[i].netStart &&
elapcfgp[ifno].netEnd <= elapcfgp[i].netEnd) ||
(elapcfgp[ifno].netStart >= elapcfgp[i].netStart &&
elapcfgp[ifno].netStart <= elapcfgp[i].netEnd) ||
(elapcfgp[i].netEnd >= elapcfgp[ifno].netStart &&
elapcfgp[i].netEnd <= elapcfgp[ifno].netEnd) ||
(elapcfgp[i].netStart >= elapcfgp[ifno].netStart &&
elapcfgp[i].netStart <= elapcfgp[ifno].netEnd)) {
sprintf(errbuf,
MSGSTR(M_RANGE_CONFLICT,
"%s net range conflict with %s"),
elapcfgp[ifno].ifr_name,
elapcfgp[i].ifr_name);
bad = TRUE;
break;
}
}
}
if (!bad) {
strcpy(filecfgp->used.at_if[used_ind++],
elapcfgp[ifno].ifr_name);
if (elapcfgp[ifno].flags & ELAP_CFG_HOME) {
strcpy(filecfgp->home_if,
elapcfgp[ifno].ifr_name);
}
if (gotNetStart) {
strcpy(seed_names.at_if[seed_ind++],
elapcfgp[ifno].ifr_name);
}
ifno++;
}
}
}
if (bad ) {
memset(&elapcfgp[ifno], NULL,sizeof(elapcfgp[ifno]));
if (checkCfg)
fprintf(stderr, MSGSTR(M_LINE1,
"error, %s\n"),errbuf);
else
fprintf(stderr, MSGSTR(M_LINE,
"error, line %d. %s\n%s\n"),lineno,errbuf,linein);
error = TRUE;
break;
}
}
if (!mh && !home && !error && ifno) {
if (checkCfg || displayCfg)
fprintf(stderr, MSGSTR(M_NO_HOME_PT_W,
"Warning, no home port specified.\n"\
"(You must designate one interface as the home port before\n"\
"starting Appletalk in router mode)\n\n"));
else {
error = TRUE;
fprintf(stderr, MSGSTR(M_NO_HOME_PT,
"error, no home port specified\n"));
}
}
if (mh && !home)
elapcfgp[0].flags = ELAP_CFG_HOME;
fclose(pFin);
return(error ? -1 : 0);
}
int getZoneInfo(zonep, filecfgp, cfgFileName, checkCfg)
zone_usage_t zonep[];
if_cfg_t *filecfgp;
char *cfgFileName;
short checkCfg;
{
FILE *pFin;
char linein[MAX_LINE_LENGTH], buf[MAX_LINE_LENGTH],
buf1[MAX_LINE_LENGTH], *pc1, *pc2;
char tbuf1[NBP_NVE_STR_SIZE+20];
char tbuf2[NBP_NVE_STR_SIZE+20];
char curzone[NBP_NVE_STR_SIZE+20];
char errbuf[100];
int i,j;
int parmno;
int lineno=0, homeLine=0;
int home = FALSE;
int bad = TRUE;
int done = FALSE;
int error = FALSE;
int zoneno = 0;
int z_ind = 0;
int len;
int gotHomePort = (filecfgp->home_if[0])?1:0;
if (!(pFin = fopen(cfgFileName, "r"))) {
fprintf(stderr, MSGSTR(M_OPEN_CFG,
"error opening configuration file: %s\n"), cfgFileName);
return(-1);
}
while (!feof(pFin)) {
if (!fgets(linein, sizeof(linein)-1, pFin)) {
continue;
}
lineno++;
if (linein[0] == COMMENT_CHAR)
continue;
if (linein[0] != ':')
continue;
strcpy(buf,linein);
pc1 = strtok(&buf[1],":");
parmno=0;
if (!bad) {
zoneno++;
z_ind = 0;
}
bad = FALSE;
done = FALSE;
if (pc1) do {
if (sscanf(pc1," %[^:\n]",buf1) != 1)
break;
switch(parmno) {
case 0:
strcpy(curzone,buf1);
if ((len = strlen(buf1)) > NBP_NVE_STR_SIZE) {
sprintf(errbuf, MSGSTR(M_ZONE_TOO_LONG,
"zone name too long"));
bad = TRUE;
break;
}
if (pc2 = strpbrk(buf1, INVALID_ZIP_CHARS)) {
sprintf(errbuf, MSGSTR(M_IVAL_CHAR,
"invalid character in zone name (%c)"),*pc2);
bad = TRUE;
break;
}
for (i=0; i < zoneno; i++) {
strcpy(tbuf1, buf1);
strcpy(tbuf2, zonep[i].zone_name.str);
for (j=0; j<NBP_NVE_STR_SIZE; j++) {
if (!tbuf1[j]) break;
tbuf1[j] = toupper(tbuf1[j] );
tbuf2[j] = toupper(tbuf2[j] );
}
if (!strcmp(tbuf1,tbuf2)) {
sprintf(errbuf, MSGSTR(M_DUPE_ZONE,
"duplicate zone entry"));
bad = TRUE;
break;
}
}
strcpy(zonep[zoneno].zone_name.str,buf1);
zonep[zoneno].zone_name.len = len;
break;
default:
if (buf1[0] == '*') {
if (home++) {
sprintf(errbuf, MSGSTR(M_DUPE_HOME,
"home already designated on line %d"),homeLine);
bad = TRUE;
break;
}
homeLine = lineno;
if (gotHomePort &&
!in_list(filecfgp->home_if,
&zonep[zoneno].zone_iflist)) {
sprintf(errbuf, MSGSTR(M_HOME_MUST,
"Zone designated as 'home' must contain\n"\
"the home I/F (%s)"),
homePort);
bad = TRUE;
break;
}
done = TRUE;
zonep[zoneno].zone_home = TRUE;
}
else {
if (in_list(buf1, &filecfgp->used)) {
if (in_list(buf1, &seed_names)) {
strcpy(zonep[zoneno].zone_iflist.at_if[z_ind++], buf1);
} else {
sprintf(errbuf,
MSGSTR(M_ALL_IFS_ASSOC,
"all interfaces associated with a "\
"zone must be seed type\n"\
"interface for this zone is non-seed (%s)"),
buf1);
bad = TRUE;
}
} else {
sprintf(errbuf,
MSGSTR(M_INVAL_IF,
"invalid interface (%s)"), buf1);
bad = TRUE;
}
break;
}
}
parmno++;
if (bad) {
if(checkCfg) {
sscanf(pc1,":%[^:\n]",linein);
fprintf(stderr, MSGSTR(M_EZONE,
"error, %s\nzone:%s\n"),errbuf,curzone);
}
else
fprintf(stderr, MSGSTR(M_LINE,
"error, line %d. %s\n%s\n"),lineno,errbuf,linein);
error = TRUE;
break;
}
} while(!done && pc1 && (pc1 = strtok(NULL,":")));
}
if (gotHomePort && !home &&
(in_list(filecfgp->home_if, &seed_names))) {
j = -1;
for (i=0; i<MAX_ZONES; i++) {
if (!zonep[i].zone_name.len)
break;
if (in_list(filecfgp->home_if,&zonep[i].zone_iflist))
if (j >=0 ) {
home = 0;
break;
}
else {
home = 1;
j = i;
}
}
if (home)
zonep[j].zone_home = TRUE;
else {
error = TRUE;
fprintf(stderr, MSGSTR(M_NO_HOME_ZN,
"error, no home zone designated\n"));
}
}
fclose(pFin);
return(error? -1:0);
}
int ifcompare(v1,v2)
void *v1, *v2;
{
return(strcmp(((at_if_cfg_t *)v1)->ifr_name, ((at_if_cfg_t *)v2)->ifr_name));
}
void showCfg(cfgp, if_zones)
at_if_cfg_t *cfgp;
zone_usage_t if_zones[];
{
int i,k, cnt=0, seed=FALSE;
char range[40];
qsort((void*)cfgp, IF_TOTAL_MAX, sizeof(*cfgp), ifcompare);
for (i=0; i<IF_TOTAL_MAX; i++) {
if (cfgp[i].ifr_name[0]) {
if (!cnt++) {
fprintf(STDOUT,
MSGSTR(M_RTR_CFG,"Router mode configuration:\n\n"));
fprintf(STDOUT,
MSGSTR(M_RTR_CFG_HDR,"H I/F Network Range\n"));
fprintf(STDOUT,"- --- -------------\n");
}
range[0] = '\0';
if (cfgp[i].netStart || cfgp[i].netEnd) {
sprintf(range,"%5d - %d", cfgp[i].netStart, cfgp[i].netEnd);
seed=TRUE;
}
fprintf(STDOUT,"%c %-3s %s\n", cfgp[i].flags & ELAP_CFG_HOME ? '*' : ' ',
cfgp[i].ifr_name,
range[0] ? range : MSGSTR(M_NONSEED,"(non-seed)"));
}
}
if (!cnt) {
fprintf(STDOUT,
MSGSTR(M_NO_IF_CFG,
"There are no interfaces configured\n"));
return;
}
if (seed) {
fprintf(STDOUT, "\n\n %-32s %s\n",
MSGSTR(M_ZONE_DEF,"Defined zones"),
MSGSTR(M_IF_DEF_ZONE,"Interfaces Defining Zone"));
fprintf(STDOUT," %-32s %s\n",
"-------------",
"------------------------");
}
for (i=0; i<MAX_ZONES && seed; i++) {
int ifcnt=0;
if (!if_zones[i].zone_name.str[0])
break;
fprintf(STDOUT, "%c %-32s ", if_zones[i].zone_home ? '*' : ' ',
if_zones[i].zone_name.str);
for (k=0; k<IF_TOTAL_MAX; k++)
if (if_zones[i].zone_iflist.at_if[k][0]) {
if (ifcnt && !((ifcnt)%ZONE_IFS_PER_LINE))
fprintf(STDOUT,"\n%36s","");
ifcnt++;
fprintf(STDOUT, "%s ",
if_zones[i].zone_iflist.at_if[k]);
}
fprintf(STDOUT, "\n");
}
fprintf(STDOUT,
MSGSTR(M_HOME_Z_IND,
"\n* indicates home port and home zone\n"\
" (if home port is a seed port)\n"));
}
int checkSeeds(elapcfgp,zonep)
at_if_cfg_t elapcfgp[];
zone_usage_t zonep[];
{
int i,j;
int ok;
int error=FALSE;
for (i=0; i<IF_TOTAL_MAX; i++) {
if (!elapcfgp[i].ifr_name[0])
break;
if (!elapcfgp[i].netStart)
continue;
ok = FALSE;
for (j=0;j<MAX_ZONES; j++) {
if (!zonep[j].zone_name.str[0])
break;
if (in_list(elapcfgp[i].ifr_name,
&zonep[j].zone_iflist)) {
ok = TRUE;
break;
}
}
if (!ok) {
fprintf(stderr, MSGSTR(M_SEED_WO_ZONE,
"error, seed I/F without any zones: %s\n"),elapcfgp[i].ifr_name);
error = -1;
}
}
return(error);
}
int
showZones()
{
int status;
int size;
int if_id;
int done = FALSE;
ZT_entryno zte;
int did_header=0;
if ((if_id = socket(AF_APPLETALK, SOCK_RAW, 0)) < 0)
return(-1);
*(int *)&zte = 1;
while (1) {
size = sizeof(ZT_entryno);
status = at_send_to_dev(if_id, LAP_IOC_GET_ZONE, &zte, &size);
if (status <0)
switch (errno) {
case EINVAL:
done = TRUE;
break;
case 0:
break;
default:
fprintf(stderr, MSGSTR(M_RET_ZONES,
"showZones: error retrieving zone list\n"));
goto error;
}
if (done)
break;
if (!did_header++) {
fprintf(STDOUT, MSGSTR(M_ZONES,"..... Zones ......\n"));
fprintf(STDOUT, MSGSTR(M_ZONE_HDR,"zno zcnt zone\n"));
}
zte.zt.Zone.str[zte.zt.Zone.len] = '\0';
fprintf(STDOUT, "%3d %3d %s\n", zte.entryno+1,zte.zt.ZoneCount, zte.zt.Zone.str);
*(int *)&zte = 0;
}
if (*(int *)&zte == 1)
fprintf(STDOUT, MSGSTR(M_NO_ZONES,"no zones found\n"));
(void) close(if_id);
return(0);
error:
(void) close(if_id);
return(-1);
}
int
showRoutes()
{
int status;
int size;
int if_id;
int i,j;
int zcnt,gap;
int done = FALSE;
int did_header = FALSE;
RT_entry rt;
char state[10];
if ((if_id = socket(AF_APPLETALK, SOCK_RAW, 0)) < 0)
return(-1);
*(int *)&rt = 1;
state[9] = '\0';
while (1) {
size = sizeof(RT_entry);
status = at_send_to_dev(if_id, LAP_IOC_GET_ROUTE, &rt, &size);
if (status < 0)
switch (errno) {
case EINVAL:
done = TRUE;
break;
case 0:
break;
default:
fprintf(STDOUT, MSGSTR(M_RET_ROUTES,
"showRoutes: error retrieving route table\n"));
goto error;
}
if (done)
break;
if (!did_header++) {
fprintf(STDOUT, MSGSTR(M_ROUTES,
"............ Routes ................\n"));
fprintf(STDOUT, MSGSTR(M_NXT_STATE,
" next state\n"));
fprintf(STDOUT, MSGSTR(M_RTR_HDR,
"start-stop net:node d p PBTZ GSBU zones\n"));
}
gap = 0;
for (i=0; i<8; i++) {
if (i==4) {
gap =1;
state[4] = ' ';
}
state[i+gap] = (rt.EntryState & 1<<(7-i)) ? '1' : '0';
}
fprintf(STDOUT, "%5d-%-5d %5d:%-05d %2d %2d %s ",
rt.NetStart, rt.NetStop, rt.NextIRNet, rt.NextIRNode,
rt.NetDist, rt.NetPort, state);
zcnt = 0;
for (i=0; i<ZT_BYTES; i++) {
for (j=0; j<8; j++)
if ((rt.ZoneBitMap[i] <<j) & 0x80) {
if (zcnt >= Z_MAX_PRINT) {
fprintf(STDOUT, MSGSTR(M_MORE,",more..."));
i = ZT_BYTES;
break;
}
fprintf(STDOUT, zcnt ? ",%d" : " %d",i*8+j+1);
zcnt++;
}
}
fprintf(STDOUT, "\n");
*(int *)&rt = 0;
}
if (*(int *)&rt == 1)
fprintf(STDOUT, MSGSTR(M_NO_ROUTES,"no routes found\n"));
(void) close(if_id);
return(0);
error:
(void) close(if_id);
return(-1);
}
void aurpd(fd)
int fd;
{
ATgetmsg(fd, 0, 0, 0);
}