/* * Router rc control script * * Copyright 2006, Broadcom Corporation * All Rights Reserved. * * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. * * $Id$ */ #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 #include #include #include #include static void restore_defaults(void); static void sysinit(void); static void rc_signal(int sig); extern struct nvram_tuple router_defaults[]; static int build_ifnames(char *type, char *names, int *size) { char name[32], *next; int len = 0; int s; /* open a raw scoket for ioctl */ if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) return -1; /* * go thru all device names (wl il et vlan) and interfaces to * build an interface name list in which each i/f name coresponds to a device * name in device name list. Interface/device name matching rule is device * type dependant: * * wl: by unit # provided by the driver, for example, if eth1 is wireless * i/f and its unit # is 0, then it will be in the i/f name list if * wl0 is in the device name list. * il/et: by mac address, for example, if et0's mac address is identical to * that of eth2's, then eth2 will be in the i/f name list if et0 is * in the device name list. * vlan: by name, for example, vlan0 will be in the i/f name list if vlan0 * is in the device name list. */ foreach (name, type, next) { struct ifreq ifr; int i, unit; char var[32], *mac, ea[ETHER_ADDR_LEN]; /* vlan: add it to interface name list */ if (!strncmp(name, "vlan", 4)) { /* append interface name to list */ len += snprintf(&names[len], *size - len, "%s ", name); continue; } /* others: proceed only when rules are met */ for (i = 1; i <= DEV_NUMIFS; i ++) { /* ignore i/f that is not ethernet */ ifr.ifr_ifindex = i; if (ioctl(s, SIOCGIFNAME, &ifr)) continue; if (ioctl(s, SIOCGIFHWADDR, &ifr)) continue; if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) continue; if (!strncmp(ifr.ifr_name, "vlan", 4)) continue; /* wl: use unit # to identify wl */ if (!strncmp(name, "wl", 2)) { if (wl_probe(ifr.ifr_name) || wl_ioctl(ifr.ifr_name, WLC_GET_INSTANCE, &unit, sizeof(unit)) || unit != atoi(&name[2])) continue; } /* et/il: use mac addr to identify et/il */ else if (!strncmp(name, "et", 2) || !strncmp(name, "il", 2)) { snprintf(var, sizeof(var), "%smacaddr", name); if (!(mac = nvram_get(var)) || !ether_atoe(mac, ea) || bcmp(ea, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN)) continue; } /* mac address: compare value */ else if (ether_atoe(name, ea) && !bcmp(ea, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN)) ; /* others: ignore */ else continue; /* append interface name to list */ len += snprintf(&names[len], *size - len, "%s ", ifr.ifr_name); } } close(s); *size = len; return 0; } static void ses_cleanup(void) { /* well known event to cleanly initialize state machine */ nvram_set("ses_event", "2"); /* Delete lethal dynamically generated variables */ nvram_unset("ses_bridge_disable"); } static void ses_restore_defaults(void) { char tmp[100], prefix[] = "wlXXXXXXXXXX_ses_"; int i, j; int len = 0; /* Delete dynamically generated variables */ for (i = 0; i < MAX_NVPARSE; i++) { sprintf(prefix, "wl%d_ses_", i); nvram_unset(strcat_r(prefix, "ssid", tmp)); nvram_unset(strcat_r(prefix, "closed", tmp)); nvram_unset(strcat_r(prefix, "wpa_psk", tmp)); nvram_unset(strcat_r(prefix, "auth", tmp)); nvram_unset(strcat_r(prefix, "wep", tmp)); nvram_unset(strcat_r(prefix, "auth_mode", tmp)); nvram_unset(strcat_r(prefix, "crypto", tmp)); nvram_unset(strcat_r(prefix, "akm", tmp)); nvram_unset(strcat_r(prefix, "wds", tmp)); nvram_unset(strcat_r(prefix, "wds_timeout", tmp)); } /* Delete dynamically generated variables */ for (i = 0; i < DEV_NUMIFS; i++) { sprintf(prefix, "wl%d_wds", i); len = strlen(prefix); for (j = 0; j < MAX_NVPARSE; j++) { sprintf(&prefix[len], "%d", j); nvram_unset( prefix ); } } nvram_unset("ses_fsm_current_states"); nvram_unset("ses_fsm_last_status"); } static void virtual_radio_restore_defaults(void) { char tmp[100], prefix[] = "wlXXXXXXXXXX_mssid_"; int i,j; nvram_unset("unbridged_ifnames"); nvram_unset("ure_disable"); /* Delete dynamically generated variables */ for (i = 0; i < MAX_NVPARSE; i++) { sprintf(prefix, "wl%d_", i); nvram_unset(strcat_r(prefix, "vifs", tmp)); nvram_unset(strcat_r(prefix, "ssid", tmp)); nvram_unset(strcat_r(prefix, "guest", tmp)); nvram_unset(strcat_r(prefix, "ure", tmp)); nvram_unset(strcat_r(prefix, "ipconfig_index", tmp)); sprintf(prefix, "lan%d_", i); nvram_unset(strcat_r(prefix, "ifname", tmp)); nvram_unset(strcat_r(prefix, "ifnames", tmp)); nvram_unset(strcat_r(prefix, "gateway", tmp)); nvram_unset(strcat_r(prefix, "proto", tmp)); nvram_unset(strcat_r(prefix, "ipaddr", tmp)); nvram_unset(strcat_r(prefix, "netmask", tmp)); nvram_unset(strcat_r(prefix, "lease", tmp)); sprintf(prefix, "dhcp%d_", i); nvram_unset(strcat_r(prefix, "start", tmp)); nvram_unset(strcat_r(prefix, "end", tmp)); /* clear virtual versions */ for (j=0; j< 16;j++){ sprintf(prefix, "wl%d.%d_", i,j); nvram_unset(strcat_r(prefix, "ssid", tmp)); nvram_unset(strcat_r(prefix, "ipconfig_index", tmp)); nvram_unset(strcat_r(prefix, "guest", tmp)); nvram_unset(strcat_r(prefix, "closed", tmp)); nvram_unset(strcat_r(prefix, "wpa_psk", tmp)); nvram_unset(strcat_r(prefix, "auth", tmp)); nvram_unset(strcat_r(prefix, "wep", tmp)); nvram_unset(strcat_r(prefix, "auth_mode", tmp)); nvram_unset(strcat_r(prefix, "crypto", tmp)); nvram_unset(strcat_r(prefix, "akm", tmp)); } } } static void ses_cl_cleanup(void) { /* well known event to cleanly initialize state machine */ nvram_set("ses_cl_event", "0"); } static void restore_defaults(void) { struct nvram_tuple generic[] = { { "lan_ifname", "br0", 0 }, { "lan_ifnames", "eth0 eth2 eth3 eth4", 0 }, { "wan_ifname", "eth1", 0 }, { "wan_ifnames", "eth1", 0 }, { 0, 0, 0 } }; #ifdef __CONFIG_VLAN__ struct nvram_tuple vlan[] = { { "lan_ifname", "br0", 0 }, { "lan_ifnames", "vlan0 eth1 eth2 eth3", 0 }, { "wan_ifname", "vlan1", 0 }, { "wan_ifnames", "vlan1", 0 }, { 0, 0, 0 } }; #endif /* __CONFIG_VLAN__ */ struct nvram_tuple dyna[] = { { "lan_ifname", "br0", 0 }, { "lan_ifnames", "", 0 }, { "wan_ifname", "", 0 }, { "wan_ifnames", "", 0 }, { 0, 0, 0 } }; struct nvram_tuple *linux_overrides; struct nvram_tuple *t, *u; int restore_defaults, i; #ifdef __CONFIG_VLAN__ uint boardflags; #endif /* __CONFIG_VLAN_ */ char *landevs, *wandevs; char lan_ifnames[128], wan_ifnames[128]; char wan_ifname[32], *next; int len; int ap = 0; /* Restore defaults if told to or OS has changed */ restore_defaults = !nvram_match("restore_defaults", "0") || nvram_invmatch("os_name", "linux"); if (restore_defaults) cprintf("Restoring defaults..."); /* Delete dynamically generated variables */ if (restore_defaults) { char tmp[100], prefix[] = "wlXXXXXXXXXX_"; for (i = 0; i < MAX_NVPARSE; i++) { #ifdef __CONFIG_NAT__ del_filter_client(i); del_forward_port(i); del_autofw_port(i); #endif /* __CONFIG_NAT__ */ snprintf(prefix, sizeof(prefix), "wl%d_", i); for (t = router_defaults; t->name; t ++) { if (!strncmp(t->name, "wl_", 3)) nvram_unset(strcat_r(prefix, &t->name[3], tmp)); } #ifdef __CONFIG_NAT__ snprintf(prefix, sizeof(prefix), "wan%d_", i); for (t = router_defaults; t->name; t ++) { if (!strncmp(t->name, "wan_", 4)) nvram_unset(strcat_r(prefix, &t->name[4], tmp)); } #endif /* __CONFIG_NAT__ */ } ses_restore_defaults(); virtual_radio_restore_defaults(); } /* * Build bridged i/f name list and wan i/f name list from lan device name list * and wan device name list. Both lan device list "landevs" and wan device list * "wandevs" must exist in order to preceed. */ if ((landevs = nvram_get("landevs")) && (wandevs = nvram_get("wandevs"))) { /* build bridged i/f list based on nvram variable "landevs" */ len = sizeof(lan_ifnames); if (!build_ifnames(landevs, lan_ifnames, &len) && len) dyna[1].value = lan_ifnames; else goto canned_config; /* build wan i/f list based on nvram variable "wandevs" */ len = sizeof(wan_ifnames); if (!build_ifnames(wandevs, wan_ifnames, &len) && len) { dyna[3].value = wan_ifnames; foreach (wan_ifname, wan_ifnames, next) { dyna[2].value = wan_ifname; break; } } else ap = 1; linux_overrides = dyna; } /* override lan i/f name list and wan i/f name list with default values */ else { canned_config: #ifdef __CONFIG_VLAN__ boardflags = strtoul(nvram_safe_get("boardflags"), NULL, 0); if (boardflags & BFL_ENETVLAN) linux_overrides = vlan; else #endif /* __CONFIG_VLAN__ */ linux_overrides = generic; } /* Restore defaults */ for (t = router_defaults; t->name; t++) { if (restore_defaults || !nvram_get(t->name)) { for (u = linux_overrides; u && u->name; u++) { if (!strcmp(t->name, u->name)) { nvram_set(u->name, u->value); break; } } if (!u || !u->name) nvram_set(t->name, t->value); } } /* Force to AP */ if (ap) nvram_set("router_disable", "1"); /* Always set OS defaults */ nvram_set("os_name", "linux"); nvram_set("os_version", EPI_ROUTER_VERSION_STR); nvram_set("os_date", __DATE__); nvram_set("is_modified", "0"); nvram_set("ezc_version", EZC_VERSION_STR); /* Commit values */ if (restore_defaults) { nvram_commit(); cprintf("done\n"); } } #ifdef __CONFIG_NAT__ static void set_wan0_vars(void) { int unit; char tmp[100], prefix[] = "wanXXXXXXXXXX_"; /* check if there are any connections configured */ for (unit = 0; unit < MAX_NVPARSE; unit ++) { snprintf(prefix, sizeof(prefix), "wan%d_", unit); if (nvram_get(strcat_r(prefix, "unit", tmp))) break; } /* automatically configure wan0_ if no connections found */ if (unit >= MAX_NVPARSE) { struct nvram_tuple *t; char *v; /* Write through to wan0_ variable set */ snprintf(prefix, sizeof(prefix), "wan%d_", 0); for (t = router_defaults; t->name; t ++) { if (!strncmp(t->name, "wan_", 4)) { if (nvram_get(strcat_r(prefix, &t->name[4], tmp))) continue; v = nvram_get(t->name); nvram_set(tmp, v ? v : t->value); } } nvram_set(strcat_r(prefix, "unit", tmp), "0"); nvram_set(strcat_r(prefix, "desc", tmp), "Default Connection"); nvram_set(strcat_r(prefix, "primary", tmp), "1"); } } #endif /* __CONFIG_NAT__ */ static int noconsole = 0; static void sysinit(void) { char buf[PATH_MAX]; struct utsname name; struct stat tmp_stat; time_t tm = 0; char *loglevel; /* /proc */ mount("proc", "/proc", "proc", MS_MGC_VAL, NULL); /* /tmp */ mount("ramfs", "/tmp", "ramfs", MS_MGC_VAL, NULL); /* /var */ mkdir("/tmp/var", 0777); mkdir("/var/lock", 0777); mkdir("/var/log", 0777); mkdir("/var/run", 0777); mkdir("/var/tmp", 0777); /* Setup console */ if (console_init()) noconsole = 1; if ((loglevel = nvram_get("console_loglevel"))) klogctl(8, NULL, atoi(loglevel)); else klogctl(8, NULL, 1); /* Modules */ uname(&name); snprintf(buf, sizeof(buf), "/lib/modules/%s", name.release); if (stat("/proc/modules", &tmp_stat) == 0 && stat(buf, &tmp_stat) == 0) { char module[80], *modules, *next; modules = nvram_get("kernel_mods") ? : "et bcm57xx wl"; foreach(module, modules, next) eval("insmod", module); #ifdef __CONFIG_WCN__ modules = "scsi_mod sd_mod usbcore usb-ohci usb-storage fat vfat msdos"; foreach(module, modules, next) eval("insmod", module); #endif } /* Set a sane date */ stime(&tm); dprintf("done\n"); } /* States */ enum { RESTART, STOP, START, TIMER, IDLE, }; static int state = START; static int signalled = -1; /* Signal handling */ static void rc_signal(int sig) { if (state == IDLE) { if (sig == SIGHUP) { dprintf("signalling RESTART\n"); signalled = RESTART; } else if (sig == SIGUSR2) { dprintf("signalling START\n"); signalled = START; } else if (sig == SIGINT) { dprintf("signalling STOP\n"); signalled = STOP; } else if (sig == SIGALRM) { dprintf("signalling TIMER\n"); signalled = TIMER; } } } /* Get the timezone from NVRAM and set the timezone in the kernel * and export the TZ variable */ static void set_timezone(void) { time_t now; struct tm gm, local; struct timezone tz; /* Export TZ variable for the time libraries to * use. */ setenv("TZ",nvram_get("time_zone"),1); /* Update kernel timezone */ time(&now); gmtime_r(&now, &gm); localtime_r(&now, &local); tz.tz_minuteswest = (mktime(&gm) - mktime(&local)) / 60; settimeofday(NULL, &tz); } /* Timer procedure.Gets time from the NTP servers once every timer interval * Interval specified by the NVRAM variable timer_interval */ int do_timer(void) { int interval = atoi(nvram_safe_get("timer_interval")); dprintf("%d\n", interval); if (interval == 0) return 0; /* Report stats */ if (nvram_invmatch("stats_server", "")) { char *stats_argv[] = { "stats", nvram_get("stats_server"), NULL }; _eval(stats_argv, NULL, 5, NULL); } /* Sync time */ start_ntpc(); alarm(interval); return 0; } /* Main loop */ static void main_loop(void) { sigset_t sigset; pid_t shell_pid = 0; #ifdef __CONFIG_VLAN__ uint boardflags; #endif /* Basic initialization */ sysinit(); /* Setup signal handlers */ signal_init(); signal(SIGHUP, rc_signal); signal(SIGUSR2, rc_signal); signal(SIGINT, rc_signal); signal(SIGALRM, rc_signal); sigemptyset(&sigset); /* Give user a chance to run a shell before bringing up the rest of the system */ if (!noconsole) run_shell(1, 0); /* Get boardflags to see if VLAN is supported */ #ifdef __CONFIG_VLAN__ boardflags = strtoul(nvram_safe_get("boardflags"), NULL, 0); #endif /* __CONFIG_VLAN__ */ /* Add loopback */ config_loopback(); /* Convert deprecated variables */ convert_deprecated(); /* ses cleanup of variables left half way through */ ses_cleanup(); /* ses_cl cleanup of variables left half way through */ ses_cl_cleanup(); /* Restore defaults if necessary */ restore_defaults(); #ifdef __CONFIG_NAT__ /* Setup wan0 variables if necessary */ set_wan0_vars(); #endif /* __CONFIG_NAT__ */ /* Loop forever */ for (;;) { switch (state) { case RESTART: dprintf("RESTART\n"); /* Fall through */ case STOP: dprintf("STOP\n"); stop_services(); #ifdef __CONFIG_NAT__ stop_wan(); #endif /* __CONFIG_NAT__ */ stop_lan(); #ifdef __CONFIG_VLAN__ if (boardflags & BFL_ENETVLAN) stop_vlan(); #endif /* __CONFIG_VLAN__ */ if (state == STOP) { state = IDLE; break; } /* Fall through */ case START: dprintf("START\n"); set_timezone(); #ifdef __CONFIG_VLAN__ if (boardflags & BFL_ENETVLAN) start_vlan(); #endif /* __CONFIG_VLAN__ */ start_lan(); start_services(); #ifdef __CONFIG_NAT__ start_wan(); start_nas("wan"); #endif /* __CONFIG_NAT__ */ /* Fall through */ case TIMER: dprintf("TIMER\n"); do_timer(); /* Fall through */ case IDLE: dprintf("IDLE\n"); state = IDLE; /* Wait for user input or state change */ while (signalled == -1) { if (!noconsole && (!shell_pid || kill(shell_pid, 0) != 0)) shell_pid = run_shell(0, 1); else sigsuspend(&sigset); } state = signalled; signalled = -1; break; default: dprintf("UNKNOWN\n"); return; } } } int main(int argc, char **argv) { char *base = strrchr(argv[0], '/'); base = base ? base + 1 : argv[0]; /* init */ if (strstr(base, "init")) { main_loop(); return 0; } /* Set TZ for all rc programs */ setenv("TZ", nvram_safe_get("time_zone"), 1); /* rc [stop|start|restart ] */ if (strstr(base, "rc")) { if (argv[1]) { if (strncmp(argv[1], "start", 5) == 0) return kill(1, SIGUSR2); else if (strncmp(argv[1], "stop", 4) == 0) return kill(1, SIGINT); else if (strncmp(argv[1], "restart", 7) == 0) return kill(1, SIGHUP); } else { fprintf(stderr, "usage: rc [start|stop|restart]\n"); return EINVAL; } } #ifdef __CONFIG_NAT__ /* ppp */ else if (strstr(base, "ip-up")) return ipup_main(argc, argv); else if (strstr(base, "ip-down")) return ipdown_main(argc, argv); /* udhcpc [ deconfig bound renew ] */ else if (strstr(base, "udhcpc")) return udhcpc_wan(argc, argv); #endif /* __CONFIG_NAT__ */ /* ldhclnt [ deconfig bound renew ] */ else if (strstr(base, "ldhclnt")) return udhcpc_lan(argc, argv); /* stats [ url ] */ else if (strstr(base, "stats")) return http_stats(argv[1] ? : nvram_safe_get("stats_server")); /* erase [device] */ else if (strstr(base, "erase")) { if (argv[1]) return mtd_erase(argv[1]); else { fprintf(stderr, "usage: erase [device]\n"); return EINVAL; } } /* write [path] [device] */ else if (strstr(base, "write")) { if (argc >= 3) return mtd_write(argv[1], argv[2]); else { fprintf(stderr, "usage: write [path] [device]\n"); return EINVAL; } } /* hotplug [event] */ else if (strstr(base, "hotplug")) { if (argc >= 2) { if (!strcmp(argv[1], "net")) return hotplug_net(); #ifdef __CONFIG_WCN__ else if (!strcmp(argv[1], "usb")) return hotplug_usb(); #endif } else { fprintf(stderr, "usage: hotplug [event]\n"); return EINVAL; } } return EINVAL; }