/* * Copyright (c) 1988-91 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. */ #include #include #include #include #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 ATOL(str) strtol(str, (char **) NULL, 0) static char *usage = " Startup in single port mode:\n\ -u Bring up in single-interface mode.\n\ modifier:\n\ -q don't ask for zones (non-interactive mode)\n\ Startup for multiple ports:\n\ -r bring up Appletalk in routing mode\n\ -x bring up Appletalk in multihoming mode\n\ modifiers:\n\ -f \n\ -c check config file only\n\ -e check & display configuration only\n\ -q don't ask for zones (non-interactive mode)\n\ -v maximum routing table entries\n\ -w maximum zip table entries\n\ Other commands:\n\ -d shut down AppleTalk\n\ -h check/modify default zone\n\ -n print network number and node id\n\ -p print saved PRAM AppleTalk information\n\ -s show statistics & error counts\n\ Other routing commands:\n\ -j print router status\n\ -m maximum routing (pack/sec)\n\ -t show routing table\n\ -z show zone list\n\ " ; extern char *optarg; static char *et_interface = AT_DEF_ET_INTERFACE; static char *progname = NULL; static int router = 0; static at_if_cfg_t elapcfg[IF_TOTAL_MAX]; static zone_usage_t if_zones[MAX_ZONES]; /* zone info from cfg file */ static at_if_cfg_t cfg; /* configuration to use for printing status */ static at_router_params_t rt_param = {0, 0, 0, 0}; static char *cfgFileName; /* optional alternate router.cfg file */ static char *computerName = NULL; /* computer name, if specified */ static short checkCfg; /* if true, just check config file */ static short displayCfg; /* display configuration only */ extern int getConfigInfo(at_if_cfg_t elapcfgp[], zone_usage_t zonep[], char *cfgFileName, short checkCfg, short displayCfg, short mh), showRoutes(), showZones(); static int do_init(), do_shutdown(), register_this_node(int), showRouterStats(), print_statistics(), print_pram_info(char *); static void displayZoneDef(int, at_if_cfg_t *), print_routerid(at_if_cfg_t *), print_nodeid(at_if_cfg_t *); static FILE *STDOUT = stdout; /* *** Create a header file in /System/Developer/Headers later? *** */ #include #define AT_CMD_SUCCESS EX_OK /* success */ #define AT_CMD_PERMISSION_DENIED EX_NOPERM #define AT_CMD_OPTION_ERROR EX_USAGE #define AT_CMD_MISSING_CFG_FILE EX_NOINPUT #define AT_CMD_INTERFACE_ERROR EX_UNAVAILABLE #define AT_CMD_CONFIG_ERROR EX_OSFILE #define AT_CMD_SYSTEM_ERROR EX_OSERR #define AT_CMD_ALREADY_RUNNING EX__MAX + 10 #define AT_CMD_NOT_RUNNING EX__MAX + 11 #define AT_CMD_NBP_REG_ERROR EX__MAX + 12 #define AT_CMD_LZONE_NOT_SET EX__MAX + 13 #define AT_CMD_ROUTER_ERROR EX__MAX + 14 /* *** end appletalk exit codes *** */ #define IFR_NEXT(ifr) \ ((struct ifreq *) ((char *) (ifr) + sizeof(*(ifr)) + \ MAX(0, (int) (ifr)->ifr_addr.sa_len - (int) sizeof((ifr)->ifr_addr)))) #define MAX_IF 16 int main(argc, argv) int argc; char *argv[]; { int opterr; int ch; int fd; at_state_t global_state; int no_option = 1, C_option = 0, d_option = 0, r_option = 0, j_option = 0, h_option = 0, /* check/modify future default zone */ v_option = 0, w_option = 0, z_option = 0, t_option = 0, m_option = 0, u_option = 0, n_option = 0, s_option = 0, p_option = 0, q_option = 0; /* don't ask for zones */ int zone_prompt = 0; int router_mix = 0; int flag = 0; /* used on AIOCSTOPATALK to force shutdown */ int ret; /* used for do_init() return value */ checkCfg = 0; displayCfg = 0; cfgFileName = NULL; q_option = FALSE; /* default, ask for zones on startup */ /* find out what this program is called and other miscellaneous stuff */ progname = argv[0]; /* check whether at least one argument was provided */ opterr = argc < 2; /* process the arguments */ while (!opterr && (ch = getopt(argc, argv, "C:cdef:hjm:npqrstu:v:w:xz")) != EOF) { no_option = 0; switch (ch) { /* option '-C': computer name */ case 'C': C_option++; computerName = optarg; break; /* option '-h': check/modify future default zone */ case 'h': h_option++; break; /* option '-j': print router stats */ case 'j': j_option++; break; /* option '-n': print node and network */ case 'n': n_option++; break; /* option '-u': start the network up */ case 'u': u_option++; et_interface = optarg; break; /* option '-q': run quiet, don't ask for zones */ case 'q': q_option = TRUE; break; /* option '-d': stop AppleTalk stack */ case 'd': d_option++; break; /* option '-s': print statistics and error counts */ case 's': s_option++; break; case 'p': p_option++; break; /* router command only */ /* option '-r': start the network up - router mode*/ case 'r': r_option++; break; /* option '-x': start the network up - multihoming*/ case 'x': rt_param.multihome = 1; break; /* option '-e': display config only */ case 'e': displayCfg = TRUE; break; /* option '-f': specify router config file */ case 'f': cfgFileName = optarg; break; /* option '-c': test config file only */ case 'c': checkCfg = TRUE; break; /* option '-m': mix of routing vs home stack selection*/ case 'm': m_option++; rt_param.router_mix = ATOL(optarg) & 0xFFFF; router_mix = (int)(ATOL(optarg) & 0xFFFF); break; case 'v': v_option++; rt_param.rtmp_table_sz = ATOL(optarg) & 0xFFFF; if (!rt_param.rtmp_table_sz) rt_param.rtmp_table_sz = RT_DEFAULT; else if (rt_param.rtmp_table_sz < RT_MIN) rt_param.rtmp_table_sz = RT_MIN; else if (rt_param.rtmp_table_sz > RT_MAX) rt_param.rtmp_table_sz = RT_MAX; else /* it's correct */ break; fprintf(stderr, "%s: Routing table size should be between %d and %d; using %d\n", progname, RT_MIN, RT_MAX, rt_param.rtmp_table_sz); break; /* option '-w': zip table size*/ case 'w': w_option++; rt_param.zone_table_sz = ATOL(optarg) & 0xFFFF; if (!rt_param.zone_table_sz) rt_param.zone_table_sz = ZT_DEFAULT; else if (rt_param.zone_table_sz < ZT_MIN) rt_param.zone_table_sz = ZT_MIN; else if (rt_param.zone_table_sz > ZT_MAX) rt_param.zone_table_sz = ZT_MAX; else /* it's correct */ break; fprintf(stderr, "%s: Zone table size should be between %d and %d; using %d\n", progname, ZT_MIN, ZT_MAX, rt_param.zone_table_sz); break; /* option '-t' display routing table */ case 't': t_option++; break; /* option '-z' display zone table */ case 'z': z_option++; break; default: opterr++; break; } }/* of while */ /* error-check the options */ if (u_option || d_option || r_option || rt_param.multihome || C_option || h_option || m_option) { /* single-port startup, stop, router startup, multihome startup, setting the computer name, and changing the default zone require root access */ if (getuid () != 0) { fprintf (stderr, "%s: Permission denied; must be super-user.\n", progname); exit(AT_CMD_PERMISSION_DENIED ); } } if ((u_option + r_option + rt_param.multihome) > 1) { fprintf(stderr, "%s: Only one of [-u -r -x] may be used at the same time.\n", progname); opterr++; } if (u_option || r_option || rt_param.multihome) { if (d_option) { fprintf(stderr, "%s: -%c and -d options are incompatible\n", progname, (r_option)? 'r': (rt_param.multihome)? 'x' : 'u'); opterr++; } if (u_option && (checkCfg || displayCfg || cfgFileName || v_option || w_option)) { fprintf(stderr, "%s: -c, -e, -f, -v and -w can only be used with -r or -x option\n", progname); opterr++; } } else { if (q_option) { fprintf(stderr, "%s: -q can only be used with -u, -r, or -x option\n", progname); opterr++; } if (checkCfg || displayCfg || cfgFileName || v_option || w_option) { fprintf(stderr, "%s: -c, -e, -f, -v and -w can only be used with -r or -x option\n", progname); opterr++; } } if (opterr || no_option) { printf(usage, progname); exit(AT_CMD_OPTION_ERROR); } /* process options which start Appletalk */ if (u_option || r_option || rt_param.multihome) { if (checkATStack() == RUNNING) { fprintf(stderr,"The AppleTalk stack is already running.\n"); exit(AT_CMD_ALREADY_RUNNING); } else { if (r_option || rt_param.multihome) { router = TRUE; if (!cfgFileName) /* alternate cfg file specified? */ if (rt_param.multihome) cfgFileName = MH_CFG_FILE; else cfgFileName = AT_CFG_FILE; /* does cfg file exist? */ if (access(cfgFileName,0)) { fprintf(stderr, "Error, configuration file %s not found\n", cfgFileName); exit(AT_CMD_MISSING_CFG_FILE); } } if ((ret = do_init())) { exit(ret); } if (checkCfg || displayCfg) { exit(AT_CMD_SUCCESS); } /* if not -q, ask for default zone */ if (!q_option) zone_prompt = TRUE; } } /* Either Appletalk was just started, or it should have been started before. */ if ((fd = socket(AF_APPLETALK, SOCK_RAW, 0)) < 0) exit(AT_CMD_SYSTEM_ERROR); if ((ioctl(fd, AIOCGETSTATE, (caddr_t)&global_state)) < 0) { (void)close(fd); exit(AT_CMD_SYSTEM_ERROR); } if (global_state.flags & AT_ST_STARTED) { if (d_option) { exit(do_shutdown()); } if (zone_prompt || h_option || n_option || s_option || j_option) { if ((!(global_state.flags & AT_ST_MULTIHOME)) && (s_option || n_option || h_option || zone_prompt)) { /* if in routing and single-port modes, only get the default interface */ cfg.ifr_name[0] = '\0'; if (ioctl(fd, AIOCGETIFCFG, (caddr_t)&cfg) < 0) { (void)close(fd); exit(AT_CMD_SYSTEM_ERROR); } /* if there's room, terminate the zone string for printing */ if (cfg.zonename.len < NBP_NVE_STR_SIZE) cfg.zonename.str[cfg.zonename.len] = '\0'; if (h_option || zone_prompt) displayZoneDef(fd, &cfg); if (s_option || n_option) print_nodeid(&cfg); if (s_option & (!(global_state.flags & AT_ST_ROUTER))) print_routerid(&cfg); } else { /* for each interface that is configured for Appletalk */ struct ifaddrs *ifaddrs, *ifa; at_if_cfg_t cfg; if (getifaddrs(&ifaddrs) < 0) exit(AT_CMD_SYSTEM_ERROR); /* getifaddrs properly set errno */ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { unsigned char *p, c; if (ifa->ifa_addr->sa_family != AF_APPLETALK) continue; if (*ifa->ifa_name == '\0') continue; /* * Adapt to buggy kernel implementation (> 9 of a type) */ p = &ifa->ifa_name[strlen(ifa->ifa_name)-1]; if ((c = *p) > '0'+9) sprintf(p, "%d", c-'0'); strcpy(cfg.ifr_name, ifa->ifa_name); if (ioctl(fd, AIOCGETIFCFG, (caddr_t)&cfg) < 0) continue; /* if there's room, terminate the zone string for printing */ if (cfg.zonename.len < NBP_NVE_STR_SIZE) cfg.zonename.str[cfg.zonename.len] = '\0'; if (h_option || zone_prompt) displayZoneDef(fd, &cfg); else if (n_option || s_option) print_nodeid(&cfg); else if (j_option) showRouterStats(); } freeifaddrs(ifaddrs); } if (s_option) (void)print_statistics(); } /* register the node after the default zone has been selected */ if (u_option || r_option || rt_param.multihome) if (register_this_node(fd) != 0) { fprintf(stderr, "%s: node registration failed\n", progname); /* Commented out for now, because this doesn't have to be a fatal error. (void)ioctl(fd, AIOCSTOPATALK, (caddr_t)&flag); close(fd); fprintf(stderr, "Failed to start the AppleTalk stack.\n"); exit(AT_CMD_NBP_REG_ERROR); */ } if (t_option) { showRoutes(); } if (z_option) { showZones(); } if (m_option && !r_option) { size_t size = (size_t)sizeof(int); /* set the router mix in the kernel */ if (sysctlbyname("net.appletalk.routermix", 0, 0, (void *)&router_mix, size) < 0) { fprintf(stderr, "%s: error setting router mix in kernel\n", progname); exit(AT_CMD_SYSTEM_ERROR); } } } else { /* not RUNNING */ if (!p_option) { fprintf(stderr, "The AppleTalk stack is not running.\n"); exit(AT_CMD_NOT_RUNNING); } } if (p_option) { /* for each interface that is configured for Appletalk */ struct ifaddrs *ifaddrs, *ifa, *ifa2; if (getifaddrs(&ifaddrs) < 0) exit(AT_CMD_SYSTEM_ERROR); /* getifaddrs properly set errno */ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { unsigned char *p, c; /* skip duplicate names */ for (ifa2 = ifaddrs; ifa2 < ifa; ifa2 = ifa2->ifa_next) { if (strncmp(ifa2->ifa_name, ifa->ifa_name, sizeof(ifa->ifa_name)) == 0) break; } if (ifa2 < ifa) continue; if (*ifa->ifa_name == '\0') continue; /* * Adapt to buggy kernel implementation (> 9 of a type) */ p = &ifa->ifa_name[strlen(ifa->ifa_name)-1]; if ((c = *p) > '0'+9) sprintf(p, "%d", c-'0'); (void)print_pram_info(ifa->ifa_name); } freeifaddrs(ifaddrs); } (void)close(fd); exit(AT_CMD_SUCCESS); } /* main */ static void print_routerid(cfg) at_if_cfg_t *cfg; { printf("\tBridge net ...................... %u (0x%x)\n", cfg->router.s_net, cfg->router.s_net); printf("\tBridge number ................... %u (0x%x)\n", cfg->router.s_node, cfg->router.s_node); } static int print_pram_info(ifName) char *ifName; { at_nvestr_t zonename; struct at_addr netnumber; char zone[sizeof(zonename.str+1)]; if (!ifName || !strlen(ifName)) return(-1); if (at_getdefaultzone(ifName, &zonename) < 0) return(-1); if (at_getdefaultaddr(ifName, &netnumber) < 0) return(-1); if (zonename.len == 0) strcpy(zone, "*"); else { strncpy(zone, zonename.str, zonename.len); zone[zonename.len] = '\0'; } printf("\n\tAppleTalk interface.............. %s\n", ifName); printf("\tPRAM default zonename ........... %s\n", zone); printf("\tPRAM netnumber .................. %u (%#x)\n", netnumber.s_net, netnumber.s_net); printf("\tPRAM node id .................... %u (%#x)\n", netnumber.s_node, netnumber.s_node); return(0); } /* print_pram_info */ static void print_nodeid(cfg) at_if_cfg_t *cfg; { printf("\n"); printf("\tAppleTalk interface.............. %s\n", cfg->ifr_name); printf("\tNetwork Number .................. %u (0x%x)\n", cfg->node.s_net, cfg->node.s_net); printf("\tNode ID ......................... %u (0x%x)\n", cfg->node.s_node, cfg->node.s_node); /* for now, we'll ignore the possibility of null characters embedded in the zonename (as we have been in the past */ printf("\tCurrent Zone .................... %s\n", cfg->zonename.str); } static int print_statistics() { at_ddp_stats_t ddp_stats; int size = sizeof(ddp_stats); /* read the value of ddpstats from the kernel */ if (sysctlbyname("net.appletalk.ddpstats", (void *)&ddp_stats, &size, 0, 0) < 0) { fprintf(stderr, "%s: Can't get the network statistics (%s)\n", progname, sys_errlist[errno]); return(-1); } printf("\n DDP statistics:\n\n"); printf("\tPackets Transmitted ............. %u\n", ddp_stats.xmit_packets); printf("\tBytes Transmitted ............... %u\n", ddp_stats.xmit_bytes); printf("\tBest Router Cache used (pkts) ... %u\n", ddp_stats.xmit_BRT_used); printf("\tPackets Received ................ %u\n", ddp_stats.rcv_packets); printf("\tBytes Received .................. %u\n", ddp_stats.rcv_bytes); printf("\tPackets for unregistered socket . %u\n", ddp_stats.rcv_unreg_socket); printf("\tPackets for out of range socket . %u\n", ddp_stats.rcv_bad_socket); printf("\tLength errors ................... %u\n", ddp_stats.rcv_bad_length); printf("\tChecksum errors ................. %u\n", ddp_stats.rcv_bad_checksum); printf("\tPackets dropped (no buffers) .... %u\n", ddp_stats.rcv_dropped_nobuf + ddp_stats.xmit_dropped_nobuf); return(0); } int routerStartup(s) int s; { at_kern_err_t ke; /* start the router */ memset(&ke, 0, sizeof(ke)); if ((ioctl(s, AIOCSTARTROUTER, (caddr_t)&ke)) < 0) { fprintf(stderr, "%s: AIOCSTARTROUTER failed (%d)\n", progname, errno); return(-1); } else if (ke.error) { switch (ke.error) { case KE_CONF_RANGE: fprintf(stderr, "Conflict between port %d (%s) and port %d (%s)\n\ they are using the same net range (%d-%d)\n", ke.port1, ke.name1, ke.port2, ke.name2, ke.netr1b, ke.netr1e); break; case KE_CONF_SEED_RNG: fprintf(stderr, "Conflict on port %d (%s): Router %d:%d seeds net %d-%d\n\ and not %d:%d as asked in our configuration\n", ke.port1, ke.name1, ke.net, ke.node, ke.netr1b, ke.netr1e, ke.netr2b, ke.netr2e); break; case KE_CONF_SEED1: fprintf(stderr, "Conflict on port %d (%s): Router %d seeds net %d\n\ and not %d as asked in our configuration\n", ke.port1, ke.name1, ke.node, ke.netr1e, ke.netr2e); break; case KE_CONF_SEED_NODE: fprintf(stderr, "Conflict on port %d (%s): Node %d:%d seeds %d:%d instead of %d:%d\n", ke.port1, ke.name1, ke.net, ke.node, ke.netr1b, ke.netr1e, ke.netr2b, ke.netr2e); break; case KE_NO_ZONES_FOUND: fprintf(stderr, "No Zones names received for Port %d (%s) on net %d:%d\n", ke.port1, ke.name1, ke.netr1b, ke.netr1e); break; case KE_NO_SEED: fprintf(stderr, "No seed information for port %d (%s) was found on the net\n", ke.port1, ke.name1); break; case KE_INVAL_RANGE: fprintf(stderr, "Port %d (%s) is using an invalid network range (%d:%d)\n", ke.port1, ke.name1, ke.netr1b, ke.netr1e); break; case KE_SEED_STARTUP: fprintf(stderr, "Problem, port %d (%s) Router %d:%d seeds in the startup range\n", ke.port1, ke.name1, ke.net, ke.node); break; case KE_BAD_VER: fprintf(stderr, "Received a bad version (v%d) RTMP packet from node %d:%d\n", ke.rtmp_id, ke.net, ke.node); break; case KE_RTMP_OVERFLOW: fprintf(stderr, "RTMP Route Table overflow. Too many routes. Increase RTMP Table size > %d\n", (rt_param.rtmp_table_sz)? rt_param.rtmp_table_sz: RT_DEFAULT); break; case KE_ZIP_OVERFLOW: fprintf(stderr, "ZIP Zone Table overflow. Too many zones. Increase ZIP Table size > %d\n", (rt_param.zone_table_sz)? rt_param.zone_table_sz: ZT_DEFAULT); break; default: fprintf(stderr, "Unknown kernel error code:%d\n", ke.error); } return(-1); } return(0); } /* routerStartup */ static int get_if_flags(int s, char * ifname, short * flags) { struct ifreq ifr; int ret; bzero(&ifr, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); ret = ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr); if (ret < 0) { perror("SIOCGIFFLAGS"); return (ret); } *flags = ifr.ifr_flags; return (0); } static int set_if_flags(int s, char * ifname, short flags) { struct ifreq ifr; int ret; bzero(&ifr, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); ifr.ifr_flags = flags; ret = ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr); if (ret < 0) { perror("SIOCSIFFLAGS"); return (ret); } return (0); } static int mark_interface_up(char * ifname) { short flags = 0; int ret = FALSE; int s = -1; if ((s = socket(AF_APPLETALK, SOCK_RAW, 0)) < 0) { perror("socket"); goto error; } if (get_if_flags(s, ifname, &flags) != 0) { goto error; } if (flags & IFF_UP) ; else { flags |= IFF_UP; if (set_if_flags(s, ifname, flags) != 0) { goto error; } } ret = TRUE; error: if (s >= 0) { close(s); } return (ret); } /* do_init() returns exit code */ int do_init() { int i, s, ret = 1; char *p; struct ifreq ifr; int flag = 0; /* used on AIOCSTOPATALK to force shutdown */ /* get the device information: from the config file, for router and multihome mode, and from the command line, for single-port mode */ memset(&elapcfg[0], 0, sizeof(elapcfg)); if (router) { /* read & validate config file */ if (getConfigInfo(elapcfg, if_zones, cfgFileName, checkCfg, displayCfg, rt_param.multihome)) { return(AT_CMD_CONFIG_ERROR); } if (checkCfg) printf("Configuration file checked.\n"); if (displayCfg || checkCfg) return(AT_CMD_SUCCESS); /* if just checking cfg, we passed */ } else { at_nvestr_t zone_name; struct at_addr init_addr; elapcfg[0].flags |= ELAP_CFG_HOME; strcpy(elapcfg[0].ifr_name, et_interface); /* in single-port mode, if a default zone can be read from the nvram file for the interface, it will be sent to the kernel via the AIOCSIFADDR IOCTL */ if (at_getdefaultzone(et_interface, &zone_name) == 0) if (!DEFAULT_ZONE(&zone_name)) { elapcfg[0].zonename = zone_name; /* Check if we can reuse the same net/node address we have saved. Don't try to reuse the old address unless there's a good zone to go with it. */ if (at_getdefaultaddr(et_interface, &init_addr) == 0) elapcfg[0].node = init_addr; } } /* open AppleTalk control socket */ if ((s = socket(AF_APPLETALK, SOCK_RAW, 0)) < 0) { return(AT_CMD_SYSTEM_ERROR); } /* Past this point, we have to stop AppleTalk if a failure occurs. ...So that it won't be left in a partially initialized state. */ /* set routing/multihome parameters */ if (router) { if (ioctl(s, AIOCSETROUTER, (caddr_t)&rt_param) < 0) { #ifdef APPLETALK_DEBUG fprintf(stderr, "%s: AIOCSETROUTER returned %s (%d)\n", progname, sys_errlist[errno], errno); #endif ret = AT_CMD_SYSTEM_ERROR; goto error; } } /* configure all of the interfaces up */ for (i=0; elapcfg[i].ifr_name[0] ; i++) { /* make sure that the interface name is valid */ for (p = elapcfg[i].ifr_name; *p != '\0'; p++) { if (isdigit(*p)) break; } if (*p == '\0' || !isdigit(*p)) { fprintf(stderr, "%s: %s: bad device name\n", progname, elapcfg[i].ifr_name); ret = AT_CMD_INTERFACE_ERROR; goto error; } /* do an ifconfig on the interface name */ if (mark_interface_up(elapcfg[i].ifr_name) == FALSE) { #ifdef APPLETALK_DEBUG fprintf(stderr, "%s: mark_interface_up(%s) failed\n", progname, elapcfg[i].ifr_name); #endif ret = AT_CMD_INTERFACE_ERROR; goto error; } /* Initialize the AT kernel structures associated with the interface using SIOCSIFADDR and AIOCSIFADDR IOCTLs */ strncpy(ifr.ifr_name, elapcfg[i].ifr_name, sizeof(ifr.ifr_name)); ifr.ifr_addr.sa_family = AF_APPLETALK; if (ioctl(s, SIOCSIFADDR, (caddr_t)&ifr) < 0) { fprintf(stderr, "%s: SIOCSIFADDR for %s returned %s (%d)\n", progname, ifr.ifr_name, sys_errlist[errno], errno); ret = AT_CMD_INTERFACE_ERROR; goto error; } if (ioctl(s, AIOCSIFADDR, (caddr_t)&elapcfg[i]) < 0) { fprintf(stderr, "%s: AIOCSIFADDR for %s returned %s (%d)\n", progname, elapcfg[i].ifr_name, sys_errlist[errno], errno); switch (errno) { case EEXIST : fprintf(stderr, "%s: another home port already designated\n", progname); break; case EACCES : fprintf(stderr, "%s: permission denied\n", progname); break; case EPERM : fprintf(stderr, "%s: port already up, can't designate as home port\n", progname); break; case EINVAL : fprintf(stderr, "%s: invalid interface specified\n", progname); break; case EFAULT : fprintf(stderr, "%s: can't change range in current i/f state\n", progname); break; case EOPNOTSUPP : fprintf(stderr, "%s: error registering packet type\n", progname); break; case EALREADY : fprintf(stderr, "%s: interface %s is already running\n", progname, et_interface); break; default : fprintf(stderr, "%s: %s (%d)\n", progname, sys_errlist[errno], errno); break; } ret = AT_CMD_INTERFACE_ERROR; goto error; } } /* communicate zone info to kernel */ if (router) { if (!rt_param.multihome) { /* in router mode, add zones for interfaces being seeded */ for (i=0; i < MAX_ZONES && if_zones[i].zone_name.len; i++) { if (ioctl(s, AIOCSETZNUSAGE, (caddr_t)&if_zones[i])) { /* if length permits null terminate string for printing */ if (if_zones[i].zone_name.len < NBP_NVE_STR_SIZE) if_zones[i].zone_name.str[if_zones[i].zone_name.len] = '\0'; fprintf(stderr, "%s: error adding zone %s\n", progname, if_zones[i].zone_name.str); ret = AT_CMD_ROUTER_ERROR; goto error; } } } if (routerStartup(s) < 0) { ret = AT_CMD_ROUTER_ERROR; goto error; } } else { /* in single-port mode, local zones need to be set so that the kernel will be able to figure out whether a zone is local, when an NBP entity is registered later. */ char buf[ATP_DATA_SIZE+1], *cp; at_nvestr_t *zp; int count = 0, context = ZIP_FIRST_ZONE; while (context != ZIP_NO_MORE_ZONES) { if ((count = zip_getlocalzones(ZIP_DEF_INTERFACE, &context, &(buf[0]), (ATP_DATA_SIZE+1))) > 0) { for (i=0, cp=buf, zp = (at_nvestr_t *)cp; i < count && zp->len; i++, cp += (zp->len+1), zp = (at_nvestr_t *)cp) { /* feed local zone to NBP, in the kernel */ if (ioctl(s, AIOCREGLOCALZN, (caddr_t)zp)) { fprintf(stderr, "error setting localzones %d\n", errno); ret = AT_CMD_LZONE_NOT_SET; goto error; } } } else { break; } } } /* set and/or save default zones from/in persistent storage */ for (i=0; elapcfg[i].ifr_name[0] ; i++) { if ((elapcfg[i].flags & ELAP_CFG_HOME) || rt_param.multihome) { /* in router / multihome mode, if a default zone can be read from the nvram file for the interface, the kernel is updated after startup has taken place */ if (router) { at_nvestr_t nvramzone; if (!at_getdefaultzone(elapcfg[i].ifr_name, &nvramzone)) { at_def_zone_t defzone; strcpy(defzone.ifr_name, elapcfg[i].ifr_name); defzone.zonename = nvramzone; if ((ioctl(s, AIOCSETDEFZONE, (caddr_t)&defzone))) { if (nvramzone.len < NBP_NVE_STR_SIZE) nvramzone.str[nvramzone.len] = '\0'; fprintf(stderr, "%s: AIOCSETDEFZONE failed for %s %s\n", progname, elapcfg[i].ifr_name, nvramzone.str); } } } /* in any case, save the values AppleTalk came up with in the nvram file, for next time */ (void)at_savecfgdefaults(s, elapcfg[i].ifr_name); } } close(s); return(AT_CMD_SUCCESS); error: (void)ioctl(s, AIOCSTOPATALK, (caddr_t)&flag); (void)close(s); fprintf(stderr, "Failed to start the AppleTalk stack.\n"); return(ret); } /* do_init */ /* do_shutdown() returns exit code */ static int do_shutdown() { int s; int flag = 0; /* used on AIOCSTOPATALK to force shutdown */ if ((s = socket(AF_APPLETALK, SOCK_RAW, 0)) < 0) { return(AT_CMD_SYSTEM_ERROR); } else { if ((ioctl(s, AIOCSTOPATALK, (caddr_t)&flag)) < 0) { if (errno == EACCES) fprintf(stderr, "%s: permission denied\n", progname); else fprintf(stderr, "%s: error: %s\n", progname, sys_errlist[errno]); close(s); return(AT_CMD_SYSTEM_ERROR); } } close(s); return(AT_CMD_SUCCESS); } /* do_shutdown */ static int register_this_node(fd) int fd; /* control socket */ { struct utsname u_name; int ret = 0; at_nbp_reg_t reg; at_retry_t retry = {1, 1, 1}; /* interval, max retries, backoff */ if (uname(&u_name) < 0) { fprintf(stderr, "%s: uname() failed\n", progname); return (-1); } nbp_make_entity(®.name, u_name.nodename, u_name.sysname, "*"); if (computerName) { int len; len = strlen(computerName); if ((len > 2) && (computerName[0] == '*') && (computerName[len-1] == '*') && ((len & 1) == 0)) { /* if encoded string markers are present and len OK */ char buf[NBP_NVE_STR_SIZE]; int cp, bp; len = (len - 2) / 2; memset(&buf[0], 0, sizeof(buf)); for (cp = 1, bp = 0; bp < len; cp += 2, bp++) { int byte; if (sscanf(&computerName[cp], "%2x", &byte) != 1) goto proceed; buf[bp] = (char)byte; } nbp_make_entity(®.name, buf, u_name.sysname, "*"); } else { nbp_make_entity(®.name, computerName, u_name.sysname, "*"); } } proceed : if ((ret = nbp_reg_lookup(®.name, &retry)) != 0) { if (ret > 0) fprintf(stderr, "%s: identical node was previously registered\n", progname); #ifdef APPLETALK_DEBUG else fprintf(stderr, "%s: nbp_reg_lookup() failed\n", progname); #endif return (-1); } /* the net and node will be automatically filled in with address information from the default interface. */ reg.addr.net = 0; reg.addr.node = 0; reg.addr.socket = DDP_SOCKET_1st_DYNAMIC; reg.ddptype = 0; if ((ioctl(fd, AIOCNBPREG, (caddr_t)®)) < 0) { fprintf(stderr, "%s: AIOCNBPREG failed\n", progname); return(-1); } return(0); } /* register_this_node */ static void displayZoneDef(fd, cfg) int fd; at_if_cfg_t *cfg; { #define DISPLAY_ZONES 100 at_nvestr_t *zp; at_def_zone_t defzone; u_char buf[ATP_DATA_SIZE+1]; char input_buff[80], zone[DISPLAY_ZONES][ZIP_MAX_ZONE_LENGTH], *cp; int start = ZIP_FIRST_ZONE, i = 0, times = 0, count = 0, zone_index = 0; if (!fd) return; printf("Current zone for interface %s is %s\n", cfg->ifr_name, cfg->zonename.str); while (times < DISPLAY_ZONES && zone_index < DISPLAY_ZONES && start != ZIP_NO_MORE_ZONES && count != -1) { if ((count = zip_getlocalzones(cfg->ifr_name, &start, buf, sizeof(buf))) > 0) { if (count && !zone_index) printf("Zones found:\n"); for (i=zone_index, cp=buf, zp = (at_nvestr_t *)cp; i < (count+zone_index) && i < DISPLAY_ZONES && zp->len; i++, cp += (zp->len+1), zp = (at_nvestr_t *)cp) { strncpy(zone[i], zp->str, zp->len); zone[i][zp->len] = '\0'; printf("%2d: %s\n", i+1, zone[i]); } } times++; zone_index += count; } if (zone_index > 1) { zone_index = 0; printf("\nEnter to exit.\n"); again: printf ("\nZone Number? "); gets(input_buff); if (input_buff[0] == '\0') return;; if (!isdigit(input_buff[strspn(input_buff, " \t")])) goto again; zone_index = atoi(input_buff); if (zone_index < 1 || zone_index > i) { printf("That's not a valid choice. Try again.\n"); goto again; } strcpy(defzone.ifr_name, cfg->ifr_name); strcpy(defzone.zonename.str, zone[zone_index-1]); defzone.zonename.len = strlen(zone[zone_index-1]); /* set the default in the config file, to be used next time appletalk is started */ (void)at_setdefaultzone(cfg->ifr_name, &defzone.zonename); /* change the default zone now, without restarting appletalk */ if ((ioctl(fd, AIOCSETDEFZONE, (caddr_t)&defzone)) < 0) { printf("AIOCSETDEFZONE errno = %d\n", errno); } else { printf("Default zone changed to %s\n", defzone.zonename.str); } } else { if (!zone_index) printf("No local zones found.\n"); } } /* displayZoneDef */ #define STATS_HEADER1 \ "\n-------- Appletalk Configuration -----------\n" #define STATS_HEADER2 \ " Network:\n" #define STATS_HEADER3 \ " I/F State Range Node Default Zone\n" #define STATS_HEADER4 \ " ---- ----------------- ----------- --------- -------------------------\n" int first = 0; static int showRouterStats() { u_char state = cfg.flags & LAP_STATE_MASK; if (!first++) fprintf(STDOUT, "%s%s%s%s", STATS_HEADER1, STATS_HEADER2, STATS_HEADER3, STATS_HEADER4); fprintf(STDOUT, "%c%-4s %-18s", (cfg.flags & AT_IFF_DEFAULT)? '*' : ' ', cfg.ifr_name, (state == LAP_OFFLINE)? "Offline": (state == LAP_ONLINE)? "Online": (state == LAP_ONLINE_FOR_ZIP)? "Online for ZIP": (state == LAP_ONLINE_ZONELESS)? "Online zoneless": "Unknown"); fprintf(STDOUT, "%5d-%-5d %5d:%-3d ", cfg.netStart, cfg.netEnd, cfg.node.s_net, cfg.node.s_node); fprintf(STDOUT, "%s\n", cfg.zonename.str); /* *** Should stats be printed too? *** */ return(0); } /* showRouterStats */