/* * no license, extracted from wag54gv2-AU_v1.00.39 GPL * */ #include #include #include #include #include #include #include #include #include /* phy types */ #define PHY_TYPE_A 0 #define PHY_TYPE_B 1 #define PHY_TYPE_G 2 #define PHY_TYPE_NULL 0xf /* parts of an idcode: */ #define IDCODE_MFG_MASK 0x00000fff #define IDCODE_MFG_SHIFT 0 #define IDCODE_ID_MASK 0x0ffff000 #define IDCODE_ID_SHIFT 12 #define IDCODE_REV_MASK 0xf0000000 #define IDCODE_REV_SHIFT 28 #define WL_IOCTL(name, cmd, buf, len) ((void) wl_ioctl((name), (cmd), (buf), (len))) /* set WEP key */ static int wlconf_set_wep_key(char *name, char *prefix, int i) { wsec_key_t key; char wl_key[] = "wlXXXXXXXXXX_keyXXXXXXXXXX"; char *keystr, hex[] = "XX"; unsigned char *data = key.data; int ret = 0; memset(&key, 0, sizeof(key)); key.index = i - 1; sprintf(wl_key, "%skey%d", prefix, i); keystr = nvram_safe_get(wl_key); switch (strlen(keystr)) { case WEP1_KEY_SIZE: case WEP128_KEY_SIZE: key.len = strlen(keystr); strcpy(key.data, keystr); break; case WEP1_KEY_HEX_SIZE: case WEP128_KEY_HEX_SIZE: key.len = strlen(keystr) / 2; while (*keystr) { strncpy(hex, keystr, 2); *data++ = (unsigned char) strtoul(hex, NULL, 16); keystr += 2; } break; default: key.len = 0; break; } /* Set current WEP key */ if (key.len && i == atoi(nvram_safe_get(strcat_r(prefix, "key", wl_key)))) key.flags = WSEC_PRIMARY_KEY; WL_IOCTL(name, WLC_SET_KEY, &key, sizeof(key)); return ret; } extern struct nvram_tuple router_defaults[]; /* Keep this table in order */ static struct { int locale; char **names; char *abbr; } countries[] = { { WLC_WW, ((char *[]) { "Worldwide", "WW", NULL }), "AU" }, { WLC_THA, ((char *[]) { "Thailand", "THA", NULL }), "TH" }, { WLC_ISR, ((char *[]) { "Israel", "ISR", NULL }), "IL" }, { WLC_JDN, ((char *[]) { "Jordan", "JDN", NULL }), "JO" }, { WLC_PRC, ((char *[]) { "China", "P.R. China", "PRC", NULL }), "CN" }, { WLC_JPN, ((char *[]) { "Japan", "JPN", NULL }), "JP" }, { WLC_FCC, ((char *[]) { "USA", "Canada", "ANZ", "New Zealand", "FCC", NULL }), "US" }, { WLC_EUR, ((char *[]) { "Europe", "EUR", NULL }), "DE" }, { WLC_USL, ((char *[]) { "USA Low", "USALow", "USL", NULL }), "US" }, { WLC_JPH, ((char *[]) { "Japan High", "JapanHigh", "JPH", NULL }), "JP" }, { WLC_ALL, ((char *[]) { "All", "AllTheChannels", NULL }), "All" }, }; /* validate/restore all per-interface related variables */ static void wlconf_validate_all(char *prefix, bool restore) { struct nvram_tuple *t; char tmp[100]; char *v; for (t = router_defaults; t->name; t++) { if (!strncmp(t->name, "wl_", 3)) { strcat_r(prefix, &t->name[3], tmp); if (!restore && nvram_get(tmp)) continue; v = nvram_get(t->name); nvram_set(tmp, v ? v : t->value); } } } /* restore specific per-interface variable */ static void wlconf_restore_var(char *prefix, char *name) { struct nvram_tuple *t; char tmp[100]; for (t = router_defaults; t->name; t++) { if (!strncmp(t->name, "wl_", 3) && !strcmp(&t->name[3], name)) { nvram_set(strcat_r(prefix, name, tmp), t->value); break; } } } /* Set up wsec */ static int wlconf_set_wsec(char *ifname, char *prefix) { char tmp[100]; int val; strcat_r(prefix, "wep", tmp); if (nvram_match(tmp, "wep") || nvram_match(tmp, "on") || nvram_match(tmp, "restricted")) val = WEP_ENABLED; else if (nvram_match(tmp, "tkip")) val = TKIP_ENABLED; else if (nvram_match(tmp, "aes")) val = AES_ENABLED; else if (nvram_match(tmp, "tkip+aes")) val = TKIP_ENABLED | AES_ENABLED; else val = 0; return wl_ioctl(ifname, WLC_SET_WSEC, &val, sizeof(val)); } /* * For debugging only */ #define WLCONF_DBG(fmt, arg...) #if defined(linux) #include static void sleep_ms(const unsigned int ms) { usleep(1000*ms); } #endif /* * The following condition(s) must be met when Auto Channel Selection * is enabled. * - the I/F is up (change radio channel requires it is up?) * - the AP must not be associated (setting SSID to empty should * make sure it for us) */ static uint8 wlconf_auto_channel(char *name) { int chosen = 0; wl_uint32_list_t request; int phytype; /* query the phy type */ wl_ioctl(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype)); request.count = 0; /* let the ioctl decide */ if (!wl_ioctl(name, WLC_START_CHANNEL_SEL, &request, sizeof(request))) { sleep_ms(phytype == PHY_TYPE_A ? 1000 : 750); while (wl_ioctl(name, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen))) sleep_ms(100); } WLCONF_DBG("interface %s: channel selected %d\n", name, chosen); return chosen; } /* PHY type/BAND conversion */ #define WLCONF_PHYTYPE2BAND(phy) ((phy) == PHY_TYPE_A ? WLC_BAND_A : WLC_BAND_B) /* PHY type conversion */ #define WLCONF_PHYTYPE2STR(phy) ((phy) == PHY_TYPE_A ? "a" : \ (phy) == PHY_TYPE_B ? "b" : "g") #define WLCONF_STR2PHYTYPE(phy) ((phy) && (phy)[0] == 'a' ? PHY_TYPE_A : \ (phy) && (phy)[0] == 'b' ? PHY_TYPE_B : PHY_TYPE_G) /* configure the specified wireless interface */ int wlconf(char *name) { int restore_defaults, val, unit, phytype, gmode = 0, ret = 0; char tmp[100], prefix[] = "wlXXXXXXXXXX_"; char var[80], *next, phy[] = "a", *str; unsigned char buf[WLC_IOCTL_MAXLEN]; char *country; wlc_rev_info_t rev; channel_info_t ci; struct maclist *maclist; struct ether_addr *ea; wlc_ssid_t ssid; wl_rateset_t rs; unsigned int i; char eaddr[32]; int ap, sta = 0, wet = 0; char country_code[4]; /* Check interface (fail silently for non-wl interfaces) */ if ((ret = wl_probe(name))) return ret; /* Get MAC address */ (void) wl_hwaddr(name, buf); /* Get instance */ WL_IOCTL(name, WLC_GET_INSTANCE, &unit, sizeof(unit)); snprintf(prefix, sizeof(prefix), "wl%d_", unit); /* Restore defaults if per-interface parameters do not exist */ restore_defaults = !nvram_get(strcat_r(prefix, "ifname", tmp)); wlconf_validate_all(prefix, restore_defaults); nvram_set(strcat_r(prefix, "ifname", tmp), name); nvram_set(strcat_r(prefix, "hwaddr", tmp), ether_etoa(buf, eaddr)); snprintf(buf, sizeof(buf), "%d", unit); nvram_set(strcat_r(prefix, "unit", tmp), buf); /* * Nuke SSID first so that the AP won't be associated when WLC_UP. * This must be done here if Auto Channel Selection is enabled. */ WL_IOCTL(name, WLC_GET_UP, &val, sizeof(val)); if (val) { /* Nuke SSID */ ssid.SSID_len = 0; ssid.SSID[0] = '\0'; WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid)); /* Bring the interface down */ WL_IOCTL(name, WLC_DOWN, NULL, sizeof(val)); } /* Set mode : AP, STA */ ap = nvram_match(strcat_r(prefix, "mode", tmp), "ap"); val = (ap + nvram_match(strcat_r(prefix, "mode", tmp), "wds")) ? 1 : 0; WL_IOCTL(name, WLC_SET_AP, &val, sizeof(val)); /* Set STA specific parameters */ if (!ap) { /* Set mode: WET */ if ((wet = nvram_match(strcat_r(prefix, "mode", tmp), "wet"))) WL_IOCTL(name, WLC_SET_WET, &wet, sizeof(wet)); /* Set infra: BSS/IBSS */ if (wet || (sta = nvram_match(strcat_r(prefix, "mode", tmp), "sta"))) { val = atoi(nvram_safe_get(strcat_r(prefix, "infra", tmp))); WL_IOCTL(name, WLC_SET_INFRA, &val, sizeof(val)); } } /* Set network type */ val = atoi(nvram_safe_get(strcat_r(prefix, "closed", tmp))); WL_IOCTL(name, WLC_SET_CLOSED, &val, sizeof(val)); /* Set up the country code */ (void) strcat_r(prefix, "country_code", tmp); country = nvram_get(tmp); if (country) { strncpy(country_code, country, sizeof(country_code)); WL_IOCTL(name, WLC_SET_COUNTRY, country_code, strlen(country_code)+1); } else { /* If country_code doesn't exist, check for country to be backward compatible */ (void) strcat_r(prefix, "country", tmp); country = nvram_safe_get(tmp); for (val = 0; val < ARRAYSIZE(countries); val++) { char **synonym; for (synonym = countries[val].names; *synonym; synonym++) if (!strcmp(country, *synonym)) break; if (*synonym) break; } /* Get the default country code if undefined or invalid and set the NVRAM */ if (val >= ARRAYSIZE(countries)) { WL_IOCTL(name, WLC_GET_COUNTRY, country_code, sizeof(country_code)); } else { strncpy(country_code, countries[val].abbr, sizeof(country_code)); WL_IOCTL(name, WLC_SET_COUNTRY, country_code, strlen(country_code)+1); } /* Add the new NVRAM variable */ nvram_set("wl_country_code", country_code); (void) strcat_r(prefix, "country_code", tmp); nvram_set(tmp, country_code); } /* Set the MAC list */ maclist = (struct maclist *) buf; maclist->count = 0; if (!nvram_match(strcat_r(prefix, "macmode", tmp), "disabled")) { ea = maclist->ea; foreach(var, nvram_safe_get(strcat_r(prefix, "maclist", tmp)), next) { if ((&ea[1])->octet > &buf[sizeof(buf)]) break; if (ether_atoe(var, ea->octet)) { maclist->count++; ea++; } } } WL_IOCTL(name, WLC_SET_MACLIST, buf, sizeof(buf)); /* Set the MAC list mode */ (void) strcat_r(prefix, "macmode", tmp); if (nvram_match(tmp, "deny")) val = WLC_MACMODE_DENY; else if (nvram_match(tmp, "allow")) val = WLC_MACMODE_ALLOW; else val = WLC_MACMODE_DISABLED; WL_IOCTL(name, WLC_SET_MACMODE, &val, sizeof(val)); /* Enable or disable the radio */ val = nvram_match(strcat_r(prefix, "radio", tmp), "0"); WL_IOCTL(name, WLC_SET_RADIO, &val, sizeof(val)); /* Get supported phy types */ WL_IOCTL(name, WLC_GET_PHYLIST, var, sizeof(var)); nvram_set(strcat_r(prefix, "phytypes", tmp), var); /* Get radio IDs */ *(next = buf) = '\0'; for (i = 0; i < strlen(var); i++) { /* Switch to band */ phy[0] = var[i]; val = WLCONF_STR2PHYTYPE(phy); val = WLCONF_PHYTYPE2BAND(val); WL_IOCTL(name, WLC_SET_BAND, &val, sizeof(val)); /* Get radio ID on this band */ WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev)); next += sprintf(next, "%sBCM%X", i ? " " : "", (rev.radiorev & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT); } nvram_set(strcat_r(prefix, "radioids", tmp), buf); /* Set band */ str = nvram_get(strcat_r(prefix, "phytype", tmp)); val = WLCONF_STR2PHYTYPE(str); val = WLCONF_PHYTYPE2BAND(val); /* Check errors (card may have changed) */ if (wl_ioctl(name, WLC_SET_BAND, &val, sizeof(val))) { /* default band to the first band in band list */ phy[0] = var[0]; val = WLCONF_STR2PHYTYPE(phy); val = WLCONF_PHYTYPE2BAND(val); WL_IOCTL(name, WLC_SET_BAND, &val, sizeof(val)); } /* Get current core revision */ WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev)); snprintf(buf, sizeof(buf), "%d", rev.corerev); nvram_set(strcat_r(prefix, "corerev", tmp), buf); /* Get current phy type */ WL_IOCTL(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype)); snprintf(buf, sizeof(buf), "%s", WLCONF_PHYTYPE2STR(phytype)); nvram_set(strcat_r(prefix, "phytype", tmp), buf); /* Set channel before setting gmode or rateset */ /* Manual Channel Selection - when channel # is not 0 */ val = atoi(nvram_safe_get(strcat_r(prefix, "channel", tmp))); if (val) { if (wl_ioctl(name, WLC_SET_CHANNEL, &val, sizeof(val))) { /* Use current channel (card may have changed) */ WL_IOCTL(name, WLC_GET_CHANNEL, &ci, sizeof(ci)); snprintf(buf, sizeof(buf), "%d", ci.target_channel); nvram_set(strcat_r(prefix, "channel", tmp), buf); } } /* Reset to hardware rateset (band may have changed) */ WL_IOCTL(name, WLC_GET_RATESET, &rs, sizeof (wl_rateset_t)); WL_IOCTL(name, WLC_SET_RATESET, &rs, sizeof (wl_rateset_t)); /* Set gmode */ if (phytype == PHY_TYPE_G) { /* Set gmode */ gmode = atoi(nvram_safe_get(strcat_r(prefix, "gmode", tmp))); if (gmode == GMODE_AFTERBURNER) { if (wl_get_int(name, "abcap", &val) || !val) { gmode = GMODE_AUTO; snprintf(buf, sizeof(buf), "%d", gmode); nvram_set(tmp, buf); } } WL_IOCTL(name, WLC_SET_GMODE, &gmode, sizeof(gmode)); /* Set gmode protection override and control algorithm */ if (gmode != GMODE_AFTERBURNER) { int override = WLC_G_PROTECTION_OFF; int control = WLC_G_PROTECTION_CTL_OFF; strcat_r(prefix, "gmode_protection", tmp); if (nvram_match(tmp, "auto")) { override = WLC_G_PROTECTION_AUTO; control = WLC_G_PROTECTION_CTL_OVERLAP; } WL_IOCTL(name, WLC_SET_GMODE_PROTECTION_OVERRIDE, &override, sizeof(override)); WL_IOCTL(name, WLC_SET_GMODE_PROTECTION_CONTROL, &control, sizeof(control)); } } /* Get current rateset (gmode may have changed) */ WL_IOCTL(name, WLC_GET_CURR_RATESET, &rs, sizeof (wl_rateset_t)); strcat_r(prefix, "rateset", tmp); if (nvram_match(tmp, "all")) { /* Make all rates basic */ for (i = 0; i < rs.count; i++) rs.rates[i] |= 0x80; } else if (nvram_match(tmp, "12")) { /* Make 1 and 2 basic */ for (i = 0; i < rs.count; i++) { if ((rs.rates[i] & 0x7f) == 2 || (rs.rates[i] & 0x7f) == 4) rs.rates[i] |= 0x80; else rs.rates[i] &= ~0x80; } } /* Set rateset */ WL_IOCTL(name, WLC_SET_RATESET, &rs, sizeof (wl_rateset_t)); /* Allow short preamble override for b cards */ if (phytype == PHY_TYPE_B || gmode == 0) { strcat_r(prefix, "plcphdr", tmp); if (nvram_match(tmp, "long")) val = WLC_PLCP_AUTO; else val = WLC_PLCP_SHORT; WL_IOCTL(name, WLC_SET_PLCPHDR, &val, sizeof(val)); } /* Set rate in 500 Kbps units */ val = atoi(nvram_safe_get(strcat_r(prefix, "rate", tmp))) / 500000; if (wl_ioctl(name, WLC_SET_RATE, &val, sizeof(val))) { /* Try default rate (card may have changed) */ val = 0; WL_IOCTL(name, WLC_SET_RATE, &val, sizeof(val)); snprintf(buf, sizeof(buf), "%d", val); nvram_set(strcat_r(prefix, "rate", tmp), buf); } /* Set fragmentation threshold */ val = atoi(nvram_safe_get(strcat_r(prefix, "frag", tmp))); WL_IOCTL(name, WLC_SET_FRAG, &val, sizeof(val)); /* Set RTS threshold */ val = atoi(nvram_safe_get(strcat_r(prefix, "rts", tmp))); WL_IOCTL(name, WLC_SET_RTS, &val, sizeof(val)); /* Set DTIM period */ val = atoi(nvram_safe_get(strcat_r(prefix, "dtim", tmp))); WL_IOCTL(name, WLC_SET_DTIMPRD, &val, sizeof(val)); /* Set beacon period */ val = atoi(nvram_safe_get(strcat_r(prefix, "bcn", tmp))); WL_IOCTL(name, WLC_SET_BCNPRD, &val, sizeof(val)); /* Set lazy WDS mode */ val = atoi(nvram_safe_get(strcat_r(prefix, "lazywds", tmp))); WL_IOCTL(name, WLC_SET_LAZYWDS, &val, sizeof(val)); /* Set the WDS list */ maclist = (struct maclist *) buf; maclist->count = 0; ea = maclist->ea; foreach(var, nvram_safe_get(strcat_r(prefix, "wds", tmp)), next) { if (ea->octet > &buf[sizeof(buf)]) break; ether_atoe(var, ea->octet); maclist->count++; ea++; } WL_IOCTL(name, WLC_SET_WDSLIST, buf, sizeof(buf)); /* Set framebursting mode */ val = nvram_match(strcat_r(prefix, "frameburst", tmp), "on"); WL_IOCTL(name, WLC_SET_FAKEFRAG, &val, sizeof(val)); /* Bring the interface back up */ WL_IOCTL(name, WLC_UP, NULL, 0); /* Set antenna */ val = atoi(nvram_safe_get(strcat_r(prefix, "antdiv", tmp))); WL_IOCTL(name, WLC_SET_ANTDIV, &val, sizeof(val)); /* Auto Channel Selection - when channel # is 0 in AP mode */ /* * The following condition(s) must be met in order for * Auto Channel Selection to work. * - the I/F must be up (change radio channel requires it is up?) * - the AP must not be associated (setting SSID to empty should * make sure it for us) */ if (ap) { if (!(val = atoi(nvram_safe_get(strcat_r(prefix, "channel", tmp))))) { /* select a channel */ val = wlconf_auto_channel(name); /* switch to the selected channel */ WL_IOCTL(name, WLC_SET_CHANNEL, &val, sizeof(val)); /* set the auto channel scan timer in the driver when in auto mode */ val = 15; /* 15 minutes for now */ WL_IOCTL(name, WLC_SET_CS_SCAN_TIMER, &val, sizeof(val)); } else { /* reset the channel scan timer in the driver when not in auto mode */ val = 0; WL_IOCTL(name, WLC_SET_CS_SCAN_TIMER, &val, sizeof(val)); } } /* Set WEP keys */ for (i = 1; i <= WLC_MAX_DEFAULT_KEYS; i++) wlconf_set_wep_key(name, prefix, i); /* Set WSEC */ /* * Need to check errors (card may have changed) and change to * defaults since the new chip may not support the requested * encryptions after the card has been changed. */ if (wlconf_set_wsec(name, prefix)) { /* change nvram only, code below will pass them on */ wlconf_restore_var(prefix, "auth_mode"); wlconf_restore_var(prefix, "auth"); /* reset wep to default */ wlconf_restore_var(prefix, "wep"); wlconf_set_wsec(name, prefix); } /* Set WPA authentication mode - radius/wpa/psk */ strcat_r(prefix, "auth_mode", tmp); if (nvram_match(tmp, "radius")) val = WPA_AUTH_DISABLED; else if (nvram_match(tmp, "wpa")) val = WPA_AUTH_UNSPECIFIED; else if (nvram_match(tmp, "psk")) val = WPA_AUTH_PSK; else /* if (nvram_match(tmp, "disabled")) */ val = WPA_AUTH_DISABLED; WL_IOCTL(name, WLC_SET_WPA_AUTH, &val, sizeof(val)); /* Set non-WPA authentication mode - open/shared */ val = atoi(nvram_safe_get(strcat_r(prefix, "auth", tmp))); WL_IOCTL(name, WLC_SET_AUTH, &val, sizeof(val)); /* Set WEP restrict if WEP is not disabled */ val = nvram_invmatch(strcat_r(prefix, "wep", tmp), "off"); WL_IOCTL(name, WLC_SET_WEP_RESTRICT, &val, sizeof(val)); /* Set SSID/Join network */ if (ap | sta | wet) { strcat_r(prefix, "ssid", tmp); ssid.SSID_len = strlen(nvram_safe_get(tmp)); if (ssid.SSID_len > sizeof(ssid.SSID)) ssid.SSID_len = sizeof(ssid.SSID); strncpy(ssid.SSID, nvram_safe_get(tmp), ssid.SSID_len); } else { /* A zero length SSID turns off the AP */ ssid.SSID_len = 0; ssid.SSID[0] = '\0'; } WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid)); return 0; } int wlconf_down(char *name) { int val, ret = 0; unsigned char buf[WLC_IOCTL_MAXLEN]; struct maclist *maclist; wlc_ssid_t ssid; /* Check interface (fail silently for non-wl interfaces) */ if ((ret = wl_probe(name))) return ret; /* Bring down the interface */ WL_IOCTL(name, WLC_GET_UP, &val, sizeof(val)); if (val) { /* Nuke SSID */ ssid.SSID_len = 0; ssid.SSID[0] = '\0'; WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid)); /* Bring the interface down */ WL_IOCTL(name, WLC_DOWN, NULL, sizeof(val)); } /* Nuke the WDS list */ maclist = (struct maclist *) buf; maclist->count = 0; WL_IOCTL(name, WLC_SET_WDSLIST, buf, sizeof(buf)); return 0; } int main(int argc, char *argv[]) { int ret = -1; /* Check parameters and branch based on action */ if (argc == 3 && !strcmp(argv[2], "up")) ret = wlconf(argv[1]); else if (argc == 3 && !strcmp(argv[2], "down")) ret = wlconf_down(argv[1]); else { fprintf(stderr, "Usage: wlconf up|down\n"); return -1; } /* Check result */ if (ret) { fprintf(stderr, "wlconf: %s failed (%d)\n", argv[1], ret); return ret; } return 0; }