rendered paste body/*
* 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 <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <signal.h>
#include <string.h>
#include <sys/klog.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <net/if_arp.h>
#include <dirent.h>
#include <epivers.h>
#include <bcmnvram.h>
#include <mtd.h>
#include <shutils.h>
#include <rc.h>
#include <netconf.h>
#include <nvparse.h>
#include <bcmdevs.h>
#include <bcmparams.h>
#include <wlutils.h>
#include <ezc.h>
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<N> il<N> et<N> vlan<N>) 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;
}