You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
950 lines
21 KiB
C
950 lines
21 KiB
C
/*
|
|
|
|
Tomato Firmware
|
|
Copyright (C) 2006-2009 Jonathan Zarate
|
|
|
|
*/
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <stdarg.h>
|
|
#include <syslog.h>
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <ifaddrs.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <bcmnvram.h>
|
|
#include <bcmdevs.h>
|
|
#include <wlutils.h>
|
|
|
|
#include "shutils.h"
|
|
#include "shared.h"
|
|
|
|
|
|
int get_wan_proto(void)
|
|
{
|
|
const char *names[] = { // order must be synced with def at shared.h
|
|
"static",
|
|
"dhcp",
|
|
"l2tp",
|
|
"pppoe",
|
|
"pptp",
|
|
"ppp3g",
|
|
"lte",
|
|
NULL
|
|
};
|
|
int i;
|
|
const char *p;
|
|
|
|
p = nvram_safe_get("wan_proto");
|
|
for (i = 0; names[i] != NULL; ++i) {
|
|
if (strcmp(p, names[i]) == 0) return i + 1;
|
|
}
|
|
return WP_DISABLED;
|
|
}
|
|
|
|
int get_wanx_proto(char *prefix)
|
|
{
|
|
char tmp[100];
|
|
const char *names[] = { // order must be synced with def at shared.h
|
|
"static",
|
|
"dhcp",
|
|
"l2tp",
|
|
"pppoe",
|
|
"pptp",
|
|
"ppp3g",
|
|
"lte",
|
|
NULL
|
|
};
|
|
int i;
|
|
const char *p;
|
|
|
|
p = nvram_safe_get(strcat_r(prefix, "_proto", tmp));
|
|
for (i = 0; names[i] != NULL; ++i) {
|
|
if (strcmp(p, names[i]) == 0) return i + 1;
|
|
}
|
|
return WP_DISABLED;
|
|
}
|
|
|
|
#ifdef TCONFIG_IPV6
|
|
int get_ipv6_service(void)
|
|
{
|
|
const char *names[] = { // order must be synced with def at shared.h
|
|
"native", // IPV6_NATIVE
|
|
"native-pd", // IPV6_NATIVE_DHCP
|
|
"6to4", // IPV6_ANYCAST_6TO4
|
|
"sit", // IPV6_6IN4
|
|
"other", // IPV6_MANUAL
|
|
"6rd", // IPV6_6RD
|
|
"6rd-pd", // IPV6_6RD_DHCP
|
|
NULL
|
|
};
|
|
int i;
|
|
const char *p;
|
|
|
|
p = nvram_safe_get("ipv6_service");
|
|
for (i = 0; names[i] != NULL; ++i) {
|
|
if (strcmp(p, names[i]) == 0) return i + 1;
|
|
}
|
|
return IPV6_DISABLED;
|
|
}
|
|
|
|
const char *ipv6_router_address(struct in6_addr *in6addr)
|
|
{
|
|
char *p;
|
|
struct in6_addr addr;
|
|
static char addr6[INET6_ADDRSTRLEN];
|
|
|
|
addr6[0] = '\0';
|
|
|
|
if ((p = nvram_get("ipv6_rtr_addr")) && *p) {
|
|
inet_pton(AF_INET6, p, &addr);
|
|
}
|
|
else if ((p = nvram_get("ipv6_prefix")) && *p) {
|
|
inet_pton(AF_INET6, p, &addr);
|
|
addr.s6_addr16[7] = htons(0x0001);
|
|
}
|
|
else {
|
|
return addr6;
|
|
}
|
|
|
|
inet_ntop(AF_INET6, &addr, addr6, sizeof(addr6));
|
|
if (in6addr)
|
|
memcpy(in6addr, &addr, sizeof(addr));
|
|
|
|
return addr6;
|
|
}
|
|
|
|
int calc_6rd_local_prefix(const struct in6_addr *prefix,
|
|
int prefix_len, int relay_prefix_len,
|
|
const struct in_addr *local_ip,
|
|
struct in6_addr *local_prefix, int *local_prefix_len)
|
|
{
|
|
// the following code is based on ipv6calc's code
|
|
uint32_t local_ip_bits, j;
|
|
int i;
|
|
|
|
if (!prefix || !local_ip || !local_prefix || !local_prefix_len) {
|
|
return 0;
|
|
}
|
|
|
|
*local_prefix_len = prefix_len + 32 - relay_prefix_len;
|
|
if (*local_prefix_len > 64) {
|
|
return 0;
|
|
}
|
|
|
|
local_ip_bits = ntohl(local_ip->s_addr) << relay_prefix_len;
|
|
|
|
for (i=0; i<4; i++) {
|
|
local_prefix->s6_addr32[i] = prefix->s6_addr32[i];
|
|
}
|
|
|
|
for (j = 0x80000000, i = prefix_len; i < *local_prefix_len; i++, j>>=1)
|
|
{
|
|
if (local_ip_bits & j)
|
|
local_prefix->s6_addr[i>>3] |= (0x80 >> (i & 0x7));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
int using_dhcpc(char *prefix)
|
|
{
|
|
char tmp[100];
|
|
switch (get_wanx_proto(prefix)) {
|
|
case WP_DHCP:
|
|
case WP_LTE:
|
|
return 1;
|
|
case WP_L2TP:
|
|
case WP_PPTP:
|
|
case WP_PPPOE: // PPPoE with MAN
|
|
return nvram_get_int(strcat_r(prefix, "_pptp_dhcp", tmp));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int wl_client(int unit, int subunit)
|
|
{
|
|
char *mode = nvram_safe_get(wl_nvname("mode", unit, subunit));
|
|
|
|
return ((strcmp(mode, "sta") == 0) || (strcmp(mode, "wet") == 0));
|
|
}
|
|
|
|
int foreach_wif(int include_vifs, void *param,
|
|
int (*func)(int idx, int unit, int subunit, void *param))
|
|
{
|
|
char ifnames[256];
|
|
char name[64], ifname[64], *next = NULL;
|
|
int unit = -1, subunit = -1;
|
|
int i;
|
|
int ret = 0;
|
|
|
|
#ifdef TCONFIG_MULTIWAN
|
|
snprintf(ifnames, sizeof(ifnames), "%s %s %s %s %s %s %s %s %s %s %s %s %s",
|
|
#else
|
|
snprintf(ifnames, sizeof(ifnames), "%s %s %s %s %s %s %s %s %s %s",
|
|
#endif
|
|
nvram_safe_get("lan_ifnames"),
|
|
nvram_safe_get("lan1_ifnames"),
|
|
nvram_safe_get("lan2_ifnames"),
|
|
nvram_safe_get("lan3_ifnames"),
|
|
nvram_safe_get("wan_ifnames"),
|
|
nvram_safe_get("wan2_ifnames"),
|
|
#ifdef TCONFIG_MULTIWAN
|
|
nvram_safe_get("wan3_ifnames"),
|
|
nvram_safe_get("wan4_ifnames"),
|
|
#endif
|
|
nvram_safe_get("wl_ifname"),
|
|
nvram_safe_get("wl0_ifname"),
|
|
nvram_safe_get("wl0_vifs"),
|
|
nvram_safe_get("wl1_ifname"),
|
|
nvram_safe_get("wl1_vifs"));
|
|
remove_dups(ifnames, sizeof(ifnames));
|
|
sort_list(ifnames, sizeof(ifnames));
|
|
|
|
i = 0;
|
|
foreach(name, ifnames, next) {
|
|
if (nvifname_to_osifname(name, ifname, sizeof(ifname)) != 0)
|
|
continue;
|
|
|
|
if (wl_probe(ifname) || wl_ioctl(ifname, WLC_GET_INSTANCE, &unit, sizeof(unit)))
|
|
continue;
|
|
|
|
// Convert eth name to wl name
|
|
if (osifname_to_nvifname(name, ifname, sizeof(ifname)) != 0)
|
|
continue;
|
|
|
|
// Slave intefaces have a '.' in the name
|
|
if (strchr(ifname, '.') && !include_vifs)
|
|
continue;
|
|
|
|
if (get_ifname_unit(ifname, &unit, &subunit) < 0)
|
|
continue;
|
|
|
|
ret |= func(i++, unit, subunit, param);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void notice_set(const char *path, const char *format, ...)
|
|
{
|
|
char p[256];
|
|
char buf[2048];
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vsnprintf(buf, sizeof(buf), format, args);
|
|
va_end(args);
|
|
|
|
mkdir("/var/notice", 0755);
|
|
snprintf(p, sizeof(p), "/var/notice/%s", path);
|
|
f_write_string(p, buf, 0, 0);
|
|
if (buf[0]) syslog(LOG_INFO, "notice[%s]: %s", path, buf);
|
|
}
|
|
|
|
#define mwanlog(level,x...) if(nvram_get_int("mwan_debug")>=level) syslog(level, x)
|
|
#define _x_dprintf(args...) mwanlog(LOG_DEBUG, args);
|
|
//#define _x_dprintf(args...) do { } while (0);
|
|
|
|
int wan_led(int *mode) // mode: 0 - OFF, 1 - ON
|
|
{
|
|
int model;
|
|
|
|
if (mode) {
|
|
mwanlog(LOG_DEBUG, "### wan_led: led(LED_WHITE,ON)");
|
|
} else {
|
|
mwanlog(LOG_DEBUG, "### wan_led: led(LED_WHITE,OFF)");
|
|
}
|
|
|
|
model = get_model();
|
|
|
|
if (nvram_match("boardrev", "0x11")) { // Ovislink 1600GL - led "connected" on
|
|
led(LED_WHITE,mode);
|
|
}
|
|
if (nvram_match("boardtype", "0x052b") && nvram_match("boardrev", "0x1204")) { //rt-n15u wan led on
|
|
led(LED_WHITE,mode);
|
|
}
|
|
if (nvram_match("model", "RT-N18U")) {
|
|
led(LED_WHITE,mode);
|
|
}
|
|
if (model == MODEL_DIR868L) {
|
|
led(LED_WHITE,mode);
|
|
}
|
|
if (model == MODEL_WS880) {
|
|
led(LED_WHITE,mode);
|
|
}
|
|
if (model == MODEL_R6250) {
|
|
led(LED_WHITE,mode);
|
|
}
|
|
if (model == MODEL_R6300v2) {
|
|
led(LED_WHITE,mode);
|
|
}
|
|
|
|
return mode;
|
|
}
|
|
|
|
int wan_led_off(char *prefix) // off WAN LED only if no other WAN active
|
|
{
|
|
char tmp[100];
|
|
char ppplink_file[32];
|
|
const char *names[] = { // FIXME: hardcoded to 4 WANs
|
|
"wan",
|
|
"wan2",
|
|
#ifdef TCONFIG_MULTIWAN
|
|
"wan3",
|
|
"wan4",
|
|
#endif
|
|
NULL
|
|
};
|
|
int i;
|
|
int f;
|
|
struct ifreq ifr;
|
|
int up;
|
|
int count;
|
|
|
|
for (i = 0; names[i] != NULL; ++i) {
|
|
up = 0; // default is 0 (LED_OFF)
|
|
if (!strcmp(prefix, names[i])) continue; // only check others
|
|
mwanlog(LOG_DEBUG, "### wan_led_off: check %s aliveness...", names[i]);
|
|
switch (get_wanx_proto(names[i])) {
|
|
case WP_DISABLED:
|
|
break; // WAN is disabled - skip
|
|
case WP_STATIC:
|
|
case WP_DHCP:
|
|
case WP_LTE:
|
|
if (!nvram_match(strcat_r(names[i], "_ipaddr", tmp), "0.0.0.0")) { // have IP, assume ON
|
|
_x_dprintf("### %s: %s_ipaddr found, ++count\n", __FUNCTION__, names[i]);
|
|
up = 1;
|
|
if (((f = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)) { // check interface
|
|
strlcpy(ifr.ifr_name, nvram_safe_get(strcat_r(names[i], "_iface", tmp)), sizeof(ifr.ifr_name));
|
|
if (ioctl(f, SIOCGIFFLAGS, &ifr) < 0) {
|
|
up = 0;
|
|
_x_dprintf("### %s: %s SIOCGIFFLAGS, reset count\n", __FUNCTION__, names[i]);
|
|
}
|
|
close(f);
|
|
if ((ifr.ifr_flags & IFF_UP) == 0) {
|
|
up = 0;
|
|
_x_dprintf("### %s: %s !IFF_UP, reset count\n", __FUNCTION__, names[i]);
|
|
}
|
|
}
|
|
}
|
|
if (up) ++count;
|
|
break;
|
|
case WP_L2TP:
|
|
case WP_PPTP:
|
|
case WP_PPPOE:
|
|
case WP_PPP3G:
|
|
memset(ppplink_file , 0, 32);
|
|
sprintf(ppplink_file, "/tmp/ppp/%s_link", names[i]);
|
|
if (fopen(ppplink_file, "r") != NULL) { // have PPP link, assume ON
|
|
_x_dprintf("### %s: /tmp/ppp/%s_link found, ++count\n", __FUNCTION__, names[i]);
|
|
up = 1;
|
|
}
|
|
if (up) ++count;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (count > 0) {
|
|
mwanlog(LOG_DEBUG, "### OUT wan_led_off: %s, active WANs count:%d, stay on", prefix, count);
|
|
return count; // do not LED OFF
|
|
}
|
|
else {
|
|
mwanlog(LOG_DEBUG, "### OUT wan_led_off: %s, no other active WANs, turn off led", prefix);
|
|
return wan_led(LED_OFF); // LED OFF
|
|
}
|
|
}
|
|
|
|
//function for rstats & cstats
|
|
long check_wanup_time(void)
|
|
{
|
|
long wanuptime = 0; // wanuptime in seconds
|
|
struct sysinfo si;
|
|
long uptime;
|
|
|
|
sysinfo(&si); //get time
|
|
if(f_read("/var/lib/misc/wan_time", &uptime, sizeof(uptime)) == sizeof(uptime)) {
|
|
wanuptime = si.uptime - uptime; //calculate the difference
|
|
if(wanuptime < 0) wanuptime = 0; //something wrong?
|
|
}
|
|
else {
|
|
wanuptime = 0; //something wrong? f_read()?
|
|
}
|
|
|
|
return wanuptime;
|
|
}
|
|
|
|
int check_wanup(char *prefix)
|
|
{
|
|
int up = 0;
|
|
int proto;
|
|
char buf1[64];
|
|
char buf2[64];
|
|
const char *name;
|
|
int f;
|
|
struct ifreq ifr;
|
|
char tmp[100];
|
|
char ppplink_file[256];
|
|
char pppd_name[256];
|
|
|
|
proto = get_wanx_proto(prefix);
|
|
if (proto == WP_DISABLED)
|
|
{
|
|
wan_led_off(prefix); // LED OFF?
|
|
return 0;
|
|
}
|
|
|
|
if ((proto == WP_PPTP) || (proto == WP_L2TP) || (proto == WP_PPPOE) || (proto == WP_PPP3G)) {
|
|
memset(ppplink_file , 0, 256);
|
|
sprintf(ppplink_file, "/tmp/ppp/%s_link", prefix);
|
|
if (f_read_string(ppplink_file, buf1, sizeof(buf1)) > 0) {
|
|
// contains the base name of a file in /var/run/ containing pid of a daemon
|
|
snprintf(buf2, sizeof(buf2), "/var/run/%s.pid", buf1);
|
|
if (f_read_string(buf2, buf1, sizeof(buf1)) > 0) {
|
|
name = psname(atoi(buf1), buf2, sizeof(buf2));
|
|
memset(pppd_name, 0, 256);
|
|
sprintf(pppd_name, "pppd%s", prefix);
|
|
mwanlog(LOG_DEBUG, "### check_wanup: pppd name=%s, psname=%s", pppd_name, name);
|
|
if (strcmp(name, pppd_name) == 0) up = 1;
|
|
if (proto == WP_L2TP) {
|
|
sprintf(pppd_name, "pppd");
|
|
mwanlog(LOG_DEBUG, "### check_wanup: L2TP pppd name=%s, psname=%s", pppd_name, name);
|
|
if (strcmp(name, pppd_name) == 0) up = 1;
|
|
}
|
|
}
|
|
else {
|
|
_x_dprintf("%s: error reading %s\n", __FUNCTION__, buf2);
|
|
}
|
|
if (!up) {
|
|
unlink(ppplink_file); // stale PPP connection fix, also used in wan_led_off
|
|
_x_dprintf("required daemon not found, assuming link is dead\n");
|
|
}
|
|
}
|
|
else {
|
|
_x_dprintf("%s: error reading %s\n", __FUNCTION__, ppplink_file);
|
|
}
|
|
}
|
|
else if (!nvram_match(strcat_r(prefix, "_ipaddr", tmp), "0.0.0.0")) {
|
|
mwanlog(LOG_DEBUG, "### check_wanup: %s have IP, assume ON", prefix);
|
|
up = 1;
|
|
}
|
|
else {
|
|
_x_dprintf("%s: default !up\n", __FUNCTION__);
|
|
return up; // don't turn off WAN LED
|
|
}
|
|
|
|
if ((up) && ((f = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)) {
|
|
strlcpy(ifr.ifr_name, nvram_safe_get(strcat_r(prefix, "_iface", tmp)), sizeof(ifr.ifr_name));
|
|
if (ioctl(f, SIOCGIFFLAGS, &ifr) < 0) {
|
|
up = 0;
|
|
_x_dprintf("%s: SIOCGIFFLAGS\n", __FUNCTION__);
|
|
}
|
|
close(f);
|
|
if ((ifr.ifr_flags & IFF_UP) == 0) {
|
|
up = 0;
|
|
_x_dprintf("%s: !IFF_UP\n", __FUNCTION__);
|
|
}
|
|
}
|
|
// LED control
|
|
if (up)
|
|
wan_led(up); // LED ON!
|
|
else
|
|
wan_led_off(prefix); // LED OFF?
|
|
|
|
return up;
|
|
}
|
|
|
|
const dns_list_t *get_dns(char *prefix)
|
|
{
|
|
static dns_list_t dns;
|
|
char s[512];
|
|
int n;
|
|
int i, j;
|
|
struct in_addr ia;
|
|
char d[7][22];
|
|
unsigned short port;
|
|
char *c;
|
|
char tmp[100];
|
|
|
|
dns.count = 0;
|
|
|
|
strlcpy(s, nvram_safe_get(strcat_r(prefix, "_dns", tmp)), sizeof(s));
|
|
if ((nvram_get_int(strcat_r(prefix, "_dns_auto", tmp))) || (s[0] == 0)) {
|
|
n = strlen(s);
|
|
snprintf(s + n, sizeof(s) - n, " %s", nvram_safe_get(strcat_r(prefix, "_get_dns", tmp)));
|
|
}
|
|
|
|
n = sscanf(s, "%21s %21s %21s %21s %21s %21s %21s", d[0], d[1], d[2], d[3], d[4], d[5], d[6]);
|
|
for (i = 0; i < n; ++i) {
|
|
port = 53;
|
|
|
|
if ((c = strchr(d[i], ':')) != NULL) {
|
|
*c++ = 0;
|
|
if (((j = atoi(c)) < 1) || (j > 0xFFFF)) continue;
|
|
port = j;
|
|
}
|
|
|
|
if (inet_pton(AF_INET, d[i], &ia) > 0) {
|
|
for (j = dns.count - 1; j >= 0; --j) {
|
|
if ((dns.dns[j].addr.s_addr == ia.s_addr) && (dns.dns[j].port == port)) break;
|
|
}
|
|
if (j < 0) {
|
|
dns.dns[dns.count].port = port;
|
|
dns.dns[dns.count++].addr.s_addr = ia.s_addr;
|
|
if (dns.count == 6) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return &dns;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void set_action(int a)
|
|
{
|
|
int r = 3;
|
|
while (f_write("/var/lock/action", &a, sizeof(a), 0, 0) != sizeof(a)) {
|
|
sleep(1);
|
|
if (--r == 0) return;
|
|
}
|
|
if (a != ACT_IDLE) sleep(2);
|
|
}
|
|
|
|
int check_action(void)
|
|
{
|
|
int a;
|
|
int r = 3;
|
|
|
|
while (f_read("/var/lock/action", &a, sizeof(a)) != sizeof(a)) {
|
|
sleep(1);
|
|
if (--r == 0) return ACT_UNKNOWN;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
int wait_action_idle(int n)
|
|
{
|
|
while (n-- > 0) {
|
|
if (check_action() == ACT_IDLE) return 1;
|
|
sleep(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
const wanface_list_t *get_wanfaces(char *prefix)
|
|
{
|
|
static wanface_list_t wanfaces;
|
|
char *ip, *iface;
|
|
int proto;
|
|
char tmp[100];
|
|
|
|
wanfaces.count = 0;
|
|
|
|
switch ((proto = get_wanx_proto(prefix))) {
|
|
case WP_PPTP:
|
|
case WP_L2TP:
|
|
while (wanfaces.count < 2) {
|
|
if (wanfaces.count == 0) {
|
|
ip = nvram_safe_get(strcat_r(prefix, "_ppp_get_ip", tmp));
|
|
iface = nvram_safe_get(strcat_r(prefix, "_iface", tmp));
|
|
if (!(*iface)) iface = "ppp+";
|
|
}
|
|
else /* if (wanfaces.count == 1) */ {
|
|
ip = nvram_safe_get(strcat_r(prefix, "_ipaddr", tmp));
|
|
if ((!(*ip) || strcmp(ip, "0.0.0.0") == 0) && (wanfaces.count > 0))
|
|
iface = "";
|
|
else
|
|
iface = nvram_safe_get(strcat_r(prefix, "_ifname", tmp));
|
|
}
|
|
strlcpy(wanfaces.iface[wanfaces.count].ip, ip, sizeof(wanfaces.iface[0].ip));
|
|
strlcpy(wanfaces.iface[wanfaces.count].name, iface, IFNAMSIZ);
|
|
++wanfaces.count;
|
|
}
|
|
break;
|
|
default:
|
|
ip = (proto == WP_DISABLED) ? "0.0.0.0" : nvram_safe_get(strcat_r(prefix, "_ipaddr", tmp));
|
|
if ((proto == WP_PPPOE) || (proto == WP_PPP3G)) {
|
|
iface = nvram_safe_get(strcat_r(prefix, "_iface", tmp));
|
|
if (!(*iface)) iface = "ppp+";
|
|
}
|
|
else if (proto == WP_LTE) {
|
|
iface = nvram_safe_get("wan_4g");
|
|
nvram_set(strcat_r(prefix, "_ifname", tmp), iface);
|
|
} else {
|
|
iface = nvram_safe_get(strcat_r(prefix, "_ifname", tmp));
|
|
}
|
|
strlcpy(wanfaces.iface[wanfaces.count].ip, ip, sizeof(wanfaces.iface[0].ip));
|
|
strlcpy(wanfaces.iface[wanfaces.count++].name, iface, IFNAMSIZ);
|
|
break;
|
|
}
|
|
|
|
return &wanfaces;
|
|
}
|
|
|
|
const char *get_wanface(char *prefix)
|
|
{
|
|
return (*get_wanfaces(prefix)).iface[0].name;
|
|
}
|
|
|
|
#ifdef TCONFIG_IPV6
|
|
const char *get_wan6face(void)
|
|
{
|
|
switch (get_ipv6_service()) {
|
|
case IPV6_NATIVE:
|
|
case IPV6_NATIVE_DHCP:
|
|
return get_wanface("wan");
|
|
case IPV6_ANYCAST_6TO4:
|
|
return "v6to4";
|
|
case IPV6_6IN4:
|
|
return "v6in4";
|
|
case IPV6_6RD:
|
|
return "6rd";
|
|
case IPV6_6RD_DHCP:
|
|
return "6rd-pd";
|
|
}
|
|
return nvram_safe_get("ipv6_ifname");
|
|
}
|
|
#endif
|
|
|
|
const char *get_wanip(char *prefix)
|
|
{
|
|
if (!check_wanup(prefix)) return "0.0.0.0";
|
|
|
|
return (*get_wanfaces(prefix)).iface[0].ip;
|
|
}
|
|
|
|
const char *getifaddr(char *ifname, int family, int linklocal)
|
|
{
|
|
static char buf[INET6_ADDRSTRLEN];
|
|
void *addr = NULL;
|
|
struct ifaddrs *ifap, *ifa;
|
|
|
|
if (getifaddrs(&ifap) != 0) {
|
|
_dprintf("getifaddrs failed: %s\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
|
if ((ifa->ifa_addr == NULL) ||
|
|
(strncmp(ifa->ifa_name, ifname, IFNAMSIZ) != 0) ||
|
|
(ifa->ifa_addr->sa_family != family))
|
|
continue;
|
|
|
|
#ifdef TCONFIG_IPV6
|
|
if (ifa->ifa_addr->sa_family == AF_INET6) {
|
|
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)(ifa->ifa_addr);
|
|
if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr) ^ linklocal)
|
|
continue;
|
|
addr = (void *)&(s6->sin6_addr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
struct sockaddr_in *s = (struct sockaddr_in *)(ifa->ifa_addr);
|
|
addr = (void *)&(s->sin_addr);
|
|
}
|
|
|
|
if ((addr) && inet_ntop(ifa->ifa_addr->sa_family, addr, buf, sizeof(buf)) != NULL) {
|
|
freeifaddrs(ifap);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
freeifaddrs(ifap);
|
|
return NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
long get_uptime(void)
|
|
{
|
|
struct sysinfo si;
|
|
sysinfo(&si);
|
|
return si.uptime;
|
|
}
|
|
|
|
char *wl_nvname(const char *nv, int unit, int subunit)
|
|
{
|
|
static char tmp[128];
|
|
char prefix[] = "wlXXXXXXXXXX_";
|
|
|
|
if (unit < 0)
|
|
strcpy(prefix, "wl_");
|
|
else if (subunit > 0)
|
|
snprintf(prefix, sizeof(prefix), "wl%d.%d_", unit, subunit);
|
|
else
|
|
snprintf(prefix, sizeof(prefix), "wl%d_", unit);
|
|
return strcat_r(prefix, nv, tmp);
|
|
}
|
|
|
|
int get_radio(int unit)
|
|
{
|
|
uint32 n;
|
|
|
|
return (wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, 0)), WLC_GET_RADIO, &n, sizeof(n)) == 0) &&
|
|
((n & WL_RADIO_SW_DISABLE) == 0);
|
|
}
|
|
|
|
void set_radio(int on, int unit)
|
|
{
|
|
uint32 n;
|
|
|
|
#ifndef WL_BSS_INFO_VERSION
|
|
#error WL_BSS_INFO_VERSION
|
|
#endif
|
|
|
|
#if WL_BSS_INFO_VERSION >= 108
|
|
n = on ? (WL_RADIO_SW_DISABLE << 16) : ((WL_RADIO_SW_DISABLE << 16) | 1);
|
|
wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, 0)), WLC_SET_RADIO, &n, sizeof(n));
|
|
if (get_model() == MODEL_WS880) {
|
|
led(LED_5G, (on) ? LED_ON : LED_OFF);
|
|
}
|
|
if (!on) {
|
|
if (unit == 0) led(LED_WLAN, LED_OFF);
|
|
else led(LED_5G, LED_OFF);
|
|
} else {
|
|
if (unit == 0) led(LED_WLAN, LED_ON);
|
|
else led(LED_5G, LED_ON);
|
|
}
|
|
#else
|
|
n = on ? 0 : WL_RADIO_SW_DISABLE;
|
|
wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, 0)), WLC_SET_RADIO, &n, sizeof(n));
|
|
if (get_model() == MODEL_WS880) {
|
|
led(LED_WLAN, (on) ? LED_ON : LED_OFF);
|
|
}
|
|
if (!on) {
|
|
led(LED_WLAN, LED_OFF);
|
|
} else {
|
|
led(LED_WLAN, LED_ON);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
int mtd_getinfo(const char *mtdname, int *part, int *size)
|
|
{
|
|
FILE *f;
|
|
char s[256];
|
|
char t[256];
|
|
int r;
|
|
|
|
r = 0;
|
|
if ((strlen(mtdname) < 128) && (strcmp(mtdname, "pmon") != 0)) {
|
|
sprintf(t, "\"%s\"", mtdname);
|
|
if ((f = fopen("/proc/mtd", "r")) != NULL) {
|
|
while (fgets(s, sizeof(s), f) != NULL) {
|
|
if ((sscanf(s, "mtd%d: %x", part, size) == 2) && (strstr(s, t) != NULL)) {
|
|
// don't accidentally mess with bl (0)
|
|
if (*part > 0) r = 1;
|
|
break;
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
}
|
|
if (!r) {
|
|
*size = 0;
|
|
*part = -1;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
int nvram_get_int(const char *key)
|
|
{
|
|
return atoi(nvram_safe_get(key));
|
|
}
|
|
|
|
/*
|
|
long nvram_xget_long(const char *name, long min, long max, long def)
|
|
{
|
|
const char *p;
|
|
char *e;
|
|
long n;
|
|
|
|
p = nvram_get(name);
|
|
if ((p != NULL) && (*p != 0)) {
|
|
n = strtol(p, &e, 0);
|
|
if ((e != p) && ((*e == 0) || (*e == ' ')) && (n > min) && (n < max)) {
|
|
return n;
|
|
}
|
|
}
|
|
return def;
|
|
}
|
|
*/
|
|
|
|
int nvram_get_file(const char *key, const char *fname, int max)
|
|
{
|
|
int n;
|
|
char *p;
|
|
char *b;
|
|
int r;
|
|
|
|
r = 0;
|
|
p = nvram_safe_get(key);
|
|
n = strlen(p);
|
|
if (n <= max) {
|
|
if ((b = malloc(base64_decoded_len(n) + 128)) != NULL) {
|
|
n = base64_decode(p, b, n);
|
|
if (n > 0) r = (f_write(fname, b, n, 0, 0644) == n);
|
|
free(b);
|
|
}
|
|
}
|
|
return r;
|
|
/*
|
|
char b[2048];
|
|
int n;
|
|
char *p;
|
|
|
|
p = nvram_safe_get(key);
|
|
n = strlen(p);
|
|
if (n <= max) {
|
|
n = base64_decode(p, b, n);
|
|
if (n > 0) return (f_write(fname, b, n, 0, 0700) == n);
|
|
}
|
|
return 0;
|
|
*/
|
|
}
|
|
|
|
int nvram_set_file(const char *key, const char *fname, int max)
|
|
{
|
|
char *in;
|
|
char *out;
|
|
long len;
|
|
int n;
|
|
int r;
|
|
|
|
if ((len = f_size(fname)) > max) return 0;
|
|
max = (int)len;
|
|
r = 0;
|
|
if (f_read_alloc(fname, &in, max) == max) {
|
|
if ((out = malloc(base64_encoded_len(max) + 128)) != NULL) {
|
|
n = base64_encode(in, out, max);
|
|
out[n] = 0;
|
|
nvram_set(key, out);
|
|
free(out);
|
|
r = 1;
|
|
}
|
|
free(in);
|
|
}
|
|
return r;
|
|
/*
|
|
char a[2048];
|
|
char b[4096];
|
|
int n;
|
|
|
|
if (((n = f_read(fname, &a, sizeof(a))) > 0) && (n <= max)) {
|
|
n = base64_encode(a, b, n);
|
|
b[n] = 0;
|
|
nvram_set(key, b);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
*/
|
|
}
|
|
|
|
int nvram_contains_word(const char *key, const char *word)
|
|
{
|
|
return (find_word(nvram_safe_get(key), word) != NULL);
|
|
}
|
|
|
|
int nvram_is_empty(const char *key)
|
|
{
|
|
char *p;
|
|
return (((p = nvram_get(key)) == NULL) || (*p == 0));
|
|
}
|
|
|
|
void nvram_commit_x(void)
|
|
{
|
|
if (!nvram_get_int("debug_nocommit")) nvram_commit();
|
|
}
|
|
|
|
int connect_timeout(int fd, const struct sockaddr *addr, socklen_t len, int timeout)
|
|
{
|
|
fd_set fds;
|
|
struct timeval tv;
|
|
int flags;
|
|
int n;
|
|
int r;
|
|
|
|
if (((flags = fcntl(fd, F_GETFL, 0)) < 0) ||
|
|
(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)) {
|
|
_dprintf("%s: error in F_*ETFL %d\n", __FUNCTION__, fd);
|
|
return -1;
|
|
}
|
|
|
|
if (connect(fd, addr, len) < 0) {
|
|
// _dprintf("%s: connect %d = <0\n", __FUNCTION__, fd);
|
|
|
|
if (errno != EINPROGRESS) {
|
|
_dprintf("%s: error in connect %d errno=%d\n", __FUNCTION__, fd, errno);
|
|
return -1;
|
|
}
|
|
|
|
while (1) {
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
FD_ZERO(&fds);
|
|
FD_SET(fd, &fds);
|
|
r = select(fd + 1, NULL, &fds, NULL, &tv);
|
|
if (r == 0) {
|
|
_dprintf("%s: timeout in select %d\n", __FUNCTION__, fd);
|
|
return -1;
|
|
}
|
|
else if (r < 0) {
|
|
if (errno != EINTR) {
|
|
_dprintf("%s: error in select %d\n", __FUNCTION__, fd);
|
|
return -1;
|
|
}
|
|
// loop
|
|
}
|
|
else {
|
|
r = 0;
|
|
n = sizeof(r);
|
|
if ((getsockopt(fd, SOL_SOCKET, SO_ERROR, &r, &n) < 0) || (r != 0)) {
|
|
_dprintf("%s: error in SO_ERROR %d\n", __FUNCTION__, fd);
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fcntl(fd, F_SETFL, flags) < 0) {
|
|
_dprintf("%s: error in F_*ETFL %d\n", __FUNCTION__, fd);
|
|
return -1;
|
|
}
|
|
|
|
// _dprintf("%s: OK %d\n", __FUNCTION__, fd);
|
|
return 0;
|
|
}
|
|
|
|
void chld_reap(int sig)
|
|
{
|
|
while (waitpid(-1, NULL, WNOHANG) > 0) {}
|
|
}
|
|
|
|
/*
|
|
int time_ok(void)
|
|
{
|
|
return time(0) > Y2K;
|
|
}
|
|
*/
|