/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.0 (the 'License'). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License." * * @APPLE_LICENSE_HEADER_END@ */ /* Title: lap_init.c * * Facility: Generic AppleTalk Link Access Protocol Interface * (ALAP, ELAP, etc...) * * Author: Gregory Burns, Creation Date: April-1988 * ****************************************************************************** * * * Copyright (c) 1988, 1998 Apple Computer, Inc. * * * * The information contained herein is subject to change without * * notice and should not be construed as a commitment by Apple * * Computer, Inc. Apple Computer, Inc. assumes no responsibility * * for any errors that may appear. * * * * Confidential and Proprietary to Apple Computer, Inc. * * * ****************************************************************************** */ /* "@(#)lap_init.c: 2.0, 1.19; 2/26/93; Copyright 1988-92, Apple Computer, Inc." */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) catgets(catd, MS_LAP_INIT, num,str) */ #define MSGSTR(num,str) str /* multi-port setup defines */ #define MAX_ZONES 50 /* max cfg file zone count */ #define MAX_LINE_LENGTH 240 /* max size cfg file line */ #define INVALID_ZIP_CHARS "=@*:\377" #define MAX_NET_NO 0xFEFF /* max legal net number */ #define COMMENT_CHAR '#' /* version checking */ /* printout format defines */ #define ZONE_IFS_PER_LINE 6 /* # of if's to list per line for each zone for showCfg() */ #define Z_MAX_PRINT 15 /* maximum # zones to print */ at_ifnames_t seed_names; int seed_ind; #define IF_NO(c) (atoi(&c[2])) /* return i/f number from h/w name (e.g. 'et2' returns 2) */ char homePort[5]; /* name of home port */ extern int in_list(char *, at_ifnames_t *); /* prototypes */ void showCfg(); static FILE *STDOUT = stdout; /* These functions set/get the defaults in/from persistent storage */ /* PRAM state information */ #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); } /* save the current configuration in pram */ int at_savecfgdefaults(fd, ifName) int fd; char *ifName; { int tmp_fd = 0; at_if_cfg_t cfg; /* used to read 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)) { /* printf("at_getdefaultaddr: readx net number %d, node %d\n", init_address->s_net, init_address->s_node); */ 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)) { /* If we don't have a router */ return(0); /* return w/o writing to PRAM */ } 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; /* optional alternate router.cfg file */ short checkCfg; /* if true, just check config file */ short displayCfg; /* display configuration only */ short mh; /* multihoming mode of router */ /* obtains and checks zone and port configuration information from the configuration file. fills in the 2 arrays passed returns 0 if no problems were encountered. -1 if any error occurred */ { 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); } /* getConfigInfo */ int getIFInfo(elapcfgp, filecfgp, cfgFileName, checkCfg, displayCfg, mh) at_if_cfg_t elapcfgp[]; if_cfg_t *filecfgp; char *cfgFileName; /* optional alternate router.cfg file */ short checkCfg; /* if true, just check config file */ short displayCfg; /* display configuration only */ short mh; /* multihoming mode of router */ /* reads interface configuration information from source file and places valid entries into cfgp returns 0 on success -1 if any errors were encountered */ { FILE *pFin; /* input cfg file */ 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; /* true if entry had starting network # */ 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) /* if comment line */ continue; if (linein[0] == ':') /* if zone entry, skip */ continue; strcpy(buf,linein); pc1 = strtok(buf,":"); /* pc1 -> first parm */ parmno=0; bad = FALSE; gotNetStart = FALSE; done = FALSE; if (pc1) do { /* do loop start for current line */ if (sscanf(pc1," %s",buf1) != 1) /* skip whitespace */ break; switch(parmno) { case 0: /* I/F name */ /* used is a list of interfaces to keep track of whether the interface has been used */ 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: /* net range or home desig */ case 2: if (sscanf(buf1,"%d",&x) != 1){ if (buf1[0] == '*') { /* must be home then */ if (home++) { sprintf(errbuf, MSGSTR(M_MULT_HOME, "multiple home designations (%d) "), parmno); bad = TRUE; } elapcfgp[ifno].flags = ELAP_CFG_HOME; done = TRUE; /* '*' must be last */ sprintf(homePort,elapcfgp[ifno].ifr_name); } else { sprintf(errbuf, MSGSTR(M_BAD_FMT, "invalid format ")); bad = TRUE; } } else { /* this is an actual net number */ 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] == '*') { /* only valid entry for parm 3 */ 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; } /* fall through */ default: fprintf(stderr, "extra input ignored:(%s)\n", buf1); done = TRUE; } /* end switch */ if (bad) break; parmno++; } while(!done && pc1 && (pc1 = strtok(NULL,":"))); if (!bad) { if (elapcfgp[ifno].ifr_name[0]) { /* if entry used, check it */ if (gotNetStart) { if (!elapcfgp[ifno].netEnd) /* if no end, end = start */ elapcfgp[ifno].netEnd = elapcfgp[ifno].netStart; /* check for range conflicts */ for (i=0; i= 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) { /* set used here */ 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) { /* identify as seed */ strcpy(seed_names.at_if[seed_ind++], elapcfgp[ifno].ifr_name); } ifno++; } } /* end if if_name */ } if (bad ) { /* reset this entry */ memset(&elapcfgp[ifno], NULL,sizeof(elapcfgp[ifno])); if (checkCfg) /* finish error msg */ 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; } /* end if bad */ } 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); } /* getIFInfo */ int getZoneInfo(zonep, filecfgp, cfgFileName, checkCfg) zone_usage_t zonep[]; if_cfg_t *filecfgp; char *cfgFileName; /* optional alternate router.cfg file */ short checkCfg; /* if true, just check config file */ /* reads zone information from configuration file and places valid entries into zonep returns 0 on success -1 if any errors were encountered */ { FILE *pFin; /* input cfg file */ 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) /* if comment line */ continue; if (linein[0] != ':') /* zone entries start with ':' */ continue; strcpy(buf,linein); pc1 = strtok(&buf[1],":"); parmno=0; if (!bad) { zoneno++; z_ind = 0; } bad = FALSE; done = FALSE; if (pc1) do { /* do loop start for current line */ if (sscanf(pc1," %[^:\n]",buf1) != 1) /* skip whitespace */ break; switch(parmno) { case 0: /* zone name */ strcpy(curzone,buf1); /* chk length */ if ((len = strlen(buf1)) > NBP_NVE_STR_SIZE) { sprintf(errbuf, MSGSTR(M_ZONE_TOO_LONG, "zone name too long")); bad = TRUE; break; } /* chk for bad chars */ if (pc2 = strpbrk(buf1, INVALID_ZIP_CHARS)) { sprintf(errbuf, MSGSTR(M_IVAL_CHAR, "invalid character in zone name (%c)"),*pc2); bad = TRUE; break; } /* check for dupe zones */ for (i=0; i < zoneno; i++) { strcpy(tbuf1, buf1); strcpy(tbuf2, zonep[i].zone_name.str); /* compare same case */ for (j=0; jhome_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 { /* not home, must be another I/F */ /* check for valid type and make sure it has been defined too */ 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; } } /* end switch */ parmno++; if (bad) { /* finish error msg */ 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 no home zone set and home port is seed type, check to see if there is only one zone assigned to that i/f. If so, then make it the home zone */ if (gotHomePort && !home && (in_list(filecfgp->home_if, &seed_names))) { j = -1; /* j will be set to home zone */ for (i=0; ihome_if,&zonep[i].zone_iflist)) if (j >=0 ) { home = 0; /* more than one zone assigned to home port, we can't assume correct one */ break; } else { home = 1; j = i; /* save 1st zone found */ } } 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); } /* getZoneInfo */ 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[]; /* zone info from cfg file; not used in multihoming mode */ { int i,k, cnt=0, seed=FALSE; char range[40]; qsort((void*)cfgp, IF_TOTAL_MAX, sizeof(*cfgp), ifcompare); for (i=0; i= Z_MAX_PRINT) { fprintf(STDOUT, MSGSTR(M_MORE,",more...")); i = ZT_BYTES; break; } fprintf(STDOUT, zcnt ? ",%d" : " %d",i*8+j+1); /* 1st zone is 1 not 0 */ 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); }