1/*
2 * Broadcom Home Gateway Reference Design
3 * Web Page Configuration Support Routines
4 *
5 * Copyright 2004, Broadcom Corporation
6 * All Rights Reserved.
7 *
8 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
9 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
10 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
11 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id: broadcom.c,v 1.1.1.1 2008/10/15 03:31:22 james26_jang Exp $
13 */
14
15#ifdef WEBS
16#include <webs.h>
17#include <uemf.h>
18#include <ej.h>
19#else /* !WEBS */
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <ctype.h>
24#include <errno.h>
25#include <unistd.h>
26#include <limits.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <arpa/inet.h>
32#include <assert.h>
33#include <httpd.h>
34#endif /* WEBS */
35
36
37#include <typedefs.h>
38#include <proto/ethernet.h>
39#include <bcmnvram.h>
40#include <bcmutils.h>
41#include <shutils.h>
42#include <netconf.h>
43#include <nvparse.h>
44#include <wlutils.h>
45#include <bcmcvar.h>
46#include <ezc.h>
47#include <bcmconfig.h>
48
49static char * rfctime(const time_t *timep);
50static char * reltime(unsigned int seconds);
51static bool find_ethaddr_in_list (void *ethaddr, struct maclist *list);
52
53#define wan_prefix(unit, prefix)	snprintf(prefix, sizeof(prefix), "wan%d_", unit)
54
55/*
56 * Country names and abbreviations from ISO 3166
57 */
58typedef struct {
59	char *name;     /* Long name */
60	char *abbrev;   /* Abbreviation */
61} country_name_t;
62country_name_t country_names[];     /* At end of this file */
63
64struct variable variables[];
65extern struct nvram_tuple router_defaults[];
66
67enum {
68	NOTHING,
69	REBOOT,
70	RESTART,
71};
72
73static const char * const apply_header =
74"<head>"
75"<title>Broadcom Home Gateway Reference Design: Apply</title>"
76"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
77"<style type=\"text/css\">"
78"body { background: white; color: black; font-family: arial, sans-serif; font-size: 9pt }"
79".title	{ font-family: arial, sans-serif; font-size: 13pt; font-weight: bold }"
80".subtitle { font-family: arial, sans-serif; font-size: 11pt }"
81".label { color: #306498; font-family: arial, sans-serif; font-size: 7pt }"
82"</style>"
83"</head>"
84"<body>"
85"<p>"
86"<span class=\"title\">APPLY</span><br>"
87"<span class=\"subtitle\">This screen notifies you of any errors "
88"that were detected while changing the router's settings.</span>"
89"<form method=\"get\" action=\"apply.cgi\">"
90"<p>"
91;
92
93static const char * const apply_footer =
94"<p>"
95"<input type=\"button\" name=\"action\" value=\"Continue\" OnClick=\"document.location.href='%s';\">"
96"</form>"
97"<p class=\"label\">&#169;2001-2004 Broadcom Corporation. All rights reserved.</p>"
98"</body>"
99;
100
101
102#if defined(linux)
103
104#include <fcntl.h>
105#include <signal.h>
106#include <time.h>
107#include <sys/klog.h>
108#include <sys/wait.h>
109#include <sys/ioctl.h>
110#include <net/if.h>
111
112typedef u_int64_t u64;
113typedef u_int32_t u32;
114typedef u_int16_t u16;
115typedef u_int8_t u8;
116#include <linux/ethtool.h>
117#include <linux/sockios.h>
118#include <net/if_arp.h>
119
120#define sys_restart() kill(1, SIGHUP)
121#define sys_reboot() kill(1, SIGTERM)
122#define sys_stats(url) eval("stats", (url))
123
124#ifndef WEBS
125
126#define MIN_BUF_SIZE	4096
127
128/* Upgrade from remote server or socket stream */
129static int
130sys_upgrade(char *url, FILE *stream, int *total)
131{
132	char upload_fifo[] = "/tmp/uploadXXXXXX";
133	FILE *fifo = NULL;
134	char *write_argv[] = { "write", upload_fifo, "linux", NULL };
135	pid_t pid;
136	char *buf = NULL;
137	int count, ret = 0;
138	long flags = -1;
139	int size = BUFSIZ;
140
141	if (url)
142		return eval("write", url, "linux");
143
144	/* Feed write from a temporary FIFO */
145	if (!mktemp(upload_fifo) ||
146	    mkfifo(upload_fifo, S_IRWXU) < 0||
147	    (ret = _eval(write_argv, NULL, 0, &pid)) ||
148	    !(fifo = fopen(upload_fifo, "w"))) {
149		if (!ret)
150			ret = errno;
151		goto err;
152	}
153
154	/* Set nonblock on the socket so we can timeout */
155	if ((flags = fcntl(fileno(stream), F_GETFL)) < 0 ||
156	    fcntl(fileno(stream), F_SETFL, flags | O_NONBLOCK) < 0) {
157		ret = errno;
158		goto err;
159	}
160
161	/*
162	* The buffer must be at least as big as what the stream file is
163	* using so that it can read all the data that has been buffered
164	* in the stream file. Otherwise it would be out of sync with fn
165	* select specially at the end of the data stream in which case
166	* the select tells there is no more data available but there in
167	* fact is data buffered in the stream file's buffer. Since no
168	* one has changed the default stream file's buffer size, let's
169	* use the constant BUFSIZ until someone changes it.
170	*/
171	if (size < MIN_BUF_SIZE)
172		size = MIN_BUF_SIZE;
173	if ((buf = malloc(size)) == NULL) {
174		ret = ENOMEM;
175		goto err;
176	}
177
178	/* Pipe the rest to the FIFO */
179	cprintf("Upgrading");
180	while (total && *total) {
181		if (waitfor(fileno(stream), 5) <= 0)
182			break;
183		count = safe_fread(buf, 1, size, stream);
184		if (!count && (ferror(stream) || feof(stream)))
185			break;
186		*total -= count;
187		safe_fwrite(buf, 1, count, fifo);
188		cprintf(".");
189	}
190	fclose(fifo);
191	fifo = NULL;
192
193	/* Wait for write to terminate */
194	waitpid(pid, &ret, 0);
195	cprintf("done\n");
196
197	/* Reset nonblock on the socket */
198	if (fcntl(fileno(stream), F_SETFL, flags) < 0) {
199		ret = errno;
200		goto err;
201	}
202
203 err:
204 	if (buf)
205		free(buf);
206	if (fifo)
207		fclose(fifo);
208	unlink(upload_fifo);
209	return ret;
210}
211
212#endif /* WEBS */
213
214/* Dump firewall log */
215static int
216ej_dumplog(int eid, webs_t wp, int argc, char_t **argv)
217{
218	char buf[4096], *line, *next, *s;
219	int len, ret = 0;
220
221	time_t tm;
222	char *verdict, *src, *dst, *proto, *spt, *dpt;
223
224	if (klogctl(3, buf, 4096) < 0) {
225		websError(wp, 400, "Insufficient memory\n");
226		return -1;
227	}
228
229	for (next = buf; (line = strsep(&next, "\n"));) {
230		if (!strncmp(line, "<4>DROP", 7))
231			verdict = "denied";
232		else if (!strncmp(line, "<4>ACCEPT", 9))
233			verdict = "accepted";
234		else
235			continue;
236
237		/* Parse into tokens */
238		s = line;
239		len = strlen(s);
240		while (strsep(&s, " "));
241
242		/* Initialize token values */
243		time(&tm);
244		src = dst = proto = spt = dpt = "n/a";
245
246		/* Set token values */
247		for (s = line; s < &line[len] && *s; s += strlen(s) + 1) {
248			if (!strncmp(s, "TIME=", 5))
249				tm = strtoul(&s[5], NULL, 10);
250			else if (!strncmp(s, "SRC=", 4))
251				src = &s[4];
252			else if (!strncmp(s, "DST=", 4))
253				dst = &s[4];
254			else if (!strncmp(s, "PROTO=", 6))
255				proto = &s[6];
256			else if (!strncmp(s, "SPT=", 4))
257				spt = &s[4];
258			else if (!strncmp(s, "DPT=", 4))
259				dpt = &s[4];
260		}
261
262		ret += websWrite(wp, "%s %s connection %s to %s:%s from %s:%s\n",
263				 rfctime(&tm), proto, verdict, dst, dpt, src, spt);
264		ret += websWrite(wp, "<br>");
265	}
266
267	return ret;
268}
269
270static int
271ej_syslog(int eid, webs_t wp, int argc, char_t **argv)
272{
273	FILE *fp;
274	char buf[256] = "/sbin/logread > ";
275	char tmp[] = "/tmp/log.XXXXXX";
276	int ret;
277
278	if (!nvram_match("log_ram_enable", "1")) {
279		websError(wp, 400, "\"Syslog in RAM\" is not enabled.\n");
280		return (-1);
281	}
282
283	mktemp(tmp);
284	strcat(buf, tmp);
285	system(buf);
286
287	fp = fopen(tmp, "r");
288
289	unlink(tmp);
290
291	if (fp == NULL) {
292		websError(wp, 400, "logread error\n");
293		return (-1);
294	}
295
296	websWrite(wp, "<pre>");
297
298	ret = 0;
299	while(fgets(buf, sizeof(buf), fp))
300		ret += websWrite(wp, buf);
301
302	ret += websWrite(wp, "</pre>");
303
304	fclose(fp);
305
306	return (ret);
307}
308
309struct lease_t {
310	unsigned char chaddr[16];
311	u_int32_t yiaddr;
312	u_int32_t expires;
313	char hostname[64];
314};
315
316/* Dump leases in <tr><td>hostname</td><td>MAC</td><td>IP</td><td>expires</td></tr> format */
317static int
318ej_lan_leases(int eid, webs_t wp, int argc, char_t **argv)
319{
320	FILE *fp = NULL;
321	struct lease_t lease;
322	int i;
323	struct in_addr addr;
324	unsigned long expires = 0;
325	char sigusr1[] = "-XX";
326	int ret = 0;
327
328	/* Write out leases file */
329	sprintf(sigusr1, "-%d", SIGUSR1);
330	eval("killall", sigusr1, "udhcpd");
331
332	if (!(fp = fopen("/tmp/udhcpd.leases", "r")))
333		return 0;
334
335	while (fread(&lease, sizeof(lease), 1, fp)) {
336		/* Do not display reserved leases */
337		if (ETHER_ISNULLADDR(lease.chaddr))
338			continue;
339		ret += websWrite(wp, "<tr><td>%s</td><td>", lease.hostname);
340		for (i = 0; i < 6; i++) {
341			ret += websWrite(wp, "%02X", lease.chaddr[i]);
342			if (i != 5) ret += websWrite(wp, ":");
343		}
344		addr.s_addr = lease.yiaddr;
345		ret += websWrite(wp, "</td><td>%s</td><td>", inet_ntoa(addr));
346		expires = ntohl(lease.expires);
347		if (!expires)
348			ret += websWrite(wp, "Expired");
349		else
350			ret += websWrite(wp, "%s", reltime(expires));
351		ret += websWrite(wp, "</td></tr>");
352	}
353
354	fclose(fp);
355
356	return ret;
357}
358
359/* Renew lease */
360static int
361sys_renew(void)
362{
363	int unit;
364	char tmp[NVRAM_BUFSIZE];
365	char *str;
366	int pid;
367
368	if ((unit = atoi(nvram_safe_get("wan_unit"))) < 0)
369		unit = 0;
370
371	snprintf(tmp, sizeof(tmp), "/var/run/udhcpc%d.pid", unit);
372	if ((str = file2str(tmp))) {
373		pid = atoi(str);
374		free(str);
375		return kill(pid, SIGUSR1);
376	}
377
378	return -1;
379}
380
381/* Release lease */
382static int
383sys_release(void)
384{
385	int unit;
386	char tmp[NVRAM_BUFSIZE];
387	char *str;
388	int pid;
389
390	if ((unit = atoi(nvram_safe_get("wan_unit"))) < 0)
391		unit = 0;
392
393	snprintf(tmp, sizeof(tmp), "/var/run/udhcpc%d.pid", unit);
394	if ((str = file2str(tmp))) {
395		pid = atoi(str);
396		free(str);
397		return kill(pid, SIGUSR2);
398	}
399
400	return -1;
401}
402
403#ifdef __CONFIG_NAT__
404#define sin_addr(s) (((struct sockaddr_in *)(s))->sin_addr)
405
406/* Return WAN link state */
407static int
408ej_wan_link(int eid, webs_t wp, int argc, char_t **argv)
409{
410	char *wan_ifname;
411	int s;
412	struct ifreq ifr;
413	struct ethtool_cmd ecmd;
414	FILE *fp;
415	int unit;
416	char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
417
418	if ((unit = atoi(nvram_safe_get("wan_unit"))) < 0)
419		unit = 0;
420	wan_prefix(unit, prefix);
421
422	/* non-exist and disabled */
423	if (nvram_match(strcat_r(prefix, "proto", tmp), "") ||
424	    nvram_match(strcat_r(prefix, "proto", tmp), "disabled")) {
425		return websWrite(wp, "N/A");
426	}
427	/* PPPoE connection status */
428	else if (nvram_match(strcat_r(prefix, "proto", tmp), "pppoe")) {
429		wan_ifname = nvram_safe_get(strcat_r(prefix, "pppoe_ifname", tmp));
430		if ((fp = fopen(strcat_r("/tmp/ppp/link.", wan_ifname, tmp), "r"))) {
431			fclose(fp);
432			return websWrite(wp, "Connected");
433		} else
434			return websWrite(wp, "Disconnected");
435	}
436	/* Get real interface name */
437	else
438		wan_ifname = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
439
440	/* Open socket to kernel */
441	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
442		return websWrite(wp, "N/A");
443
444	/* Check for hardware link */
445	strncpy(ifr.ifr_name, wan_ifname, IFNAMSIZ);
446	ifr.ifr_data = (void *) &ecmd;
447	ecmd.cmd = ETHTOOL_GSET;
448	if (ioctl(s, SIOCETHTOOL, &ifr) < 0) {
449		close(s);
450		return websWrite(wp, "Unknown");
451	}
452	if (!ecmd.speed) {
453		close(s);
454		return websWrite(wp, "Disconnected");
455	}
456
457	/* Check for valid IP address */
458	strncpy(ifr.ifr_name, wan_ifname, IFNAMSIZ);
459	if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
460		close(s);
461		return websWrite(wp, "Connecting");
462	}
463
464	/* Otherwise we are probably configured */
465	close(s);
466	return websWrite(wp, "Connected");
467}
468
469/* Display IP Address lease */
470static int
471ej_wan_lease(int eid, webs_t wp, int argc, char_t **argv)
472{
473	unsigned long expires = 0;
474	int ret = 0;
475	int unit;
476	char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
477
478	if ((unit = atoi(nvram_safe_get("wan_unit"))) < 0)
479		unit = 0;
480	wan_prefix(unit, prefix);
481
482	if (nvram_match(strcat_r(prefix, "proto", tmp), "dhcp")) {
483		char *str;
484		time_t now;
485
486		snprintf(tmp, sizeof(tmp), "/tmp/udhcpc%d.expires", unit);
487		if ((str = file2str(tmp))) {
488			expires = atoi(str);
489			free(str);
490		}
491		time(&now);
492		if (expires <= now)
493			ret += websWrite(wp, "Expired");
494		else
495			ret += websWrite(wp, "%s", reltime(expires - now));
496	} else
497		ret += websWrite(wp, "N/A");
498
499	return ret;
500}
501#endif	/* __CONFIG_NAT__ */
502
503/* Report sys up time */
504static int
505ej_sysuptime(int eid, webs_t wp, int argc, char_t **argv)
506{
507	char *str = file2str("/proc/uptime");
508	if (str) {
509		unsigned int up = atoi(str);
510		free(str);
511		return websWrite(wp, reltime(up));
512	}
513	return websWrite(wp, "N/A");
514}
515
516#ifdef __CONFIG_NAT__
517/* Return a list of wan interfaces (eth0/eth1/eth2/eth3) */
518static int
519ej_wan_iflist(int eid, webs_t wp, int argc, char_t **argv)
520{
521	char name[IFNAMSIZ], *next;
522	int ret = 0;
523	int unit;
524	char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
525	char ea[64];
526	int s;
527	struct ifreq ifr;
528
529	/* current unit # */
530	if ((unit = atoi(nvram_safe_get("wan_unit"))) < 0)
531		unit = 0;
532	wan_prefix(unit, prefix);
533
534	if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
535		return errno;
536
537	/* build wan interface name list */
538	foreach(name, nvram_safe_get("wan_ifnames"), next) {
539		strncpy(ifr.ifr_name, name, IFNAMSIZ);
540		if (ioctl(s, SIOCGIFHWADDR, &ifr))
541			continue;
542		ret += websWrite(wp, "<option value=\"%s\" %s>%s (%s)</option>", name,
543				 nvram_match(strcat_r(prefix, "ifname", tmp), name) ? "selected" : "",
544				 name, ether_etoa(ifr.ifr_hwaddr.sa_data, ea));
545	}
546
547	close(s);
548
549	return ret;
550}
551#endif	/* __CONFIG_NAT__ */
552
553
554#endif /* vxworks */
555
556static int
557ej_asp_list(int eid, webs_t wp, int argc, char_t **argv)
558{
559	websWrite(wp,
560	  "<tr>\n"
561	  "  <td><a href=\"index.asp\"><img border=\"0\" src=\"basic.gif\" alt=\"Basic\"></a></td>\n"
562	  "  <td><a href=\"lan.asp\"><img border=\"0\" src=\"lan.gif\" alt=\"LAN\"></a></td>\n");
563#ifdef __CONFIG_NAT__
564	websWrite(wp,
565	  "  <td><a href=\"wan.asp\"><img border=\"0\" src=\"wan.gif\" alt=\"WAN\"></a></td>\n");
566#endif
567	websWrite(wp,
568	  "  <td><a href=\"status.asp\"><img border=\"0\" src=\"status.gif\" alt=\"Status\"></a></td>\n");
569#ifdef __CONFIG_NAT__
570	websWrite(wp,
571	  "  <td><a href=\"filter.asp\"><img border=\"0\" src=\"filter.gif\" alt=\"Filters\"></a></td>\n"
572	  "  <td><a href=\"forward.asp\"><img border=\"0\" src=\"forward.gif\" alt=\"Routing\"></a></td>\n");
573#endif
574	websWrite(wp,
575	  "  <td><a href=\"wireless.asp\"><img border=\"0\" src=\"wireless.gif\" alt=\"Wireless\"></a></td>\n"
576	  "  <td><a href=\"security.asp\"><img border=\"0\" src=\"security.gif\" alt=\"Security\"></a></td>\n"
577	  "  <td><a href=\"firmware.asp\"><img border=\"0\" src=\"firmware.gif\" alt=\"Firmware\"></a></td>\n");
578	websWrite(wp,
579	  "  <td width=\"100%%\"></td>\n"
580	  "</tr>\n");
581	return 0;
582}
583
584static char *
585rfctime(const time_t *timep)
586{
587	static char s[201];
588	struct tm tm;
589
590#if defined(linux)
591	setenv("TZ", nvram_safe_get("time_zone"), 1);
592#endif
593	memcpy(&tm, localtime(timep), sizeof(struct tm));
594	strftime(s, 200, "%a, %d %b %Y %H:%M:%S %z", &tm);
595	return s;
596}
597
598static char *
599reltime(unsigned int seconds)
600{
601	static char s[] = "XXXXX days, XX hours, XX minutes, XX seconds";
602	char *c = s;
603
604	if (seconds > 60*60*24) {
605		c += sprintf(c, "%d days, ", seconds / (60*60*24));
606		seconds %= 60*60*24;
607	}
608	if (seconds > 60*60) {
609		c += sprintf(c, "%d hours, ", seconds / (60*60));
610		seconds %= 60*60;
611	}
612	if (seconds > 60) {
613		c += sprintf(c, "%d minutes, ", seconds / 60);
614		seconds %= 60;
615	}
616	c += sprintf(c, "%d seconds", seconds);
617
618	return s;
619}
620
621/* Report time in RFC-822 format */
622static int
623ej_localtime(int eid, webs_t wp, int argc, char_t **argv)
624{
625	time_t tm;
626
627	time(&tm);
628	return websWrite(wp, rfctime(&tm));
629}
630
631static int
632ej_wl_mode_list(int eid, webs_t wp, int argc, char_t **argv)
633{
634	int unit;
635	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
636	char *name, *next;
637	int ap = 0, sta = 0, wet = 0, wds = 0;
638	char cap[WLC_IOCTL_SMLEN];
639	char caps[WLC_IOCTL_SMLEN];
640
641	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
642		return -1;
643
644	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
645	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
646
647	if (wl_get_val(name, "cap", (void *)caps, WLC_IOCTL_SMLEN))
648		return -1;
649
650	foreach(cap, caps, next) {
651		if (!strcmp(cap, "ap"))
652			ap = wds = 1;
653		else if (!strcmp(cap, "sta"))
654			sta = 1;
655		else if (!strcmp(cap, "wet"))
656			wet = 1;
657        }
658
659	if (ap)
660		websWrite(wp, "<option value=\"ap\" %s>Access Point</option>\n",
661				nvram_match("wl_mode", "ap" ) ? "selected" : "");
662	if (wds)
663		websWrite(wp, "<option value=\"wds\" %s>Wireless Bridge</option>\n",
664				nvram_match("wl_mode", "wds" ) ? "selected" : "");
665	if (wet)
666		websWrite(wp, "<option value=\"wet\" %s>Wireless Ethernet</option>\n",
667			nvram_match("wl_mode", "wet" ) ? "selected" : "");
668
669	return 0;
670
671}
672
673static int
674ej_wl_inlist(int eid, webs_t wp, int argc, char_t **argv)
675{
676	int unit;
677	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
678	char *name, *next;
679	char cap[WLC_IOCTL_SMLEN];
680	char caps[WLC_IOCTL_SMLEN];
681	char *var, *item;
682
683	if (ejArgs(argc, argv, "%s %s", &var, &item) < 2) {
684		websError(wp, 400, "Insufficient args\n");
685		return -1;
686	}
687
688	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
689		return -1;
690
691	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
692	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
693
694	if (wl_get_val(name, var, (void *)caps, WLC_IOCTL_SMLEN))
695		return -1;
696
697	foreach(cap, caps, next) {
698		if (!strcmp(cap, item))
699			return websWrite(wp, "1");
700	}
701
702	return websWrite(wp, "0");
703}
704
705static int
706ej_wl_wds_status(int eid, webs_t wp, int argc, char_t **argv)
707{
708	int unit;
709	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
710	char *macs, *next, *name;
711	char mac[100];
712	int i, len;
713	sta_info_t *sta;
714	char buf[300];
715
716	if (ejArgs(argc, argv, "%d", &i) < 1) {
717		websError(wp, 400, "Insufficient args\n");
718		return -1;
719	}
720
721	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
722		return -1;
723
724	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
725	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
726	macs = nvram_safe_get(strcat_r(prefix, "wds", tmp));
727
728	foreach(mac, macs, next) {
729		if (i-- == 0) {
730			len = sprintf(buf, "sta_info");
731			ether_atoe(mac, (unsigned char *)&buf[len + 1]);
732			if (atoi(nvram_safe_get(strcat_r(prefix, "wds_timeout", tmp))) &&
733			    !wl_ioctl(name, WLC_GET_VAR, buf, sizeof(buf))) {
734				sta = (sta_info_t *)buf;
735				return websWrite(wp, "%s", (sta->flags & WL_WDS_LINKUP) ? "up" : "down");
736			}
737			else
738				return websWrite(wp, "%s", "unknown");
739		}
740	}
741
742	return 0;
743}
744
745static int
746ej_ses_button_display(int eid, webs_t wp, int argc, char_t **argv)
747{
748#ifdef __CONFIG_SES__
749	if (atoi(nvram_safe_get("ses_enable")) == 0) {
750		return 1;
751	}
752
753	websWrite(wp, "<tr><th width=\"310\"> SES Button:&nbsp;&nbsp; </th>");
754	websWrite(wp, "<td>&nbsp;&nbsp;</td>");
755	websWrite(wp, "<td><input type=\"submit\" name=\"action\" value=\"NewSesNW\" ></td></tr>");
756	websWrite(wp, "<tr><th width=\"310\"> SES Button:&nbsp;&nbsp; </th>");
757	websWrite(wp, "<td>&nbsp;&nbsp;</td>");
758	websWrite(wp, "<td><input type=\"submit\" name=\"action\" value=\"OpenWindow\" ></td></tr>");
759	websWrite(wp, "<tr><th width=\"310\"> SES Button:&nbsp;&nbsp; </th>");
760	websWrite(wp, "<td>&nbsp;&nbsp;</td>");
761	websWrite(wp, "<td><input type=\"submit\" name=\"action\" value=\"NewSesNWAndOW\" ></td></tr>");
762	websWrite(wp, "<tr><th width=\"310\"> SES Button:&nbsp;&nbsp; </th>");
763	websWrite(wp, "<td>&nbsp;&nbsp;</td>");
764	websWrite(wp, "<td><input type=\"submit\" name=\"action\" value=\"ResetNWToDefault\" ></td></tr>");
765
766#endif /* __CONFIG_SES__ */
767
768	return 1;
769}
770
771static int
772ej_wl_radio_roam_option(int eid, webs_t wp, int argc, char_t **argv)
773{
774	int unit;
775	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
776	char *name;
777	int radio_status = 0;
778
779	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0) {
780		websError(wp, 400, "unit number variable doesn't exist\n");
781		return -1;
782	}
783	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
784	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
785	wl_ioctl(name, WLC_GET_RADIO, &radio_status, sizeof (radio_status));
786	if (!radio_status) /* Radio on*/
787		websWrite(wp, "<input type=\"submit\" name=\"action\" value=\"RadioOff\" >");
788	else /* Radio Off */
789		websWrite(wp, "<input type=\"submit\" name=\"action\" value=\"RadioOn\" >");
790
791	return 1;
792
793
794}
795static int
796wl_radio_onoff(webs_t wp, int disable)
797{
798	int unit;
799	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
800	char *name;
801	char *interface_status;
802
803
804	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0) {
805		websError(wp, 400, "unit number variable doesn't exist\n");
806		return -1;
807	}
808
809	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
810	interface_status = nvram_safe_get(strcat_r(prefix, "radio", tmp));
811
812	if (interface_status != NULL) {
813		if (!strcmp(interface_status, "1")) {
814			snprintf(prefix, sizeof(prefix), "wl%d_", unit);
815			name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
816			wl_ioctl(name, WLC_SET_RADIO, &disable, sizeof (disable));
817		}
818		else {
819			websWrite(wp, "Interface is not Enabled...");
820			return 0;
821		}
822	}
823	else {
824		websWrite(wp, "Interface  status UnKnown...");
825		return 0;
826	}
827
828
829	return 0;
830
831}
832
833/*
834 * Example:
835 * lan_ipaddr=192.168.1.1
836 * <% nvram_get("lan_ipaddr"); %> produces "192.168.1.1"
837 * <% nvram_get("undefined"); %> produces ""
838 */
839static int
840ej_nvram_get(int eid, webs_t wp, int argc, char_t **argv)
841{
842	char *name, *c;
843	int ret = 0;
844
845	if (ejArgs(argc, argv, "%s", &name) < 1) {
846		websError(wp, 400, "Insufficient args\n");
847		return -1;
848	}
849
850	for (c = nvram_safe_get(name); *c; c++) {
851		if (isprint((int) *c) &&
852		    *c != '"' && *c != '&' && *c != '<' && *c != '>')
853			ret += websWrite(wp, "%c", *c);
854		else
855			ret += websWrite(wp, "&#%d", *c);
856	}
857
858	return ret;
859}
860
861/*
862 * Example:
863 * wan_proto=dhcp
864 * <% nvram_match("wan_proto", "dhcp", "selected"); %> produces "selected"
865 * <% nvram_match("wan_proto", "static", "selected"); %> does not produce
866 */
867static int
868ej_nvram_match(int eid, webs_t wp, int argc, char_t **argv)
869{
870	char *name, *match, *output;
871
872	if (ejArgs(argc, argv, "%s %s %s", &name, &match, &output) < 3) {
873		websError(wp, 400, "Insufficient args\n");
874		return -1;
875	}
876
877	if (nvram_match(name, match))
878		return websWrite(wp, output);
879
880	return 0;
881}
882
883static int
884ej_wme_match_op(int eid, webs_t wp, int argc, char_t **argv)
885{
886	char *name, *match, *output;
887	char word[256], *next;
888
889	if (ejArgs(argc, argv, "%s %s %s", &name, &match, &output) < 3) {
890		websError(wp, 400, "Insufficient args\n");
891		return -1;
892	}
893
894	foreach(word, nvram_safe_get(name), next) {
895		if (!strcmp(word, match))
896			return websWrite(wp, output);
897	}
898
899	return 0;
900}
901
902static int
903wl_print_channel_list(webs_t wp, char *name, char *phytype, char *abbrev)
904{
905	int j, status = 0;
906	wl_channels_in_country_t *cic = (wl_channels_in_country_t *)malloc(WLC_IOCTL_MAXLEN);
907
908	if (!cic) {
909		status = -1;
910		goto exit;
911	}
912
913	cic->buflen = WLC_IOCTL_MAXLEN;
914	strcpy(cic->country_abbrev, abbrev);
915	if (!strcmp(phytype, "a"))
916		cic->band = WLC_BAND_A;
917	else if ((!strcmp(phytype, "b")) || (!strcmp(phytype, "g")))
918		cic->band = WLC_BAND_B;
919	else {
920		status = -1;
921		goto exit;
922	}
923
924	if (wl_ioctl(name, WLC_GET_CHANNELS_IN_COUNTRY, cic, cic->buflen) == 0) {
925		if (cic->count == 0) {
926			status = 0;
927			goto exit;
928		}
929		websWrite(wp, "\t\tif (country == \"%s\")\n\t\t\tchannels = new Array(0",
930			abbrev);
931		for(j = 0; j < cic->count; j++)
932			websWrite(wp, ", %d", cic->channel[j]);
933		websWrite(wp,");\n");
934	}
935
936exit:
937	if (cic)
938		free((void *)cic);
939	return status;
940}
941
942static int
943ej_wl_country_list(int eid, webs_t wp, int argc, char_t **argv)
944{
945	int unit, i, status = 0;
946	char *name;
947	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
948	char *phytype = NULL;
949	wl_country_list_t *cl = (wl_country_list_t *)malloc(WLC_IOCTL_MAXLEN);
950	country_name_t *cntry;
951	char *abbrev;
952
953	if (!cl) {
954		status = -1;
955		goto exit;
956	}
957
958	if (ejArgs(argc, argv, "%s", &phytype) < 1) {
959		websError(wp, 400, "Insufficient args\n");
960		status = -1;
961		goto exit;
962	}
963
964	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0) {
965		status = -1;
966		goto exit;
967	}
968
969	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
970	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
971
972	cl->buflen = WLC_IOCTL_MAXLEN;
973	cl->band_set = TRUE;
974
975	if (!strcmp(phytype, "a"))
976		cl->band = WLC_BAND_A;
977	else if ((!strcmp(phytype, "b")) || (!strcmp(phytype, "g")))
978		cl->band = WLC_BAND_B;
979	else {
980		status = -1;
981		goto exit;
982	}
983
984	if (wl_ioctl(name, WLC_GET_COUNTRY_LIST, cl, cl->buflen) == 0) {
985		websWrite(wp, "\t\tvar countries = new Array(");
986		for(i = 0; i < cl->count; i++) {
987			abbrev = &cl->country_abbrev[i*WLC_CNTRY_BUF_SZ];
988			websWrite(wp, "\"%s\"", abbrev);
989			if (i != (cl->count - 1))
990				websWrite(wp, ", ");
991		}
992		websWrite(wp, ");\n");
993		for(i = 0; i < cl->count; i++) {
994			abbrev = &cl->country_abbrev[i*WLC_CNTRY_BUF_SZ];
995			for(cntry = country_names;
996				cntry->name && strcmp(abbrev, cntry->abbrev);
997				cntry++);
998			websWrite(wp, "\t\tdocument.forms[0].wl_country_code[%d] = new Option(\"%s\", \"%s\");\n",
999				i, cntry->name ? cntry->name : abbrev, abbrev);
1000		}
1001	}
1002
1003exit:
1004	if (cl)
1005		free((void *)cl);
1006	return status;
1007}
1008
1009static int
1010ej_wl_channel_list(int eid, webs_t wp, int argc, char_t **argv)
1011{
1012	int unit, i, status = 0;
1013	char *name;
1014	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1015	char *phytype = NULL;
1016	wl_country_list_t *cl = (wl_country_list_t *)malloc(WLC_IOCTL_MAXLEN);
1017	char *abbrev;
1018
1019	if (!cl) {
1020		status = -1;
1021		goto exit;
1022	}
1023
1024	if (ejArgs(argc, argv, "%s", &phytype) < 1) {
1025		websError(wp, 400, "Insufficient args\n");
1026		status = -1;
1027		goto exit;
1028	}
1029
1030	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0) {
1031		status = -1;
1032		goto exit;
1033	}
1034
1035	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1036	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
1037
1038	cl->buflen = WLC_IOCTL_MAXLEN;
1039	cl->band_set = TRUE;
1040
1041	if (!strcmp(phytype, "a"))
1042		cl->band = WLC_BAND_A;
1043	else if ((!strcmp(phytype, "b")) || (!strcmp(phytype, "g")))
1044		cl->band = WLC_BAND_B;
1045	else {
1046		status = -1;
1047		goto exit;
1048	}
1049
1050	if (wl_ioctl(name, WLC_GET_COUNTRY_LIST, cl, cl->buflen) == 0) {
1051		for(i = 0; i < cl->count; i++) {
1052			abbrev = &cl->country_abbrev[i*WLC_CNTRY_BUF_SZ];
1053			wl_print_channel_list(wp, name, phytype, abbrev);
1054		}
1055	}
1056
1057
1058exit:
1059	if (cl)
1060		free((void *)cl);
1061	return status;
1062}
1063
1064static bool find_ethaddr_in_list (void *ethaddr, struct maclist *list)
1065{
1066	int i;
1067	for (i = 0; i < list->count; i ++) {
1068		if (!bcmp(ethaddr, (void *)&list->ea[i], ETHER_ADDR_LEN)) {
1069			return TRUE;
1070		}
1071	}
1072
1073	return FALSE;
1074}
1075
1076static int
1077ej_wl_auth_list(int eid, webs_t wp, int argc, char_t **argv)
1078{
1079	int unit;
1080	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1081	char *name;
1082	struct maclist *auth, *assoc, *authorized, *wme;
1083	int max_sta_count, maclist_size;
1084	int i;
1085
1086	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
1087		return -1;
1088
1089	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1090	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
1091
1092	/* buffers and length */
1093	max_sta_count = MAX_STA_COUNT;
1094	maclist_size = sizeof(auth->count) + max_sta_count * sizeof(struct ether_addr);
1095
1096	auth = malloc(maclist_size);
1097	assoc = malloc(maclist_size);
1098	authorized = malloc(maclist_size);
1099	wme = malloc(maclist_size);
1100
1101	if (!auth || !assoc || !authorized || !wme)
1102		goto exit;
1103
1104	/* query wl for authenticated sta list */
1105	strcpy((char*)auth, "authe_sta_list");
1106	if (wl_ioctl(name, WLC_GET_VAR, auth, maclist_size))
1107		goto exit;
1108
1109	/* query wl for associated sta list */
1110	assoc->count = max_sta_count;
1111	if (wl_ioctl(name, WLC_GET_ASSOCLIST, assoc, maclist_size))
1112		goto exit;
1113
1114	/* query wl for authorized sta list */
1115	strcpy((char*)authorized, "autho_sta_list");
1116	if (wl_ioctl(name, WLC_GET_VAR, authorized, maclist_size))
1117		goto exit;
1118
1119	/* query wl for WME sta list */
1120	strcpy((char*)wme, "wme_sta_list");
1121	if (wl_ioctl(name, WLC_GET_VAR, wme, maclist_size))
1122		goto exit;
1123
1124	/* build authenticated/associated/authorized sta list */
1125	for (i = 0; i < auth->count; i ++) {
1126		char ea[ETHER_ADDR_STR_LEN];
1127		char *value;
1128		websWrite(wp, "<tr><td>%s</td>", ether_etoa((void *)&auth->ea[i], ea));
1129
1130		value = (find_ethaddr_in_list ((void *)&auth->ea[i], assoc))? "Yes" : "No";
1131		websWrite(wp, "<td>%s</td>", value);
1132
1133		value = (find_ethaddr_in_list ((void *)&auth->ea[i], authorized))? "Yes" : "No";
1134		websWrite(wp, "<td>%s</td>", value);
1135
1136		value = (find_ethaddr_in_list ((void *)&auth->ea[i], wme))? "Yes" : "No";
1137		websWrite(wp, "<td>%s</td>", value);
1138
1139		websWrite(wp, "</tr>");
1140	}
1141
1142	/* error/exit */
1143exit:
1144	if (auth) free(auth);
1145	if (assoc) free(assoc);
1146	if (authorized) free(authorized);
1147	if (wme) free(wme);
1148
1149	return 0;
1150}
1151
1152/*
1153 * Example:
1154 * wan_proto=dhcp
1155 * <% nvram_invmatch("wan_proto", "dhcp", "disabled"); %> does not produce
1156 * <% nvram_invmatch("wan_proto", "static", "disabled"); %> produces "disabled"
1157 */
1158static int
1159ej_nvram_invmatch(int eid, webs_t wp, int argc, char_t **argv)
1160{
1161	char *name, *invmatch, *output;
1162
1163	if (ejArgs(argc, argv, "%s %s %s", &name, &invmatch, &output) < 3) {
1164		websError(wp, 400, "Insufficient args\n");
1165		return -1;
1166	}
1167
1168	if (nvram_invmatch(name, invmatch))
1169		return websWrite(wp, output);
1170
1171	return 0;
1172}
1173
1174/*
1175 * Example:
1176 * filter_maclist=00:12:34:56:78:00 00:87:65:43:21:00
1177 * <% nvram_list("filter_maclist", 1); %> produces "00:87:65:43:21:00"
1178 * <% nvram_list("filter_maclist", 100); %> produces ""
1179 */
1180static int
1181ej_nvram_list(int eid, webs_t wp, int argc, char_t **argv)
1182{
1183	char *name;
1184	int which;
1185	char word[256], *next;
1186
1187	if (ejArgs(argc, argv, "%s %d", &name, &which) < 2) {
1188		websError(wp, 400, "Insufficient args\n");
1189		return -1;
1190	}
1191
1192	foreach(word, nvram_safe_get(name), next) {
1193		if (which-- == 0)
1194			return websWrite(wp, word);
1195	}
1196
1197	return 0;
1198}
1199
1200static int
1201ej_nvram_inlist(int eid, webs_t wp, int argc, char_t **argv)
1202{
1203	char *name, *item, *output;
1204	char word[256], *next;
1205
1206	if (ejArgs(argc, argv, "%s %s %s", &name, &item, &output) < 3) {
1207		websError(wp, 400, "Insufficient args\n");
1208		return -1;
1209	}
1210
1211	foreach(word, nvram_safe_get(name), next) {
1212		if (!strcmp(word, item))
1213			return websWrite(wp, output);
1214	}
1215
1216	return 0;
1217}
1218
1219static int
1220ej_nvram_invinlist(int eid, webs_t wp, int argc, char_t **argv)
1221{
1222	char *name, *item, *output;
1223	char word[256], *next;
1224
1225	if (ejArgs(argc, argv, "%s %s %s", &name, &item, &output) < 3) {
1226		websError(wp, 400, "Insufficient args\n");
1227		return -1;
1228	}
1229
1230	foreach(word, nvram_safe_get(name), next) {
1231		if (!strcmp(word, item))
1232			return 0;
1233	}
1234
1235	return websWrite(wp, output);
1236}
1237
1238#ifdef __CONFIG_NAT__
1239/*
1240 * Example:
1241 * <% filter_client(1, 10); %> produces a table of the first 10 client filter entries
1242 */
1243static int
1244ej_filter_client(int eid, webs_t wp, int argc, char_t **argv)
1245{
1246	int i, n, j, ret = 0;
1247	netconf_filter_t start, end;
1248	bool valid;
1249	char port[] = "XXXXX";
1250	char *days[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
1251	char *hours[] = {
1252		"12:00 AM", "1:00 AM", "2:00 AM", "3:00 AM", "4:00 AM", "5:00 AM",
1253		"6:00 AM", "7:00 AM", "8:00 AM", "9:00 AM", "10:00 AM", "11:00 AM",
1254		"12:00 PM", "1:00 PM", "2:00 PM", "3:00 PM", "4:00 PM", "5:00 PM",
1255		"6:00 PM", "7:00 PM", "8:00 PM", "9:00 PM", "10:00 PM", "11:00 PM"
1256	};
1257
1258	if (ejArgs(argc, argv, "%d %d", &i, &n) < 2) {
1259		websError(wp, 400, "Insufficient args\n");
1260		return -1;
1261	}
1262
1263	for (; i <= n; i++) {
1264		valid = get_filter_client(i, &start, &end);
1265
1266		ret += websWrite(wp, "<tr>");
1267		ret += websWrite(wp, "<td></td>");
1268
1269		/* Print address range */
1270		ret += websWrite(wp, "<td><input name=\"filter_client_from_start%d\" value=\"%s\" size=\"15\" maxlength=\"15\"></td>",
1271				 i, valid ? inet_ntoa(start.match.src.ipaddr) : "");
1272		ret += websWrite(wp, "<td>-</td>");
1273		ret += websWrite(wp, "<td><input name=\"filter_client_from_end%d\" value=\"%s\" size=\"15\" maxlength=\"15\"></td>",
1274				 i, valid ? inet_ntoa(end.match.src.ipaddr) : "");
1275		ret += websWrite(wp, "<td></td>");
1276
1277		/* Print protocol */
1278		ret += websWrite(wp, "<td>");
1279		ret += websWrite(wp, "<select name=\"filter_client_proto%d\">", i);
1280		ret += websWrite(wp, "<option value=\"tcp\" %s>TCP</option>",
1281				 valid && start.match.ipproto == IPPROTO_TCP ? "selected" : "");
1282		ret += websWrite(wp, "<option value=\"udp\" %s>UDP</option>",
1283				 valid && start.match.ipproto == IPPROTO_UDP ? "selected" : "");
1284		ret += websWrite(wp, "</select>");
1285		ret += websWrite(wp, "</td>");
1286		ret += websWrite(wp, "<td></td>");
1287
1288		/* Print port range */
1289		if (valid)
1290			snprintf(port, sizeof(port), "%d", ntohs(start.match.dst.ports[0]));
1291		else
1292			*port = '\0';
1293		ret += websWrite(wp, "<td><input name=\"filter_client_to_start%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1294				 i, port);
1295		ret += websWrite(wp, "<td>-</td>");
1296		if (valid)
1297			snprintf(port, sizeof(port), "%d", ntohs(start.match.dst.ports[1]));
1298		else
1299			*port = '\0';
1300		ret += websWrite(wp, "<td><input name=\"filter_client_to_end%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1301				 i, port);
1302		ret += websWrite(wp, "<td></td>");
1303
1304		/* Print day range */
1305		ret += websWrite(wp, "<td>");
1306		ret += websWrite(wp, "<select name=\"filter_client_from_day%d\">", i);
1307		for (j = 0; j < ARRAYSIZE(days); j++)
1308			ret += websWrite(wp, "<option value=\"%d\" %s>%s</option>",
1309					 j, valid && start.match.days[0] == j ? "selected" : "", days[j]);
1310		ret += websWrite(wp, "</select>");
1311		ret += websWrite(wp, "</td>");
1312		ret += websWrite(wp, "<td>-</td>");
1313		ret += websWrite(wp, "<td>");
1314		ret += websWrite(wp, "<select name=\"filter_client_to_day%d\">", i);
1315		for (j = 0; j < ARRAYSIZE(days); j++)
1316			ret += websWrite(wp, "<option value=\"%d\" %s>%s</option>",
1317					 j, valid && start.match.days[1] == j ? "selected" : "", days[j]);
1318		ret += websWrite(wp, "</select>");
1319		ret += websWrite(wp, "</td>");
1320		ret += websWrite(wp, "<td></td>");
1321
1322		/* Print time range */
1323		ret += websWrite(wp, "<td>");
1324		ret += websWrite(wp, "<select name=\"filter_client_from_sec%d\">", i);
1325		for (j = 0; j < ARRAYSIZE(hours); j++)
1326			ret += websWrite(wp, "<option value=\"%d\" %s>%s</option>",
1327					 j * 3600, valid && start.match.secs[0] == (j * 3600) ? "selected" : "", hours[j]);
1328		ret += websWrite(wp, "</select>");
1329		ret += websWrite(wp, "</td>");
1330		ret += websWrite(wp, "<td>-</td>");
1331
1332		ret += websWrite(wp, "<td>");
1333		ret += websWrite(wp, "<select name=\"filter_client_to_sec%d\">", i);
1334		for (j = 0; j < ARRAYSIZE(hours); j++)
1335			ret += websWrite(wp, "<option value=\"%d\" %s>%s</option>",
1336					 j * 3600, valid && start.match.secs[1] == (j * 3600) ? "selected" : "", hours[j]);
1337		/* Special case for 11:59:59 PM */
1338		ret += websWrite(wp, "<option value=\"%d\" %s>12:00 AM</option>",
1339				 24 * 3600 - 1, valid && start.match.secs[1] == (24 * 3600 - 1) ? "selected" : "");
1340		ret += websWrite(wp, "</select>");
1341		ret += websWrite(wp, "</td>");
1342		ret += websWrite(wp, "<td></td>");
1343
1344		/* Print enable */
1345		ret += websWrite(wp, "<td><input type=\"checkbox\" name=\"filter_client_enable%d\" %s></td>",
1346				 i, valid && !(start.match.flags & NETCONF_DISABLED) ? "checked" : "");
1347
1348		ret += websWrite(wp, "</tr>");
1349	}
1350
1351	return ret;
1352}
1353
1354/*
1355 * Example:
1356 * <% forward_port(1, 10); %> produces a table of the first 10 port forward entries
1357 */
1358static int
1359ej_forward_port(int eid, webs_t wp, int argc, char_t **argv)
1360{
1361	int i, n, ret = 0;
1362	netconf_nat_t nat;
1363	bool valid;
1364	char port[] = "XXXXX";
1365
1366	if (ejArgs(argc, argv, "%d %d", &i, &n) < 2) {
1367		websError(wp, 400, "Insufficient args\n");
1368		return -1;
1369	}
1370
1371	for (; i <= n; i++) {
1372		valid = get_forward_port(i, &nat);
1373
1374		ret += websWrite(wp, "<tr>");
1375		ret += websWrite(wp, "<td></td>");
1376
1377		/* Print protocol */
1378		ret += websWrite(wp, "<td>");
1379		ret += websWrite(wp, "<select name=\"forward_port_proto%d\">", i);
1380		ret += websWrite(wp, "<option value=\"tcp\" %s>TCP</option>",
1381				 valid && nat.match.ipproto == IPPROTO_TCP ? "selected" : "");
1382		ret += websWrite(wp, "<option value=\"udp\" %s>UDP</option>",
1383				 valid && nat.match.ipproto == IPPROTO_UDP ? "selected" : "");
1384		ret += websWrite(wp, "</select>");
1385		ret += websWrite(wp, "</td>");
1386		ret += websWrite(wp, "<td></td>");
1387
1388		/* Print WAN destination port range */
1389		if (valid)
1390			snprintf(port, sizeof(port), "%d", ntohs(nat.match.dst.ports[0]));
1391		else
1392			*port = '\0';
1393		ret += websWrite(wp, "<td><input name=\"forward_port_from_start%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1394				 i, port);
1395		ret += websWrite(wp, "<td>-</td>");
1396		if (valid)
1397			snprintf(port, sizeof(port), "%d", ntohs(nat.match.dst.ports[1]));
1398		else
1399			*port = '\0';
1400		ret += websWrite(wp, "<td><input name=\"forward_port_from_end%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1401				 i, port);
1402		ret += websWrite(wp, "<td>></td>");
1403
1404		/* Print address range */
1405		ret += websWrite(wp, "<td><input name=\"forward_port_to_ip%d\" value=\"%s\" size=\"15\" maxlength=\"15\"></td>",
1406				 i, valid ? inet_ntoa(nat.ipaddr) : "");
1407		ret += websWrite(wp, "<td>:</td>");
1408
1409		/* Print LAN destination port range */
1410		if (valid)
1411			snprintf(port, sizeof(port), "%d", ntohs(nat.ports[0]));
1412		else
1413			*port = '\0';
1414		ret += websWrite(wp, "<td><input name=\"forward_port_to_start%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1415				 i, port);
1416		ret += websWrite(wp, "<td>-</td>");
1417		if (valid)
1418			snprintf(port, sizeof(port), "%d", ntohs(nat.ports[1]));
1419		else
1420			*port = '\0';
1421		ret += websWrite(wp, "<td><input name=\"forward_port_to_end%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1422				 i, port);
1423		ret += websWrite(wp, "<td></td>");
1424
1425		/* Print enable */
1426		ret += websWrite(wp, "<td><input type=\"checkbox\" name=\"forward_port_enable%d\" %s></td>",
1427				 i, valid && !(nat.match.flags & NETCONF_DISABLED) ? "checked" : "");
1428
1429		ret += websWrite(wp, "</tr>");
1430	}
1431
1432	return ret;
1433}
1434
1435static int
1436ej_autofw_port(int eid, webs_t wp, int argc, char_t **argv)
1437{
1438	int i, n, ret = 0;
1439	netconf_app_t app;
1440	bool valid;
1441	char port[] = "XXXXX";
1442
1443	if (ejArgs(argc, argv, "%d %d", &i, &n) < 2) {
1444		websError(wp, 400, "Insufficient args\n");
1445		return -1;
1446	}
1447
1448	for (; i <= n; i++) {
1449		valid = get_autofw_port(i, &app);
1450
1451		/* Parse out_proto:out_port,in_proto:in_start-in_end>to_start-to_end,enable,desc */
1452		ret += websWrite(wp, "<tr>");
1453		ret += websWrite(wp, "<td></td>");
1454
1455		/* Print outbound protocol */
1456		ret += websWrite(wp, "<td>");
1457		ret += websWrite(wp, "<select name=\"autofw_port_out_proto%d\">", i);
1458		ret += websWrite(wp, "<option value=\"tcp\" %s>TCP</option>",
1459				 valid && app.match.ipproto == IPPROTO_TCP ? "selected" : "");
1460		ret += websWrite(wp, "<option value=\"udp\" %s>UDP</option>",
1461				 valid && app.match.ipproto == IPPROTO_UDP ? "selected" : "");
1462		ret += websWrite(wp, "</select>");
1463		ret += websWrite(wp, "</td>");
1464		ret += websWrite(wp, "<td></td>");
1465
1466		/* Print outbound port */
1467		if (valid)
1468			snprintf(port, sizeof(port), "%d", ntohs(app.match.dst.ports[0]));
1469		else
1470			*port = '\0';
1471		ret += websWrite(wp, "<td><input name=\"autofw_port_out_start%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1472				 i, port);
1473		ret += websWrite(wp, "<td>-</td>");
1474		if (valid)
1475			snprintf(port, sizeof(port), "%d", ntohs(app.match.dst.ports[1]));
1476		else
1477			*port = '\0';
1478		ret += websWrite(wp, "<td><input name=\"autofw_port_out_end%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1479				 i, port);
1480		ret += websWrite(wp, "<td></td>");
1481
1482		/* Print related protocol */
1483		ret += websWrite(wp, "<td>");
1484		ret += websWrite(wp, "<select name=\"autofw_port_in_proto%d\">", i);
1485		ret += websWrite(wp, "<option value=\"tcp\" %s>TCP</option>",
1486				 valid && app.proto == IPPROTO_TCP ? "selected" : "");
1487		ret += websWrite(wp, "<option value=\"udp\" %s>UDP</option>",
1488				 valid && app.proto == IPPROTO_UDP ? "selected" : "");
1489		ret += websWrite(wp, "</select>");
1490		ret += websWrite(wp, "</td>");
1491		ret += websWrite(wp, "<td></td>");
1492
1493		/* Print related destination port range */
1494		if (valid)
1495			snprintf(port, sizeof(port), "%d", ntohs(app.dport[0]));
1496		else
1497			*port = '\0';
1498		ret += websWrite(wp, "<td><input name=\"autofw_port_in_start%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1499				 i, port);
1500		ret += websWrite(wp, "<td>-</td>");
1501		if (valid)
1502			snprintf(port, sizeof(port), "%d", ntohs(app.dport[1]));
1503		else
1504			*port = '\0';
1505		ret += websWrite(wp, "<td><input name=\"autofw_port_in_end%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1506				 i, port);
1507		ret += websWrite(wp, "<td></td>");
1508
1509		/* Print mapped destination port range */
1510		if (valid)
1511			snprintf(port, sizeof(port), "%d", ntohs(app.to[0]));
1512		else
1513			*port = '\0';
1514		ret += websWrite(wp, "<td><input name=\"autofw_port_to_start%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1515				 i, port);
1516		ret += websWrite(wp, "<td>-</td>");
1517		if (valid)
1518			snprintf(port, sizeof(port), "%d", ntohs(app.to[1]));
1519		else
1520			*port = '\0';
1521		ret += websWrite(wp, "<td><input name=\"autofw_port_to_end%d\" value=\"%s\" size=\"5\" maxlength=\"5\"></td>",
1522				 i, port);
1523		ret += websWrite(wp, "<td></td>");
1524
1525		/* Print enable */
1526		ret += websWrite(wp, "<td><input type=\"checkbox\" name=\"autofw_port_enable%d\" %s></td>",
1527				 i, valid && !(app.match.flags & NETCONF_DISABLED) ? "checked" : "");
1528
1529		ret += websWrite(wp, "</tr>");
1530	}
1531
1532	return ret;
1533}
1534#endif	/* __CONFIG_NAT__ */
1535
1536/*
1537 * Example:
1538 * lan_route=192.168.2.0:255.255.255.0:192.168.2.1:1
1539 * <% lan_route("ipaddr", 0); %> produces "192.168.2.0"
1540 */
1541static int
1542ej_lan_route(int eid, webs_t wp, int argc, char_t **argv)
1543{
1544	char *arg;
1545	int which;
1546	char word[256], *next;
1547	char *ipaddr, *netmask, *gateway, *metric;
1548
1549	if (ejArgs(argc, argv, "%s %d", &arg, &which) < 2) {
1550		websError(wp, 400, "Insufficient args\n");
1551		return -1;
1552	}
1553
1554	foreach(word, nvram_safe_get("lan_route"), next) {
1555		if (which-- == 0) {
1556			netmask = word;
1557			ipaddr = strsep(&netmask, ":");
1558			if (!ipaddr || !netmask)
1559				continue;
1560			gateway = netmask;
1561			netmask = strsep(&gateway, ":");
1562			if (!netmask || !gateway)
1563				continue;
1564			metric = gateway;
1565			gateway = strsep(&metric, ":");
1566			if (!gateway || !metric)
1567				continue;
1568			if (!strcmp(arg, "ipaddr"))
1569				return websWrite(wp, ipaddr);
1570			else if (!strcmp(arg, "netmask"))
1571				return websWrite(wp, netmask);
1572			else if (!strcmp(arg, "gateway"))
1573				return websWrite(wp, gateway);
1574			else if (!strcmp(arg, "metric"))
1575				return websWrite(wp, metric);
1576		}
1577	}
1578
1579	return 0;
1580}
1581
1582#ifdef __CONFIG_NAT__
1583/*
1584 * Example:
1585 * wan_route=192.168.10.0:255.255.255.0:192.168.10.1:1
1586 * <% wan_route("ipaddr", 0); %> produces "192.168.10.0"
1587 */
1588static int
1589ej_wan_route(int eid, webs_t wp, int argc, char_t **argv)
1590{
1591	char *arg;
1592	int which;
1593	char word[256], *next;
1594	char *ipaddr, *netmask, *gateway, *metric;
1595	int unit;
1596	char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
1597
1598
1599	if (ejArgs(argc, argv, "%s %d", &arg, &which) < 2) {
1600		websError(wp, 400, "Insufficient args\n");
1601		return -1;
1602	}
1603
1604	if ((unit = atoi(nvram_safe_get("wan_unit"))) < 0)
1605		unit = 0;
1606	wan_prefix(unit, prefix);
1607
1608	foreach(word, nvram_safe_get(strcat_r(prefix, "route", tmp)), next) {
1609		if (which-- == 0) {
1610			netmask = word;
1611			ipaddr = strsep(&netmask, ":");
1612			if (!ipaddr || !netmask)
1613				continue;
1614			gateway = netmask;
1615			netmask = strsep(&gateway, ":");
1616			if (!netmask || !gateway)
1617				continue;
1618			metric = gateway;
1619			gateway = strsep(&metric, ":");
1620			if (!gateway || !metric)
1621				continue;
1622			if (!strcmp(arg, "ipaddr"))
1623				return websWrite(wp, ipaddr);
1624			else if (!strcmp(arg, "netmask"))
1625				return websWrite(wp, netmask);
1626			else if (!strcmp(arg, "gateway"))
1627				return websWrite(wp, gateway);
1628			else if (!strcmp(arg, "metric"))
1629				return websWrite(wp, metric);
1630		}
1631	}
1632
1633	return 0;
1634}
1635#endif	/* __CONFIG_NAT__ */
1636
1637/* Return a list of the currently present wireless interfaces */
1638static int
1639ej_wl_list(int eid, webs_t wp, int argc, char_t **argv)
1640{
1641	char name[IFNAMSIZ], *next;
1642	int unit, ret = 0;
1643	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1644	char *hwaddr, *ssid;
1645	char ifnames[256];
1646
1647	snprintf(ifnames, sizeof(ifnames), "%s %s",
1648		nvram_safe_get("lan_ifnames"),
1649		nvram_safe_get("wan_ifnames"));
1650	foreach(name, ifnames, next) {
1651		/* Probe for wl interfaces */
1652		if (wl_probe(name) ||
1653		    wl_ioctl(name, WLC_GET_INSTANCE, &unit, sizeof(unit)))
1654			continue;
1655
1656		/* Get configured SSID */
1657		snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1658		hwaddr = nvram_get(strcat_r(prefix, "hwaddr", tmp));
1659		ssid = nvram_get(strcat_r(prefix, "ssid", tmp));
1660		if (!hwaddr || !*hwaddr || !ssid || !*ssid)
1661			continue;
1662
1663		ret += websWrite(wp, "<option value=\"%d\" %s>%s (%s)</option>", unit,
1664				 unit == atoi(nvram_safe_get("wl_unit")) ? "selected" : "",
1665				 ssid, hwaddr);
1666	}
1667
1668	if (!ret)
1669		ret += websWrite(wp, "<option value=\"-1\" selected>None</option>");
1670
1671	return ret;
1672}
1673
1674/* Return a list of the supported bands on the currently selected wireless interface */
1675static int
1676ej_wl_phytypes(int eid, webs_t wp, int argc, char_t **argv)
1677{
1678	int unit, ret = 0;
1679	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1680	char *phytype;
1681	char *phylist;
1682	int i;
1683
1684	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
1685		return websWrite(wp, "None");
1686
1687	/* Get available phy types of the currently selected wireless interface */
1688	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1689	phylist = nvram_safe_get(strcat_r(prefix, "phytypes", tmp));
1690
1691	/* Get configured phy type */
1692	phytype = nvram_safe_get("wl_phytype");
1693
1694	for (i = 0; i < strlen(phylist); i++) {
1695		ret += websWrite(wp, "<option value=\"%c\" %s>802.11%c (%s GHz)</option>",
1696				 phylist[i], phylist[i] == *phytype ? "selected" : "", phylist[i],
1697				 phylist[i] == 'a' ? "5" : "2.4");
1698	}
1699
1700	return ret;
1701}
1702
1703/* Return a radio ID given a phy type */
1704static int
1705ej_wl_radioid(int eid, webs_t wp, int argc, char_t **argv)
1706{
1707	char *phytype, var[NVRAM_BUFSIZE], *next;
1708	int unit;
1709	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1710	int which;
1711
1712	if (ejArgs(argc, argv, "%s", &phytype) < 1) {
1713		websError(wp, 400, "Insufficient args\n");
1714		return -1;
1715	}
1716
1717	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
1718		return websWrite(wp, "None");
1719	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1720
1721	which = strcspn(nvram_safe_get(strcat_r(prefix, "phytypes", tmp)), phytype);
1722	foreach(var, nvram_safe_get(strcat_r(prefix, "radioids", tmp)), next) {
1723		if (which == 0)
1724			return websWrite(wp, var);
1725		which--;
1726	}
1727
1728	return websWrite(wp, "None");
1729}
1730
1731/* Return current core revision */
1732static int
1733ej_wl_corerev(int eid, webs_t wp, int argc, char_t **argv)
1734{
1735	int unit;
1736	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1737
1738	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
1739		return websWrite(wp, "None");
1740	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1741
1742	return websWrite(wp, nvram_safe_get(strcat_r(prefix, "corerev", tmp)));
1743}
1744
1745/* Return current wireless channel */
1746static int
1747ej_wl_cur_channel(int eid, webs_t wp, int argc, char_t **argv)
1748{
1749	int unit;
1750	char *name;
1751	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1752	channel_info_t ci;
1753
1754	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
1755		return -1;
1756
1757	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1758	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
1759
1760	wl_ioctl(name, WLC_GET_CHANNEL, &ci, sizeof(ci));
1761	return websWrite(wp, "Current: %d", ci.target_channel);
1762}
1763
1764/* Return current country */
1765static int
1766ej_wl_cur_country(int eid, webs_t wp, int argc, char_t **argv)
1767{
1768	int unit;
1769	char *name;
1770	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1771	char buf[WLC_CNTRY_BUF_SZ];
1772
1773	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
1774		return -1;
1775
1776	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1777	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
1778
1779	wl_ioctl(name, WLC_GET_COUNTRY, buf, sizeof(buf));
1780	return websWrite(wp, "%s", buf);
1781}
1782
1783/* hardware-defined phy types (from d11.h) */
1784#define	PHY_TYPE_A		0
1785#define	PHY_TYPE_B		1
1786#define	PHY_TYPE_G		2
1787#define	PHY_TYPE_NULL		0xf
1788
1789/* Return current phytype */
1790static int
1791ej_wl_cur_phytype(int eid, webs_t wp, int argc, char_t **argv)
1792{
1793	int unit;
1794	char *name;
1795	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
1796	int phytype;
1797
1798	if ((unit = atoi(nvram_safe_get("wl_unit"))) < 0)
1799		return -1;
1800
1801	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1802	name = nvram_safe_get(strcat_r(prefix, "ifname", tmp));
1803
1804	/* Get configured phy type */
1805	wl_ioctl(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
1806
1807	return websWrite(wp, "Current: 802.11%s", phytype == PHY_TYPE_A ? "a" :
1808		phytype == PHY_TYPE_B ? "b" : "g");
1809}
1810
1811#ifdef __CONFIG_NAT__
1812static char *
1813wan_name(int unit, char *prefix, char *name, int len)
1814{
1815	char tmp[NVRAM_BUFSIZE], *desc;
1816	desc = nvram_safe_get(strcat_r(prefix, "desc", tmp));
1817	snprintf(tmp, sizeof(tmp), "Connection %d", unit + 1);
1818	snprintf(name, len, "%s", !strcmp(desc, "") ? tmp : desc);
1819	return name;
1820}
1821
1822/* Return a list of wan connections (Connection <N>/<Connection Name>) */
1823static int
1824ej_wan_list(int eid, webs_t wp, int argc, char_t **argv)
1825{
1826	char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
1827	int unit, ret = 0;
1828
1829	/* build wan connection name list */
1830	for (unit = 0; unit < MAX_NVPARSE; unit ++) {
1831		wan_prefix(unit, prefix);
1832		if (!nvram_get(strcat_r(prefix, "unit", tmp)))
1833			continue;
1834		ret += websWrite(wp, "<option value=\"%d\" %s>%s</option>", unit,
1835				unit == atoi(nvram_safe_get("wan_unit")) ? "selected" : "",
1836				wan_name(unit, prefix, tmp, sizeof(tmp)));
1837	}
1838
1839	return ret;
1840}
1841#endif	/* __CONFIG_NAT__ */
1842
1843char *webs_buf=NULL;
1844int webs_buf_offset=0;
1845
1846static void
1847validate_list(webs_t wp, char *value, struct variable *v,
1848	      int (*valid)(webs_t, char *, struct variable *))
1849{
1850	int n, i;
1851	char name[100];
1852	char buf[1000] = "", *cur = buf;
1853
1854	n = atoi(value);
1855
1856	for (i = 0; i < n; i++) {
1857		snprintf(name, sizeof(name), "%s%d", v->name, i);
1858		if (!(value = websGetVar(wp, name, NULL)))
1859			return;
1860		if (!*value && v->nullok)
1861			continue;
1862		if (!valid(wp, value, v))
1863			continue;
1864		cur += snprintf(cur, buf + sizeof(buf) - cur, "%s%s",
1865				cur == buf ? "" : " ", value);
1866	}
1867
1868	nvram_set(v->name, buf);
1869}
1870
1871static int
1872valid_ipaddr(webs_t wp, char *value, struct variable *v)
1873{
1874	unsigned int buf[4];
1875	struct in_addr ipaddr, netaddr, broadaddr, netmask;
1876
1877	if (sscanf(value, "%d.%d.%d.%d", &buf[0], &buf[1], &buf[2], &buf[3]) != 4) {
1878		websBufferWrite(wp, "Invalid <b>%s</b> %s: not an IP address<br>",
1879			  v->longname, value);
1880		return FALSE;
1881	}
1882
1883	ipaddr.s_addr = htonl((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
1884
1885	if (v->argv) {
1886		(void) inet_aton(nvram_safe_get(v->argv[0]), &netaddr);
1887		(void) inet_aton(nvram_safe_get(v->argv[1]), &netmask);
1888		netaddr.s_addr &= netmask.s_addr;
1889		broadaddr.s_addr = netaddr.s_addr | ~netmask.s_addr;
1890		if (netaddr.s_addr != (ipaddr.s_addr & netmask.s_addr)) {
1891			websBufferWrite(wp, "Invalid <b>%s</b> %s: not in the %s/",
1892				  v->longname, value, inet_ntoa(netaddr));
1893			websBufferWrite(wp, "%s network<br>", inet_ntoa(netmask));
1894			return FALSE;
1895		}
1896		if (ipaddr.s_addr == netaddr.s_addr) {
1897			websBufferWrite(wp, "Invalid <b>%s</b> %s: cannot be the network address<br>",
1898				  v->longname, value);
1899			return FALSE;
1900		}
1901		if (ipaddr.s_addr == broadaddr.s_addr) {
1902			websBufferWrite(wp, "Invalid <b>%s</b> %s: cannot be the broadcast address<br>",
1903				  v->longname, value);
1904			return FALSE;
1905		}
1906	}
1907
1908	return TRUE;
1909}
1910
1911static void
1912validate_ipaddr(webs_t wp, char *value, struct variable *v)
1913{
1914	if (valid_ipaddr(wp, value, v))
1915		nvram_set(v->name, value);
1916}
1917
1918static void
1919validate_ipaddrs(webs_t wp, char *value, struct variable *v)
1920{
1921	validate_list(wp, value, v, valid_ipaddr);
1922}
1923
1924static int
1925valid_choice(webs_t wp, char *value, struct variable *v)
1926{
1927	char **choice;
1928
1929	for (choice = v->argv; *choice; choice++) {
1930		if (!strcmp(value, *choice))
1931			return TRUE;
1932	}
1933
1934	websBufferWrite(wp, "Invalid <b>%s</b> %s: not one of ", v->longname, value);
1935	for (choice = v->argv; *choice; choice++)
1936		websBufferWrite(wp, "%s%s", choice == v->argv ? "" : "/", *choice);
1937	websBufferWrite(wp, "<br>");
1938	return FALSE;
1939}
1940
1941static void
1942validate_choice(webs_t wp, char *value, struct variable *v)
1943{
1944	if (valid_choice(wp, value, v))
1945		nvram_set(v->name, value);
1946}
1947
1948static int
1949valid_range(webs_t wp, char *value, struct variable *v)
1950{
1951	int n, start, end;
1952
1953	n = atoi(value);
1954	start = atoi(v->argv[0]);
1955	end = atoi(v->argv[1]);
1956
1957	if (n < start || n > end) {
1958		websBufferWrite(wp, "Invalid <b>%s</b> %s: out of range %d-%d<br>",
1959			  v->longname, value, start, end);
1960		return FALSE;
1961	}
1962
1963	return TRUE;
1964}
1965
1966static void
1967validate_range(webs_t wp, char *value, struct variable *v)
1968{
1969	if (valid_range(wp, value, v))
1970		nvram_set(v->name, value);
1971}
1972
1973static int
1974valid_name(webs_t wp, char *value, struct variable *v)
1975{
1976	int n, min, max;
1977
1978	n = strlen(value);
1979	min = atoi(v->argv[0]);
1980	max = atoi(v->argv[1]);
1981
1982	if (n > max) {
1983		websBufferWrite(wp, "Invalid <b>%s</b> %s: longer than %d characters<br>",
1984			  v->longname, value, max);
1985		return FALSE;
1986	}
1987	else if (n < min) {
1988		websBufferWrite(wp, "Invalid <b>%s</b> %s: shorter than %d characters<br>",
1989			  v->longname, value, min);
1990		return FALSE;
1991	}
1992
1993	return TRUE;
1994}
1995
1996static void
1997validate_name(webs_t wp, char *value, struct variable *v)
1998{
1999	if (valid_name(wp, value, v))
2000		nvram_set(v->name, value);
2001}
2002
2003static int
2004valid_hwaddr(webs_t wp, char *value, struct variable *v)
2005{
2006	unsigned char hwaddr[6];
2007
2008	/* Make exception for "NOT IMPLELEMENTED" string */
2009	if (!strcmp(value,"NOT_IMPLEMENTED"))
2010		return(TRUE);
2011
2012	/* Check for bad, multicast, broadcast, or null address */
2013	if (!ether_atoe(value, hwaddr) ||
2014	    (hwaddr[0] & 1) ||
2015	    (hwaddr[0] & hwaddr[1] & hwaddr[2] & hwaddr[3] & hwaddr[4] & hwaddr[5]) == 0xff ||
2016	    (hwaddr[0] | hwaddr[1] | hwaddr[2] | hwaddr[3] | hwaddr[4] | hwaddr[5]) == 0x00) {
2017		websBufferWrite(wp, "Invalid <b>%s</b> %s: not a MAC address<br>",
2018			  v->longname, value);
2019		return FALSE;
2020	}
2021
2022	return TRUE;
2023}
2024
2025#ifdef __CONFIG_NAT__
2026static void
2027validate_hwaddr(webs_t wp, char *value, struct variable *v)
2028{
2029	if (valid_hwaddr(wp, value, v))
2030		nvram_set(v->name, value);
2031}
2032#endif	/* __CONFIG_NAT__ */
2033
2034static void
2035validate_hwaddrs(webs_t wp, char *value, struct variable *v)
2036{
2037	validate_list(wp, value, v, valid_hwaddr);
2038}
2039
2040static void
2041validate_country(webs_t wp, char *value, struct variable *v)
2042{
2043	country_name_t *country;
2044	for(country = country_names; country->name; country++)
2045		if (!strcmp(value, country->abbrev))
2046			nvram_set(v->name, value);
2047}
2048
2049static void
2050validate_dhcp(webs_t wp, char *value, struct variable *v)
2051{
2052	struct variable dhcp_variables[] = {
2053		{ longname: "DHCP Server Starting LAN IP Address", argv: ARGV("lan_ipaddr", "lan_netmask") },
2054		{ longname: "DHCP Server Ending LAN IP Address", argv: ARGV("lan_ipaddr", "lan_netmask") },
2055	};
2056	char *start, *end;
2057
2058	if (!(start = websGetVar(wp, "dhcp_start", NULL)) ||
2059	    !(end = websGetVar(wp, "dhcp_end", NULL)))
2060		return;
2061	if (!*start) start = end;
2062	if (!*end) end = start;
2063	if (!*start && !*end && !strcmp(nvram_safe_get("lan_proto"), "dhcp")) {
2064		websBufferWrite(wp, "Invalid <b>%s</b>: must specify a range<br>", v->longname);
2065		return;
2066	}
2067	if (!valid_ipaddr(wp, start, &dhcp_variables[0]) ||
2068	    !valid_ipaddr(wp, end, &dhcp_variables[1]))
2069		return;
2070	if (ntohl(inet_addr(start)) > ntohl(inet_addr(end))) {
2071		websBufferWrite(wp, "Invalid <b>%s</b> %s: greater than <b>%s</b> %s<br>",
2072			  dhcp_variables[0].longname, start, dhcp_variables[1].longname, end);
2073		return;
2074	}
2075
2076	nvram_set("dhcp_start", start);
2077	nvram_set("dhcp_end", end);
2078}
2079
2080static void
2081validate_lan_ipaddr(webs_t wp, char *value, struct variable *v)
2082{
2083	struct variable fields[] = {
2084		{ name: "lan_ipaddr", longname: "LAN IP Address" },
2085		{ name: "lan_netmask", longname: "LAN Subnet Mask" },
2086	};
2087	char *lan_ipaddr, *lan_netmask;
2088	struct in_addr ipaddr, netmask, netaddr, broadaddr;
2089	char *lan_ipaddrs[] = { "dhcp_start", "dhcp_end", "dmz_ipaddr" };
2090#ifdef __CONFIG_NAT__
2091	netconf_filter_t start, end;
2092	netconf_nat_t nat;
2093	bool valid;
2094#endif	/* __CONFIG_NAT__ */
2095	int i;
2096
2097	/* Basic validation */
2098	if (!(lan_ipaddr = websGetVar(wp, fields[0].name, NULL)) ||
2099	    !(lan_netmask = websGetVar(wp, fields[1].name, NULL)) ||
2100	    !valid_ipaddr(wp, lan_ipaddr, &fields[0]) ||
2101	    !valid_ipaddr(wp, lan_netmask, &fields[1]))
2102		return;
2103
2104	/* Check for broadcast or network address */
2105	(void) inet_aton(lan_ipaddr, &ipaddr);
2106	(void) inet_aton(lan_netmask, &netmask);
2107	netaddr.s_addr = ipaddr.s_addr & netmask.s_addr;
2108	broadaddr.s_addr = netaddr.s_addr | ~netmask.s_addr;
2109	if (ipaddr.s_addr == netaddr.s_addr) {
2110		websBufferWrite(wp, "Invalid <b>%s</b> %s: cannot be the network address<br>",
2111			  fields[0].longname, lan_ipaddr);
2112		return;
2113	}
2114	if (ipaddr.s_addr == broadaddr.s_addr) {
2115		websBufferWrite(wp, "Invalid <b>%s</b> %s: cannot be the broadcast address<br>",
2116			  fields[0].longname, lan_ipaddr);
2117		return;
2118	}
2119
2120	nvram_set("lan_ipaddr", lan_ipaddr);
2121	nvram_set("lan_netmask", lan_netmask);
2122
2123	/* Fix up LAN IP addresses */
2124	for (i = 0; i < ARRAYSIZE(lan_ipaddrs); i++) {
2125		value = nvram_get(lan_ipaddrs[i]);
2126		if (value && *value) {
2127			(void) inet_aton(value, &ipaddr);
2128			ipaddr.s_addr &= ~netmask.s_addr;
2129			ipaddr.s_addr |= netaddr.s_addr;
2130			nvram_set(lan_ipaddrs[i], inet_ntoa(ipaddr));
2131		}
2132	}
2133
2134#ifdef __CONFIG_NAT__
2135	/* Fix up client filters and port forwards */
2136	for (i = 0; i < MAX_NVPARSE; i++) {
2137		if (get_filter_client(i, &start, &end)) {
2138			start.match.src.ipaddr.s_addr &= ~netmask.s_addr;
2139			start.match.src.ipaddr.s_addr |= netaddr.s_addr;
2140			end.match.src.ipaddr.s_addr &= ~netmask.s_addr;
2141			end.match.src.ipaddr.s_addr |= netaddr.s_addr;
2142			valid = set_filter_client(i, &start, &end);
2143			a_assert(valid);
2144		}
2145		if (get_forward_port(i, &nat)) {
2146			nat.ipaddr.s_addr &= ~netmask.s_addr;
2147			nat.ipaddr.s_addr |= netaddr.s_addr;
2148			valid = set_forward_port(i, &nat);
2149			a_assert(valid);
2150		}
2151	}
2152#endif	/* __CONFIG_NAT__ */
2153}
2154
2155#ifdef __CONFIG_NAT__
2156static void
2157validate_filter_client(webs_t wp, char *value, struct variable *v)
2158{
2159	int n, i, j;
2160	bool valid;
2161	struct variable fields[] = {
2162		{ name: "filter_client_from_start%d", longname: "LAN Client Filter Starting IP Address", argv: ARGV("lan_ipaddr", "lan_netmask") },
2163		{ name: "filter_client_from_end%d", longname: "LAN Client Filter Ending IP Address", argv: ARGV("lan_ipaddr", "lan_netmask") },
2164		{ name: "filter_client_proto%d", longname: "LAN Client Filter Protocol", argv: ARGV("tcp", "udp") },
2165		{ name: "filter_client_to_start%d", longname: "LAN Client Filter Starting Destination Port", argv: ARGV("0", "65535") },
2166		{ name: "filter_client_to_end%d", longname: "LAN Client Filter Ending Destination Port", argv: ARGV("0", "65535") },
2167		{ name: "filter_client_from_day%d", longname: "LAN Client Filter Starting Day", argv: ARGV("0", "6") },
2168		{ name: "filter_client_to_day%d", longname: "LAN Client Filter Ending Day", argv: ARGV("0", "6") },
2169		{ name: "filter_client_from_sec%d", longname: "LAN Client Filter Starting Second", argv: ARGV("0", "86400") },
2170		{ name: "filter_client_to_sec%d", longname: "LAN Client Filter Ending Second", argv: ARGV("0", "86400") },
2171	};
2172	char *from_start, *from_end, *proto, *to_start, *to_end, *from_day, *to_day, *from_sec, *to_sec, *enable;
2173	char **locals[] = { &from_start, &from_end, &proto, &to_start, &to_end, &from_day, &to_day, &from_sec, &to_sec };
2174	char name[1000];
2175	netconf_filter_t start, end;
2176
2177	/* filter_client indicates how many to expect */
2178	if (!valid_range(wp, value, v))
2179		return;
2180	n = atoi(value);
2181
2182	for (i = 0; i <= n; i++) {
2183		/* Set up field names */
2184		for (j = 0; j < ARRAYSIZE(fields); j++) {
2185			snprintf(name, sizeof(name), fields[j].name, i);
2186			if (!(*locals[j] = websGetVar(wp, name, NULL)))
2187				break;
2188		}
2189		/* Incomplete web page */
2190		if (j < ARRAYSIZE(fields))
2191			continue;
2192		/* Enable is a checkbox */
2193		snprintf(name, sizeof(name), "filter_client_enable%d", i);
2194		if (websGetVar(wp, name, NULL))
2195			enable = "on";
2196		else
2197			enable = "off";
2198		/* Delete entry if all fields are blank */
2199		if (!*from_start && !*from_end && !*to_start && !*to_end) {
2200			del_filter_client(i);
2201			continue;
2202		}
2203		/* Fill in empty fields with default values */
2204		if (!*from_start) from_start = from_end;
2205		if (!*from_end) from_end = from_start;
2206		if (!*to_start) to_start = to_end;
2207		if (!*to_end) to_end = to_start;
2208		if (!*from_start || !*from_end) {
2209			websBufferWrite(wp, "Invalid <b>%s</b>: must specify a LAN IP Address Range<br>", v->longname);
2210			continue;
2211		}
2212		if (!*to_start || !*to_end) {
2213			websBufferWrite(wp, "Invalid <b>%s</b>: must specify a Destination Port Range<br>", v->longname);
2214			continue;
2215		}
2216		/* Check individual fields */
2217		if (!valid_ipaddr(wp, from_start, &fields[0]) ||
2218		    !valid_ipaddr(wp, from_end, &fields[1]) ||
2219		    !valid_choice(wp, proto, &fields[2]) ||
2220		    !valid_range(wp, to_start, &fields[3]) ||
2221		    !valid_range(wp, to_end, &fields[4]) ||
2222		    !valid_range(wp, from_day, &fields[5]) ||
2223		    !valid_range(wp, to_day, &fields[6]) ||
2224		    !valid_range(wp, from_sec, &fields[7]) ||
2225		    !valid_range(wp, to_sec, &fields[8]))
2226			continue;
2227		/* Check dependencies between fields */
2228		if (ntohl(inet_addr(from_start)) > ntohl(inet_addr(from_end))) {
2229			websBufferWrite(wp, "Invalid <b>%s</b> %s: greater than <b>%s</b> %s<br>",
2230				  fields[0].longname, from_start, fields[1].longname, from_end);
2231			continue;
2232		}
2233		if (atoi(to_start) > atoi(to_end)) {
2234			websBufferWrite(wp, "Invalid <b>%s</b> %s: greater than <b>%s</b> %s<br>",
2235				  fields[3].longname, to_start, fields[4].longname, to_end);
2236			continue;
2237		}
2238
2239		/* Set up parameters */
2240		memset(&start, 0, sizeof(netconf_filter_t));
2241		if (!strcmp(proto, "tcp"))
2242			start.match.ipproto = IPPROTO_TCP;
2243		else if (!strcmp(proto, "udp"))
2244			start.match.ipproto = IPPROTO_UDP;
2245		(void) inet_aton(from_start, &start.match.src.ipaddr);
2246		start.match.src.netmask.s_addr = htonl(0xffffffff);
2247		start.match.dst.ports[0] = htons(atoi(to_start));
2248		start.match.dst.ports[1] = htons(atoi(to_end));
2249		start.match.days[0] = atoi(from_day);
2250		start.match.days[1] = atoi(to_day);
2251		start.match.secs[0] = atoi(from_sec);
2252		start.match.secs[1] = atoi(to_sec);
2253		if (!strcmp(enable, "off"))
2254			start.match.flags |= NETCONF_DISABLED;
2255		memcpy(&end, &start, sizeof(netconf_filter_t));
2256		(void) inet_aton(from_end, &end.match.src.ipaddr);
2257
2258		/* Do it */
2259		valid = set_filter_client(i, &start, &end);
2260		a_assert(valid);
2261	}
2262}
2263
2264static void
2265validate_forward_port(webs_t wp, char *value, struct variable *v)
2266{
2267	int n, i, j;
2268	bool valid;
2269	struct variable fields[] = {
2270		{ name: "forward_port_proto%d", longname: "Port Forward Protocol", argv: ARGV("tcp", "udp") },
2271		{ name: "forward_port_from_start%d", longname: "Port Forward Starting WAN Port", argv: ARGV("0", "65535") },
2272		{ name: "forward_port_from_end%d", longname: "Port Forward Ending WAN Port", argv: ARGV("0", "65535") },
2273		{ name: "forward_port_to_ip%d", longname: "Port Forward LAN IP Address", argv: ARGV("lan_ipaddr", "lan_netmask") },
2274		{ name: "forward_port_to_start%d", longname: "Port Forward Starting LAN Port", argv: ARGV("0", "65535") },
2275		{ name: "forward_port_to_end%d", longname: "Port Forward Ending LAN Port", argv: ARGV("0", "65535") },
2276	};
2277	char *proto, *from_start, *from_end, *to_ip, *to_start, *to_end, *enable;
2278	char **locals[] = { &proto, &from_start, &from_end, &to_ip, &to_start, &to_end };
2279	char name[1000];
2280	netconf_nat_t nat;
2281
2282	/* forward_port indicates how many to expect */
2283	if (!valid_range(wp, value, v))
2284		return;
2285	n = atoi(value);
2286
2287	for (i = 0; i <= n; i++) {
2288		/* Set up field names */
2289		for (j = 0; j < ARRAYSIZE(fields); j++) {
2290			snprintf(name, sizeof(name), fields[j].name, i);
2291			if (!(*locals[j] = websGetVar(wp, name, NULL)))
2292				break;
2293		}
2294		/* Incomplete web page */
2295		if (j < ARRAYSIZE(fields))
2296			continue;
2297		/* Enable is a checkbox */
2298		snprintf(name, sizeof(name), "forward_port_enable%d", i);
2299		if (websGetVar(wp, name, NULL))
2300			enable = "on";
2301		else
2302			enable = "off";
2303		/* Delete entry if all fields are blank */
2304		if (!*from_start && !*from_end && !*to_ip && !*to_start && !*to_end) {
2305			del_forward_port(i);
2306			continue;
2307		}
2308		/* Fill in empty fields with default values */
2309		if (!*from_start) from_start = from_end;
2310		if (!*from_end) from_end = from_start;
2311		if (!*to_start && !*to_end)
2312			to_start = from_start;
2313		if (!*to_start) to_start = to_end;
2314		if (!*to_end) to_end = to_start;
2315		if (!*from_start || !*from_end) {
2316			websBufferWrite(wp, "Invalid <b>%s</b>: must specify a LAN IP Address Range<br>", v->longname);
2317			continue;
2318		}
2319		if (!*to_ip) {
2320			websBufferWrite(wp, "Invalid <b>%s</b>: must specify a LAN IP Address<br>", v->longname);
2321			continue;
2322		}
2323		if (!*to_start || !*to_end) {
2324			websBufferWrite(wp, "Invalid <b>%s</b>: must specify a Destination Port Range<br>", v->longname);
2325			continue;
2326		}
2327		/* Check individual fields */
2328		if (!valid_choice(wp, proto, &fields[0]) ||
2329		    !valid_range(wp, from_start, &fields[1]) ||
2330		    !valid_range(wp, from_end, &fields[2]) ||
2331		    !valid_ipaddr(wp, to_ip, &fields[3]) ||
2332		    !valid_range(wp, to_start, &fields[4]) ||
2333		    !valid_range(wp, to_end, &fields[5]))
2334			continue;
2335		if (atoi(from_start) > atoi(from_end)) {
2336			websBufferWrite(wp, "Invalid <b>%s</b> %s: greater than <b>%s</b> %s<br>",
2337				  fields[1].longname, from_start, fields[2].longname, from_end);
2338			continue;
2339		}
2340		if (atoi(to_start) > atoi(to_end)) {
2341			websBufferWrite(wp, "Invalid <b>%s</b> %s: greater than <b>%s</b> %s<br>",
2342				  fields[4].longname, to_start, fields[5].longname, to_end);
2343			continue;
2344		}
2345		if ((atoi(from_end) - atoi(from_start)) != (atoi(to_end) - atoi(to_start))) {
2346			websBufferWrite(wp, "Invalid <b>%s</b>: WAN Port Range and LAN Port Range must be the same size<br>", v->longname);
2347			continue;
2348		}
2349
2350		/* Set up parameters */
2351		memset(&nat, 0, sizeof(netconf_nat_t));
2352		if (!strcmp(proto, "tcp"))
2353			nat.match.ipproto = IPPROTO_TCP;
2354		else if (!strcmp(proto, "udp"))
2355			nat.match.ipproto = IPPROTO_UDP;
2356		nat.match.dst.ports[0] = htons(atoi(from_start));
2357		nat.match.dst.ports[1] = htons(atoi(from_end));
2358		(void) inet_aton(to_ip, &nat.ipaddr);
2359		nat.ports[0] = htons(atoi(to_start));
2360		nat.ports[1] = htons(atoi(to_end));
2361		if (!strcmp(enable, "off"))
2362			nat.match.flags |= NETCONF_DISABLED;
2363
2364		/* Do it */
2365		valid = set_forward_port(i, &nat);
2366		a_assert(valid);
2367	}
2368}
2369
2370static void
2371validate_autofw_port(webs_t wp, char *value, struct variable *v)
2372{
2373	int n, i, j;
2374	bool valid;
2375	struct variable fields[] = {
2376		{ name: "autofw_port_out_proto%d", longname: "Outbound Protocol", argv: ARGV("tcp", "udp") },
2377		{ name: "autofw_port_out_start%d", longname: "Outbound Port Start", argv: ARGV("0", "65535") },
2378		{ name: "autofw_port_out_end%d", longname: "Outbound Port End", argv: ARGV("0", "65535") },
2379		{ name: "autofw_port_in_proto%d", longname: "Inbound Protocol", argv: ARGV("tcp", "udp") },
2380		{ name: "autofw_port_in_start%d", longname: "Inbound Port Start", argv: ARGV("0", "65535") },
2381		{ name: "autofw_port_in_end%d", longname: "Inbound Port End", argv: ARGV("0", "65535") },
2382 		{ name: "autofw_port_to_start%d", longname: "To Port Start", argv: ARGV("0", "65535") },
2383 		{ name: "autofw_port_to_end%d", longname: "To Port End", argv: ARGV("0", "65535") },
2384	};
2385	char *out_proto, *out_start, *out_end, *in_proto, *in_start, *in_end, *to_start, *to_end, *enable;
2386	char **locals[] = { &out_proto, &out_start, &out_end, &in_proto, &in_start, &in_end, &to_start, &to_end };
2387	char name[1000];
2388	netconf_app_t app;
2389
2390	/* autofw_port indicates how many to expect */
2391	if (!valid_range(wp, value, v))
2392		return;
2393	n = atoi(value);
2394
2395	for (i = 0; i <= n; i++) {
2396		/* Set up field names */
2397		for (j = 0; j < ARRAYSIZE(fields); j++) {
2398			snprintf(name, sizeof(name), fields[j].name, i);
2399			if (!(*locals[j] = websGetVar(wp, name, NULL)))
2400				break;
2401		}
2402		/* Incomplete web page */
2403		if (j < ARRAYSIZE(fields))
2404			continue;
2405		/* Enable is a checkbox */
2406		snprintf(name, sizeof(name), "autofw_port_enable%d", i);
2407		if (websGetVar(wp, name, NULL))
2408			enable = "on";
2409		else
2410			enable = "off";
2411		/* Delete entry if all fields are blank */
2412		if (!*out_start && !*out_end && !*in_start && !*in_end && !*to_start && !*to_end) {
2413			del_autofw_port(i);
2414			continue;
2415		}
2416		/* Fill in empty fields with default values */
2417		if (!*out_start) out_start = out_end;
2418		if (!*out_end) out_end = out_start;
2419		if (!*in_start) in_start = in_end;
2420		if (!*in_end) in_end = in_start;
2421		if (!*to_start && !*to_end)
2422			to_start = in_start;
2423		if (!*to_start) to_start = to_end;
2424		if (!*to_end) to_end = to_start;
2425		if (!*out_start || !*out_end) {
2426			websBufferWrite(wp, "Invalid <b>%s</b>: must specify an Outbound Port Range<br>", v->longname);
2427			continue;
2428		}
2429		if (!*in_start || !*in_end) {
2430			websBufferWrite(wp, "Invalid <b>%s</b>: must specify an Inbound Port Range<br>", v->longname);
2431			continue;
2432		}
2433		if (!*to_start || !*to_end) {
2434			websBufferWrite(wp, "Invalid <b>%s</b>: must specify a To Port Range<br>", v->longname);
2435			continue;
2436		}
2437		/* Check individual fields */
2438		if (!valid_choice(wp, out_proto, &fields[0]) ||
2439		    !valid_range(wp, out_start, &fields[1]) ||
2440		    !valid_range(wp, out_end, &fields[2]) ||
2441		    !valid_choice(wp, in_proto, &fields[3]) ||
2442		    !valid_range(wp, in_start, &fields[4]) ||
2443		    !valid_range(wp, in_end, &fields[5]) ||
2444		    !valid_range(wp, to_start, &fields[6]) ||
2445		    !valid_range(wp, to_end, &fields[7]))
2446			continue;
2447		/* Check dependencies between fields */
2448		if (atoi(out_start) > atoi(out_end)) {
2449			websBufferWrite(wp, "Invalid <b>%s</b> %s: greater than <b>%s</b> %s<br>",
2450				  fields[1].longname, out_start, fields[2].longname, out_end);
2451			continue;
2452		}
2453		if (atoi(in_start) > atoi(in_end)) {
2454			websBufferWrite(wp, "Invalid <b>%s</b> %s: greater than <b>%s</b> %s<br>",
2455				  fields[4].longname, in_start, fields[5].longname, in_end);
2456			continue;
2457		}
2458		if (atoi(to_start) > atoi(to_end)) {
2459			websBufferWrite(wp, "Invalid <b>%s</b> %s: greater than <b>%s</b> %s<br>",
2460				  fields[6].longname, in_start, fields[7].longname, in_end);
2461			continue;
2462		}
2463		if ((atoi(in_end) - atoi(in_start)) != (atoi(to_end) - atoi(to_start))) {
2464			websBufferWrite(wp, "Invalid <b>%s</b>: Inbound Port Range and To Port Range must be the same size<br>", v->longname);
2465			continue;
2466		}
2467#ifdef NEW_PORT_TRIG
2468		if ((atoi(in_end) - atoi(in_start)) > 100) {
2469			websBufferWrite(wp, "Invalid <b>%s</b>: Inbound Port Range must be less than 100<br>", v->longname);
2470			continue;
2471		}
2472#endif
2473
2474		/* Set up parameters */
2475		memset(&app, 0, sizeof(netconf_app_t));
2476		if (!strcmp(out_proto, "tcp"))
2477			app.match.ipproto = IPPROTO_TCP;
2478		else if (!strcmp(out_proto, "udp"))
2479			app.match.ipproto = IPPROTO_UDP;
2480		app.match.dst.ports[0] = htons(atoi(out_start));
2481		app.match.dst.ports[1] = htons(atoi(out_end));
2482		if (!strcmp(in_proto, "tcp"))
2483			app.proto = IPPROTO_TCP;
2484		else if (!strcmp(in_proto, "udp"))
2485			app.proto = IPPROTO_UDP;
2486		app.dport[0] = htons(atoi(in_start));
2487		app.dport[1] = htons(atoi(in_end));
2488		app.to[0] = htons(atoi(to_start));
2489		app.to[1] = htons(atoi(to_end));
2490		if (!strcmp(enable, "off"))
2491			app.match.flags |= NETCONF_DISABLED;
2492
2493		/* Do it */
2494		valid = set_autofw_port(i, &app);
2495		a_assert(valid);
2496	}
2497}
2498#endif	/* __CONFIG_NAT__ */
2499
2500static void
2501validate_lan_route(webs_t wp, char *value, struct variable *v)
2502{
2503	int n, i;
2504	char buf[1000] = "", *cur = buf;
2505	struct variable lan_route_variables[] = {
2506		{ longname: "Route IP Address", argv: NULL },
2507		{ longname: "Route Subnet Mask", argv: NULL },
2508		{ longname: "Route Gateway", argv: NULL },
2509		{ longname: "Route Metric", argv: ARGV("0", "15") },
2510	};
2511
2512	n = atoi(value);
2513
2514	for (i = 0; i < n; i++) {
2515		char lan_route_ipaddr[] = "lan_route_ipaddrXXX";
2516		char lan_route_netmask[] = "lan_route_netmaskXXX";
2517		char lan_route_gateway[] = "lan_route_gatewayXXX";
2518		char lan_route_metric[] = "lan_route_metricXXX";
2519		char *ipaddr, *netmask, *gateway, *metric;
2520
2521 		snprintf(lan_route_ipaddr, sizeof(lan_route_ipaddr), "%s_ipaddr%d", v->name, i);
2522		snprintf(lan_route_netmask, sizeof(lan_route_netmask), "%s_netmask%d", v->name, i);
2523 		snprintf(lan_route_gateway, sizeof(lan_route_gateway), "%s_gateway%d", v->name, i);
2524 		snprintf(lan_route_metric, sizeof(lan_route_metric), "%s_metric%d", v->name, i);
2525		if (!(ipaddr = websGetVar(wp, lan_route_ipaddr, NULL)) ||
2526		    !(netmask = websGetVar(wp, lan_route_netmask, NULL)) ||
2527		    !(gateway = websGetVar(wp, lan_route_gateway, NULL)) ||
2528		    !(metric = websGetVar(wp, lan_route_metric, NULL)))
2529			return;
2530		if (!*ipaddr && !*netmask && !*gateway && !*metric)
2531			continue;
2532		if (!*ipaddr && !*netmask && *gateway) {
2533			ipaddr = "0.0.0.0";
2534			netmask = "0.0.0.0";
2535		}
2536		if (!*gateway)
2537			gateway = "0.0.0.0";
2538		if (!*metric)
2539			metric = "0";
2540		if (!*ipaddr) {
2541			websBufferWrite(wp, "Invalid <b>%s</b>: must specify an IP Address<br>", v->longname);
2542			continue;
2543		}
2544		if (!*netmask) {
2545			websBufferWrite(wp, "Invalid <b>%s</b>: must specify a Subnet Mask<br>", v->longname);
2546			continue;
2547		}
2548		lan_route_variables[2].argv = ARGV("lan_ipaddr", "lan_netmask");
2549		if (!valid_ipaddr(wp, ipaddr, &lan_route_variables[0]) ||
2550		    !valid_ipaddr(wp, netmask, &lan_route_variables[1]) ||
2551		    !valid_ipaddr(wp, gateway, &lan_route_variables[2]) ||
2552		    !valid_range(wp, metric, &lan_route_variables[3]))
2553			continue;
2554		cur += snprintf(cur, buf + sizeof(buf) - cur, "%s%s:%s:%s:%s",
2555				cur == buf ? "" : " ", ipaddr, netmask, gateway, metric);
2556	}
2557
2558	nvram_set(v->name, buf);
2559}
2560
2561#ifdef __CONFIG_NAT__
2562static void
2563validate_wan_route(webs_t wp, char *value, struct variable *v)
2564{
2565	int n, i;
2566	char buf[1000] = "", *cur = buf;
2567	struct variable wan_route_variables[] = {
2568		{ longname: "Route IP Address", argv: NULL },
2569		{ longname: "Route Subnet Mask", argv: NULL },
2570		{ longname: "Route Gateway", argv: NULL },
2571		{ longname: "Route Metric", argv: ARGV("0", "15") },
2572	};
2573
2574	n = atoi(value);
2575
2576	for (i = 0; i < n; i++) {
2577		char wan_route_ipaddr[] = "wan_route_ipaddrXXX";
2578		char wan_route_netmask[] = "wan_route_netmaskXXX";
2579		char wan_route_gateway[] = "wan_route_gatewayXXX";
2580		char wan_route_metric[] = "wan_route_metricXXX";
2581		char *ipaddr, *netmask, *gateway, *metric;
2582
2583 		snprintf(wan_route_ipaddr, sizeof(wan_route_ipaddr), "%s_ipaddr%d", v->name, i);
2584		snprintf(wan_route_netmask, sizeof(wan_route_netmask), "%s_netmask%d", v->name, i);
2585 		snprintf(wan_route_gateway, sizeof(wan_route_gateway), "%s_gateway%d", v->name, i);
2586 		snprintf(wan_route_metric, sizeof(wan_route_metric), "%s_metric%d", v->name, i);
2587		if (!(ipaddr = websGetVar(wp, wan_route_ipaddr, NULL)) ||
2588		    !(netmask = websGetVar(wp, wan_route_netmask, NULL)) ||
2589		    !(gateway = websGetVar(wp, wan_route_gateway, NULL)) ||
2590		    !(metric = websGetVar(wp, wan_route_metric, NULL)))
2591			continue;
2592		if (!*ipaddr && !*netmask && !*gateway && !*metric)
2593			continue;
2594		if (!*ipaddr && !*netmask && *gateway) {
2595			ipaddr = "0.0.0.0";
2596			netmask = "0.0.0.0";
2597		}
2598		if (!*gateway)
2599			gateway = "0.0.0.0";
2600		if (!*metric)
2601			metric = "0";
2602		if (!*ipaddr) {
2603			websBufferWrite(wp, "Invalid <b>%s</b>: must specify an IP Address<br>", v->longname);
2604			continue;
2605		}
2606		if (!*netmask) {
2607			websBufferWrite(wp, "Invalid <b>%s</b>: must specify a Subnet Mask<br>", v->longname);
2608			continue;
2609		}
2610		if (!valid_ipaddr(wp, ipaddr, &wan_route_variables[0]) ||
2611		    !valid_ipaddr(wp, netmask, &wan_route_variables[1]) ||
2612		    !valid_ipaddr(wp, gateway, &wan_route_variables[2]) ||
2613		    !valid_range(wp, metric, &wan_route_variables[3]))
2614			continue;
2615		cur += snprintf(cur, buf + sizeof(buf) - cur, "%s%s:%s:%s:%s",
2616				cur == buf ? "" : " ", ipaddr, netmask, gateway, metric);
2617	}
2618
2619	nvram_set(v->name, buf);
2620}
2621#endif	/* __CONFIG_NAT__ */
2622
2623static void
2624validate_wl_auth(webs_t wp, char *value, struct variable *v)
2625{
2626	if (!valid_choice(wp, value, v))
2627		return;
2628
2629	if (!strcmp(value, "1")) {
2630		char *wep = websGetVar(wp, "wl_wep", "");
2631		if (!wep || strcmp(wep, "enabled")) {
2632			websBufferWrite(wp, "Invalid <b>WEP Encryption</b>: must be <b>Enabled</b> when <b>802.11 Authentication</b> is <b>%s</b><br>", value);
2633			return;
2634		}
2635	}
2636
2637	nvram_set(v->name, value);
2638}
2639
2640static void
2641validate_wl_preauth(webs_t wp, char *value, struct variable *v)
2642{
2643	if (!strcmp(value, "disabled"))
2644		nvram_set(v->name, "0");
2645	else
2646		nvram_set(v->name, "1");
2647	return;
2648}
2649
2650static void
2651validate_wl_auth_mode(webs_t wp, char *value, struct variable *v)
2652{
2653	if (!valid_choice(wp, value, v))
2654		return;
2655
2656	if (!strcmp(value, "radius")) {
2657		char *wep = websGetVar(wp, "wl_wep", "");
2658		char *ipaddr = websGetVar(wp, "wl_radius_ipaddr", "");
2659		if (!wep || strcmp(wep, "enabled")) {
2660			websBufferWrite(wp, "Invalid <b>WEP Encryption</b>: must be <b>Enabled</b> when <b>Network Authentication</b> is <b>%s</b><br>", value);
2661			return;
2662		}
2663		if (!ipaddr || !strcmp(ipaddr, "")) {
2664			websBufferWrite(wp, "Invalid <b>%s</b>: must first specify a valid <b>RADIUS Server</b><br>", v->longname);
2665			return;
2666		}
2667	}
2668
2669	nvram_set(v->name, value);
2670}
2671
2672static void
2673validate_wl_akm(webs_t wp, char *value, struct variable *v)
2674{
2675	char akms[WLC_IOCTL_SMLEN] = "";
2676	char *wpa, *psk;
2677	char *wpa2, *psk2;
2678
2679	wpa = websGetVar(wp, "wl_akm_wpa", NULL);
2680	psk = websGetVar(wp, "wl_akm_psk", NULL);
2681	wpa2 = websGetVar(wp, "wl_akm_wpa2", NULL);
2682	psk2 = websGetVar(wp, "wl_akm_psk2", NULL);
2683	if (!wpa || !psk
2684	    || !wpa2 || !psk2
2685	    ) {
2686		return;
2687	}
2688
2689	if (!strcmp(wpa, "enabled")
2690	    || !strcmp(wpa2, "enabled")
2691	    ) {
2692		char *ipaddr = websGetVar(wp, "wl_radius_ipaddr", "");
2693		if (!ipaddr || !strcmp(ipaddr, "")) {
2694			websBufferWrite(wp, "Invalid <b>%s</b>: must first specify a valid <b>RADIUS Server</b><br>", v->longname);
2695			return;
2696		}
2697	}
2698
2699	if (!strcmp(psk, "enabled")
2700	    || !strcmp(psk2, "enabled")
2701	    ) {
2702		char *key = websGetVar(wp, "wl_wpa_psk", "");
2703		if (!key || !strcmp(key, "")) {
2704			websBufferWrite(wp, "Invalid <b>%s</b>: must first specify a valid <b>WPA Pre-Shared Key</b><br>", v->longname);
2705			return;
2706		}
2707	}
2708
2709	if (!strcmp(wpa, "enabled") || !strcmp(psk, "enabled")
2710	    || !strcmp(wpa2, "enabled") || !strcmp(psk2, "enabled")
2711	    ) {
2712		char *crypto = websGetVar(wp, "wl_crypto", "");
2713		if (!crypto || (strcmp(crypto, "tkip") && strcmp(crypto, "aes") &&
2714			strcmp(crypto, "tkip+aes"))) {
2715			websBufferWrite(wp, "Invalid <b>%s</b>: <b>Crypto Algorithm</b> mode must be TKIP or AES or TKIP+AES<br>", v->longname);
2716			return;
2717		}
2718	}
2719
2720	if (!strcmp(wpa, "enabled"))
2721		strcat(akms, "wpa ");
2722	if (!strcmp(psk, "enabled"))
2723		strcat(akms, "psk ");
2724	if (!strcmp(wpa2, "enabled"))
2725		strcat(akms, "wpa2 ");
2726	if (!strcmp(psk2, "enabled"))
2727		strcat(akms, "psk2 ");
2728
2729	nvram_set("wl_akm", akms);
2730}
2731
2732static void
2733validate_wl_wpa_psk(webs_t wp, char *value, struct variable *v)
2734{
2735	int len = strlen(value);
2736	char *c;
2737
2738	if (len == 64) {
2739		for (c = value; *c; c++) {
2740			if (!isxdigit((int) *c)) {
2741				websBufferWrite(wp, "Invalid <b>%s</b>: character %c is not a hexadecimal digit<br>", v->longname, *c);
2742				return;
2743			}
2744		}
2745	} else if (len < 8 || len > 63) {
2746		websBufferWrite(wp, "Invalid <b>%s</b>: must be between 8 and 63 ASCII characters or 64 hexadecimal digits<br>", v->longname);
2747		return;
2748	}
2749
2750	nvram_set(v->name, value);
2751}
2752
2753static void
2754validate_wl_key(webs_t wp, char *value, struct variable *v)
2755{
2756	char *c;
2757
2758	switch (strlen(value)) {
2759	case 5:
2760	case 13:
2761		break;
2762	case 10:
2763	case 26:
2764		for (c = value; *c; c++) {
2765			if (!isxdigit((int) *c)) {
2766				websBufferWrite(wp, "Invalid <b>%s</b>: character %c is not a hexadecimal digit<br>", v->longname, *c);
2767				return;
2768			}
2769		}
2770		break;
2771	default:
2772		websBufferWrite(wp, "Invalid <b>%s</b>: must be 5 or 13 ASCII characters or 10 or 26 hexadecimal digits<br>", v->longname);
2773		return;
2774	}
2775
2776	nvram_set(v->name, value);
2777}
2778
2779static void
2780validate_wl_wep(webs_t wp, char *value, struct variable *v)
2781{
2782	char *auth_mode, *auth;
2783
2784	if (!valid_choice(wp, value, v))
2785		return;
2786
2787	auth = websGetVar(wp, "wl_auth", NULL);
2788	auth_mode = websGetVar(wp, "wl_auth_mode", NULL);
2789	if (!strcmp(value, "enabled")) {
2790		if (!auth_mode || strcmp(auth_mode, "radius")) {
2791			char wl_key[] = "wl_keyXXX";
2792
2793			snprintf(wl_key, sizeof(wl_key), "wl_key%s", nvram_safe_get("wl_key"));
2794			if (!strlen(nvram_safe_get(wl_key))) {
2795				websBufferWrite(wp, "Invalid <b>%s</b>: must first specify a valid <b>Network Key %s</b><br>", v->longname, nvram_safe_get("wl_key"));
2796				if (nvram_match(v->name, "enabled")) {
2797					websBufferWrite(wp, "<b>%s</b> is <b>Disabled</b><br>", v->longname);
2798					nvram_set(v->name, "disabled");
2799				}
2800				return;
2801			}
2802		}
2803	}
2804	else {
2805	    	if (!auth || !strcmp(auth, "shared") || !auth_mode || !strcmp(auth_mode, "radius")) {
2806			websBufferWrite(wp, "Invalid <b>WEP Encryption</b>: must be <b>Enabled</b> when <b>Network Authentication</b> is <b>%s</b><br>", auth_mode);
2807			return;
2808	    	}
2809	}
2810	nvram_set(v->name, value);
2811}
2812
2813static void
2814validate_wl_crypto(webs_t wp, char *value, struct variable *v)
2815{
2816	if (!valid_choice(wp, value, v))
2817		return;
2818
2819	nvram_set(v->name, value);
2820}
2821
2822#ifdef __CONFIG_NAT__
2823static void
2824validate_wan_ifname(webs_t wp, char *value, struct variable *v)
2825{
2826	char ifname[64], *next;
2827	foreach (ifname, nvram_safe_get("wan_ifnames"), next)
2828		if (!strcmp(ifname, value)) {
2829			nvram_set(v->name, value);
2830			return;
2831		}
2832	websBufferWrite(wp, "Invalid <b>%s</b>: must be one of <b>%s</b><br>", v->longname, nvram_safe_get("wan_ifnames"));
2833}
2834#endif	/* __CONFIG_NAT__ */
2835
2836static void
2837validate_wl_afterburner(webs_t wp, char *value, struct variable *v)
2838{
2839	if (!valid_choice(wp, value, v))
2840		return;
2841
2842	/* force certain wireless variables to fixed values */
2843	if (!strcmp(value, "auto")) {
2844		if (
2845		    nvram_invmatch("wl_mode", "ap") ||
2846		    nvram_invmatch("wl_lazywds", "0") ||
2847		    nvram_invmatch("wl_wds", "")) {
2848			/* notify the user */
2849			websBufferWrite(wp, "Invalid <b>%s</b>: AfterBurner mode requires:"
2850				"<br><b>Mode</b> set to <b>Access Point</b>"
2851				"<br><b>Bridge Restrict</b> set to <b>Enabled</b>"
2852				"<br><b>Remote Bridges</b> set to <b>empty</b>"
2853				"<br><b>Fragmentation value</b> set to <b>2346(disable fragmentation)</b>"
2854				"<br><b>AfterBurner mode is disabled!</b>"
2855				"<br>", v->longname);
2856			return;
2857		}
2858	}
2859
2860	nvram_set(v->name, value);
2861}
2862
2863static void
2864validate_wl_lazywds(webs_t wp, char *value, struct variable *v)
2865{
2866	char *afterburner;
2867	if (strcmp(value, "0") &&
2868	    (afterburner = websGetVar(wp, "wl_afterburner", NULL)) && (!strcmp(afterburner, "auto"))) {
2869		websBufferWrite(wp, "Invalid <b>%s</b>: must be set to <b>Enabled</b> when AfterBurner is enabled.<br>", v->longname);
2870		return;
2871	}
2872	validate_choice(wp, value, v);
2873}
2874
2875static void
2876validate_wl_wds_hwaddrs(webs_t wp, char *value, struct variable *v)
2877{
2878	char *afterburner;
2879	if ((afterburner = websGetVar(wp, "wl_afterburner", NULL)) && (!strcmp(afterburner, "auto"))) {
2880		int n, i;
2881		char name[100], *mac;
2882
2883		n = atoi(value);
2884
2885		for (i = 0; i < n; i++) {
2886			snprintf(name, sizeof(name), "%s%d", v->name, i);
2887			if (!(mac = websGetVar(wp, name, NULL)) || strlen(mac) == 0)
2888				continue;
2889			websBufferWrite(wp, "Invalid <b>%s</b>: must be empty when AfterBurner is enabled.<br>", v->longname);
2890			return;
2891		}
2892	}
2893	validate_list(wp, value, v, valid_hwaddr);
2894}
2895
2896static void
2897validate_wl_mode(webs_t wp, char *value, struct variable *v)
2898{
2899	char *afterburner;
2900	if (strcmp(value, "ap") &&
2901	    (afterburner = websGetVar(wp, "wl_afterburner", NULL)) && (!strcmp(afterburner, "auto"))) {
2902		websBufferWrite(wp, "Invalid <b>%s</b>: must be set to <b>Access Point</b> when AfterBurner is enabled.<br>", v->longname);
2903		return;
2904	}
2905	validate_choice(wp, value, v);
2906}
2907
2908static void
2909validate_noack(webs_t wp, char *value, struct variable *v)
2910{
2911	char *wme;
2912
2913	/* return if wme is not enabled */
2914	if (!(wme = websGetVar(wp, "wl_wme", NULL)))
2915		return;
2916	else if (strcmp(wme, "on"))
2917		return;
2918
2919	validate_choice(wp, value, v);
2920}
2921
2922static void
2923validate_wl_wme_params(webs_t wp, char *value, struct variable *v)
2924{
2925	int n, i;
2926	int cwmin = 0, cwmax = 0;
2927	char *wme, *afterburner;
2928	char name[100];
2929	char buf[1000] = "", *cur = buf;
2930	struct {
2931		char *name;
2932		int range;
2933		char *arg1;
2934		char *arg2;
2935	} field_attrib[] = {
2936		{ "WME AC CWmin", 1, "0", "32767" },
2937		{ "WME AC CWmax", 1, "0", "32767" },
2938		{ "WME AC AIFSN", 1, "1", "15" },
2939		{ "WME AC TXOP(b)", 1, "0", "65504" },
2940		{ "WME AC TXOP(a/g)", 1, "0", "65504" },
2941		{ "WME AC Admin Forced", 0, "on", "off" }
2942	};
2943
2944	/* return if wme is not enabled */
2945	if (!(wme = websGetVar(wp, "wl_wme", NULL)))
2946		return;
2947	else if (strcmp(wme, "on"))
2948		return;
2949
2950	/* return if afterburner enabled */
2951	if ((afterburner = websGetVar(wp, "wl_afterburner", NULL)) && (!strcmp(afterburner, "auto")))
2952		return;
2953
2954	n = atoi(value) + 1;
2955
2956	for (i = 0; i < n; i++) {
2957		snprintf(name, sizeof(name), "%s%d", v->name, i);
2958		if (!(value = websGetVar(wp, name, NULL)))
2959			return;
2960		if (!*value && v->nullok)
2961			continue;
2962
2963		if (i == 0)
2964			cwmin = atoi(value);
2965		else if (i == 1) {
2966			cwmax = atoi(value);
2967			if (cwmax < cwmin) {
2968				websBufferWrite(wp, "Invalid <b>%s</b> %d: greater than <b>%s</b> %d<br>",
2969					field_attrib[0].name, cwmin, field_attrib[i].name, cwmax);
2970				return;
2971			}
2972		}
2973		if (field_attrib[i].range) {
2974			if (atoi(value) < atoi(field_attrib[i].arg1) || atoi(value) > atoi(field_attrib[i].arg2)) {
2975				websBufferWrite(wp, "Invalid <b>%s</b> %d: should be in range %s to %s<br>",
2976					field_attrib[i].name, atoi(value), field_attrib[i].arg1, field_attrib[i].arg2);
2977				return;
2978			}
2979		} else {
2980			if (strcmp(value, field_attrib[i].arg1) && strcmp(value, field_attrib[i].arg2)) {
2981				websBufferWrite(wp, "Invalid <b>%s</b> %s: should be %s or %s<br>",
2982					field_attrib[i].name, value, field_attrib[i].arg1, field_attrib[i].arg2);
2983			}
2984		}
2985
2986		cur += snprintf(cur, buf + sizeof(buf) - cur, "%s%s",
2987				cur == buf ? "" : " ", value);
2988	}
2989
2990	nvram_set(v->name, buf);
2991}
2992
2993/* Hook to write wl_* default set through to wl%d_* variable set */
2994static void
2995wl_unit(webs_t wp, char *value, struct variable *v)
2996{
2997	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
2998
2999	/* Do not write through if no interfaces are present */
3000	if (atoi(value) < 0)
3001		return;
3002
3003	/* Set prefix */
3004	snprintf(prefix, sizeof(prefix), "wl%d_", atoi(value));
3005
3006	/* Write through to selected variable set */
3007	for (; v >= variables && !strncmp(v->name, "wl_", 3); v--)
3008		nvram_set(strcat_r(prefix, &v->name[3], tmp), nvram_safe_get(v->name));
3009}
3010
3011#ifdef __CONFIG_NAT__
3012static void
3013wan_primary(webs_t wp)
3014{
3015	char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
3016	int i;
3017	for (i = 0; i < MAX_NVPARSE; i ++) {
3018		/* skip non-exist and disabled connection */
3019		wan_prefix(i, prefix);
3020		if (!nvram_get(strcat_r(prefix, "unit", tmp))||
3021		    nvram_match(strcat_r(prefix, "proto", tmp), "disabled"))
3022			continue;
3023		/* make connection <i> primary */
3024		nvram_set(strcat_r(prefix, "primary", tmp), "1");
3025		/* notify the user */
3026		websBufferWrite(wp, "<br><b>%s</b> is set to primary.",
3027			wan_name(i, prefix, tmp, sizeof(tmp)));
3028		break;
3029	}
3030}
3031
3032/* Hook to write wan_* default set through to wan%d_* variable set */
3033static void
3034wan_unit(webs_t wp, char *value, struct variable *v)
3035{
3036	char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
3037	char pppx[] = "pppXXXXXXXXXXX";
3038	int unit, i;
3039	char *wan_ifname;
3040	int wan_disabled = 0;
3041	int wan_prim = 0;
3042	int wan_wildcard = 0;
3043	char *wan_pppoe_service;
3044	char *wan_pppoe_ac;
3045	char wan_tmp[NVRAM_BUFSIZE];
3046	int wildcard;
3047	char *pppoe_service;
3048	char *pppoe_ac;
3049	char ea[ETHER_ADDR_LEN], wan_ea[ETHER_ADDR_LEN];
3050
3051	/* Do not write through if no connections are present */
3052	if ((unit = atoi(value)) < 0)
3053		return;
3054
3055	/* override wan_pppoe_ifname */
3056	if (nvram_match("wan_proto", "pppoe")) {
3057		snprintf(pppx, sizeof(pppx), "ppp%d", unit);
3058		nvram_set("wan_pppoe_ifname", pppx);
3059	}
3060
3061	/*
3062	* Need to make sure this connection can co-exist with others.
3063	* Disable others if it can't (assuming this is the wanted one).
3064	* Disabled connection is for sure no problem to co-exist with
3065	* other connections.
3066	*/
3067	if (nvram_match("wan_proto", "disabled")) {
3068		/* Non primary always go with disabled connection. */
3069		nvram_set("wan_primary", "0");
3070		wan_disabled = 1;
3071	}
3072	/*
3073	* PPPoE connection is for sure no problem to co-exist with
3074	* other PPPoE connections even when they share the same
3075	* ethernet interface, but we need to make sure certain
3076	* PPPoE parameters are reasonablely different from eatch other
3077	* if they share the same ethernet interface.
3078	*/
3079	else if (nvram_match("wan_proto", "pppoe")) {
3080		/* must disable others if this connection is wildcard (any service any ac) */
3081		wan_pppoe_service = nvram_get("wan_pppoe_service");
3082		wan_pppoe_ac = nvram_get("wan_pppoe_ac");
3083		wan_wildcard = (wan_pppoe_service == NULL || *wan_pppoe_service == 0) &&
3084			(wan_pppoe_ac == NULL || *wan_pppoe_ac == 0);
3085		wan_ifname = nvram_safe_get("wan_ifname");
3086		wan_name(unit, "wan_", wan_tmp, sizeof(wan_tmp));
3087		/* check all PPPoE connections that share the same interface */
3088		for (i = 0; i < MAX_NVPARSE; i ++) {
3089			/* skip the current connection */
3090			if (i == unit)
3091				continue;
3092			/* skip non-exist and connection that does not share the same i/f */
3093			wan_prefix(i, prefix);
3094			if (!nvram_get(strcat_r(prefix, "unit", tmp)) ||
3095			    nvram_match(strcat_r(prefix, "proto", tmp), "disabled") ||
3096			    nvram_invmatch(strcat_r(prefix, "ifname", tmp), wan_ifname))
3097				continue;
3098			/* PPPoE can share the same i/f, but none can be wildcard */
3099			if (nvram_match(strcat_r(prefix, "proto", tmp), "pppoe")) {
3100				if (wan_wildcard) {
3101					/* disable connection <i> */
3102					nvram_set(strcat_r(prefix, "proto", tmp), "disabled");
3103					nvram_set(strcat_r(prefix, "primary", tmp), "0");
3104					/* notify the user */
3105					websBufferWrite(wp, "<br><b>%s</b> is <b>disabled</b> because both "
3106						"<b>PPPoE Service Name</b> and <b>PPPoE Access Concentrator</b> "
3107						"in <b>%s</b> are empty.",
3108						wan_name(i, prefix, tmp, sizeof(tmp)), wan_tmp);
3109				}
3110				else {
3111					pppoe_service = nvram_get(strcat_r(prefix, "pppoe_service", tmp));
3112					pppoe_ac = nvram_get(strcat_r(prefix, "pppoe_ac", tmp));
3113					wildcard = (pppoe_service == NULL || *pppoe_service == 0) &&
3114						(pppoe_ac == NULL || *pppoe_ac == 0);
3115					/* allow connection <i> if certain pppoe parameters are not all same */
3116					if (!wildcard &&
3117					    (nvram_invmatch(strcat_r(prefix, "pppoe_service", tmp), nvram_safe_get("wan_pppoe_service")) ||
3118					         nvram_invmatch(strcat_r(prefix, "pppoe_ac", tmp), nvram_safe_get("wan_pppoe_ac"))))
3119						continue;
3120					/* disable connection <i> */
3121					nvram_set(strcat_r(prefix, "proto", tmp), "disabled");
3122					nvram_set(strcat_r(prefix, "primary", tmp), "0");
3123					/* notify the user */
3124					websBufferWrite(wp, "<br><b>%s</b> is <b>disabled</b> because both its "
3125						"<b>PPPoE Service Name</b> and <b>PPPoE Access Concentrator</b> "
3126						"are empty.",
3127						wan_name(i, prefix, tmp, sizeof(tmp)));
3128				}
3129			}
3130			/* other types can't (?) share the same i/f with PPPoE */
3131			else {
3132				/* disable connection <i> */
3133				nvram_set(strcat_r(prefix, "proto", tmp), "disabled");
3134				nvram_set(strcat_r(prefix, "primary", tmp), "0");
3135				/* notify the user */
3136				websBufferWrite(wp, "<br><b>%s</b> is <b>disabled</b> because it can't  "
3137					"share the same interface with <b>%s</b>.",
3138					wan_name(i, prefix, tmp, sizeof(tmp)), wan_tmp);
3139			}
3140		}
3141	}
3142	/*
3143	* All other types (now DHCP, Static) can't co-exist with
3144	* other connections if they use the same ethernet i/f.
3145	*/
3146	else {
3147		wan_ifname = nvram_safe_get("wan_ifname");
3148		wan_name(unit, "wan_", wan_tmp, sizeof(wan_tmp));
3149		/* check all connections that share the same interface */
3150		for (i = 0; i < MAX_NVPARSE; i ++) {
3151			/* skip the current connection */
3152			if (i == unit)
3153				continue;
3154			/* check if connection <i> exists and share the same i/f*/
3155			wan_prefix(i, prefix);
3156			if (!nvram_get(strcat_r(prefix, "unit", tmp)) ||
3157			    nvram_match(strcat_r(prefix, "proto", tmp), "disabled") ||
3158			    nvram_invmatch(strcat_r(prefix, "ifname", tmp), wan_ifname))
3159				continue;
3160			/* disable connection <i> */
3161			nvram_set(strcat_r(prefix, "proto", tmp), "disabled");
3162			nvram_set(strcat_r(prefix, "primary", tmp), "0");
3163			/* notify the user */
3164			websBufferWrite(wp, "<br><b>%s</b> is disabled because it can't share "
3165				"the ethernet interface with <b>%s</b>.",
3166				wan_name(i, prefix, tmp, sizeof(tmp)), wan_tmp);
3167		}
3168	}
3169
3170	/*
3171	* Check if MAC address has been changed. Need to sync it to all connections
3172	* that share the same i/f if it is changed.
3173	*/
3174	wan_prefix(unit, prefix);
3175	ether_atoe(nvram_safe_get("wan_hwaddr"), wan_ea);
3176	ether_atoe(nvram_safe_get(strcat_r(prefix, "hwaddr", tmp)), ea);
3177	if (memcmp(ea, wan_ea, ETHER_ADDR_LEN)) {
3178		wan_ifname = nvram_safe_get("wan_ifname");
3179		wan_name(unit, "wan_", wan_tmp, sizeof(wan_tmp));
3180		/* sync all connections that share the same interface */
3181		for (i = 0; i < MAX_NVPARSE; i ++) {
3182			/* skip the current connection */
3183			if (i == unit)
3184				continue;
3185			/* check if connection <i> exists and share the same i/f*/
3186			wan_prefix(i, prefix);
3187			if (!nvram_get(strcat_r(prefix, "unit", tmp)) ||
3188			    nvram_invmatch(strcat_r(prefix, "ifname", tmp), wan_ifname))
3189				continue;
3190			/* check if connection <i>'s hardware address is different */
3191			if (ether_atoe(nvram_safe_get(strcat_r(prefix, "hwaddr", tmp)), ea) &&
3192			    !memcmp(ea, wan_ea, ETHER_ADDR_LEN))
3193			    continue;
3194			/* change connection <i>'s hardware address */
3195			nvram_set(strcat_r(prefix, "hwaddr", tmp), nvram_safe_get("wan_hwaddr"));
3196			/* notify the user */
3197			websBufferWrite(wp, "<br><b>MAC Address</b> in <b>%s</b> is changed to "
3198				"<b>%s</b> because it shares the ethernet interface with <b>%s</b>.",
3199				wan_name(i, prefix, tmp, sizeof(tmp)), nvram_safe_get("wan_hwaddr"),
3200				wan_tmp);
3201		}
3202	}
3203
3204	/* Set prefix */
3205	wan_prefix(unit, prefix);
3206
3207	/* Write through to selected variable set */
3208	for (; v >= variables && !strncmp(v->name, "wan_", 4); v--)
3209		nvram_set(strcat_r(prefix, &v->name[4], tmp), nvram_safe_get(v->name));
3210
3211	/*
3212	* There must be one and only one primary connection among all
3213	* enabled connections so that traffic can be routed by default
3214	* through the primary connection unless they are targetted to
3215	* a specific connection by means of static routes. (Primary ~=
3216	* Default Gateway).
3217	*/
3218	/* the current connection is primary, set others to non-primary */
3219	if (!wan_disabled && nvram_match(strcat_r(prefix, "primary", tmp), "1")) {
3220		/* set other connections to non-primary */
3221		for (i = 0; i < MAX_NVPARSE; i ++) {
3222			/* skip the current connection */
3223			if (i == unit)
3224				continue;
3225			/* skip non-exist and disabled connection */
3226			wan_prefix(i, prefix);
3227			if (!nvram_get(strcat_r(prefix, "unit", tmp)) ||
3228			    nvram_match(strcat_r(prefix, "proto", tmp), "disabled"))
3229				continue;
3230			/* skip non-primary connection */
3231			if (nvram_invmatch(strcat_r(prefix, "primary", tmp), "1"))
3232				continue;
3233			/* force primary to non-primary */
3234			nvram_set(strcat_r(prefix, "primary", tmp), "0");
3235			/* notify the user */
3236			websBufferWrite(wp, "<br><b>%s</b> is set to non-primary.",
3237				wan_name(i, prefix, tmp, sizeof(tmp)));
3238		}
3239		wan_prim = 1;
3240	}
3241	/* the current connection is not parimary, check if there is any primary */
3242	else {
3243		/* check other connections to see if there is any primary */
3244		for (i = 0; i < MAX_NVPARSE; i ++) {
3245			/* skip the current connection */
3246			if (i == unit)
3247				continue;
3248			/* primary connection exists, honor it */
3249			wan_prefix(i, prefix);
3250			if (nvram_match(strcat_r(prefix, "primary", tmp), "1")) {
3251				wan_prim = 1;
3252				break;
3253			}
3254		}
3255	}
3256	/* no one is primary, pick the first enabled one as primary */
3257	if (!wan_prim)
3258		wan_primary(wp);
3259}
3260#endif	/* __CONFIG_NAT__ */
3261
3262/*
3263 * Variables are set in order (put dependent variables later). Set
3264 * nullok to TRUE to ignore zero-length values of the variable itself.
3265 * For more complicated validation that cannot be done in one pass or
3266 * depends on additional form components or can throw an error in a
3267 * unique painful way, write your own validation routine and assign it
3268 * to a hidden variable (e.g. filter_ip).
3269 *
3270 * EZC_FLAGS_READ : implies the variable will be returned for ezconfig read request
3271 * EZC_FLAGS_WRITE : allows the variable to be modified by the ezconfig tool
3272 *
3273 * The variables marked with EZConfig have to maintain backward compatibility.
3274 * If they cannot then the ezc_version has to be bumped up
3275 *
3276 */
3277struct variable variables[] = {
3278	/* basic settings */
3279	{ "http_username", "Router Username", validate_name, ARGV("0", "63"), TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3280	{ "http_passwd", "Router Password", validate_name, ARGV("0", "63"), TRUE, EZC_FLAGS_WRITE },
3281	{ "http_wanport", "Router WAN Port", validate_range, ARGV("0", "65535"), TRUE, 0 },
3282	{ "router_disable", "Router Mode", validate_choice, ARGV("0", "1"), FALSE, 0 },
3283	{ "fw_disable", "Firewall", validate_choice, ARGV("0", "1"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3284	{ "time_zone", "Time Zone", validate_choice, ARGV("PST8PDT", "MST7MDT", "CST6CDT", "EST5EDT"), FALSE },
3285	{ "upnp_enable", "UPnP", validate_choice, ARGV("0", "1"), FALSE, 0 },
3286	{ "ezc_enable", "EZConfig", validate_choice, ARGV("0", "1"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE},
3287	{ "ntp_server", "NTP Servers", validate_ipaddrs, NULL, TRUE, 0 },
3288	{ "log_level", "Connection Logging", validate_range, ARGV("0", "3"), FALSE, 0 },
3289	{ "log_ipaddr", "Log LAN IP Address", validate_ipaddr, ARGV("lan_ipaddr", "lan_netmask"), TRUE, 0 },
3290	{ "log_ram_enable", "Syslog in RAM", validate_choice, ARGV("0", "1"), FALSE, 0 },
3291	/* SES Settings */
3292	{ "ses_enable", "SES", validate_choice, ARGV("0", "1"), FALSE, 0 },
3293	{ "ses_event", "SES Event", validate_choice, ARGV("0", "3", "4", "6", "7"), FALSE, 0 },
3294	/* LAN settings */
3295	{ "lan_dhcp", "DHCP Client", validate_choice, ARGV("1", "0"), FALSE, 0 },
3296	{ "lan_ipaddr", "IP Address", validate_lan_ipaddr, NULL, FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3297	{ "lan_netmask", "Subnet Mask", validate_ipaddr, NULL, TRUE, 0 },
3298	{ "lan_gateway", "Gateway Address", validate_ipaddr, NULL, TRUE, 0 },
3299	{ "lan_proto", "DHCP Server", validate_choice, ARGV("dhcp", "static"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3300	{ "dhcp_start", "DHCP Server LAN IP Address Range", validate_dhcp, NULL, FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3301	{ "lan_lease", "DHCP Server Lease Time", validate_range, ARGV("1", "604800"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3302	{ "lan_stp", "Spanning Tree Protocol", validate_choice, ARGV("0", "1"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3303	{ "lan_route", "Static Routes", validate_lan_route, NULL, FALSE, 0 },
3304#ifdef __CONFIG_NAT__
3305	/* ALL wan_XXXX variables below till wan_uint variable are per-interface */
3306	{ "wan_desc", "Description", validate_name, ARGV("0", "255"), TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3307	{ "wan_proto", "Protocol", validate_choice, ARGV("dhcp", "static", "pppoe", "disabled"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3308	{ "wan_hostname", "Host Name", validate_name, ARGV("0", "255"), TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3309	{ "wan_domain", "Domain Name", validate_name, ARGV("0", "255"), TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3310	{ "wan_ifname", "Interface Name", validate_wan_ifname, NULL, TRUE, 0 },
3311	{ "wan_hwaddr", "MAC Address", validate_hwaddr, NULL, TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3312	{ "wan_ipaddr", "IP Address", validate_ipaddr, NULL, FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3313	{ "wan_netmask", "Subnet Mask", validate_ipaddr, NULL, FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3314	{ "wan_gateway", "Default Gateway", validate_ipaddr, NULL, TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3315	{ "wan_dns", "DNS Servers", validate_ipaddrs, NULL, TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3316	{ "wan_wins", "WINS Servers", validate_ipaddrs, NULL, TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3317	{ "wan_pppoe_ifname", "PPPoE Interface Name", NULL, NULL, TRUE, 0 },
3318	{ "wan_pppoe_username", "PPPoE Username", validate_name, ARGV("0", "255"), TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3319	{ "wan_pppoe_passwd", "PPPoE Password", validate_name, ARGV("0", "255"), TRUE, EZC_FLAGS_WRITE },
3320	{ "wan_pppoe_service", "PPPoE Service Name", validate_name, ARGV("0", "255"), TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3321	{ "wan_pppoe_ac", "PPPoE Access Concentrator", validate_name, ARGV("0", "255"), TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3322	{ "wan_pppoe_keepalive", "PPPoE Keep Alive", validate_choice, ARGV("0", "1"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3323	{ "wan_pppoe_demand", "PPPoE Connect on Demand", validate_choice, ARGV("0", "1"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3324	{ "wan_pppoe_idletime", "PPPoE Max Idle Time", validate_range, ARGV("1", "3600"), TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3325	{ "wan_pppoe_mru", "PPPoE MRU", validate_range, ARGV("128", "16384"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3326	{ "wan_pppoe_mtu", "PPPoE MTU", validate_range, ARGV("128", "16384"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3327	{ "wan_primary", "Primary Interface", validate_choice, ARGV("0", "1"), FALSE, 0 },
3328	{ "wan_route", "Static Routes", validate_wan_route, NULL, FALSE, 0 },
3329	/* MUST leave this entry here after all wl_XXXX per-interface variables */
3330	{ "wan_unit", "WAN Instance", wan_unit, NULL, TRUE, 0 },
3331	/* filter settings */
3332	{ "filter_macmode", "MAC Filter Mode", validate_choice, ARGV("disabled", "allow", "deny"), FALSE, 0 },
3333	{ "filter_maclist", "MAC Filter", validate_hwaddrs, NULL, TRUE, 0 },
3334	{ "filter_client", "LAN Client Filter", validate_filter_client, ARGV("0", XSTR(MAX_NVPARSE - 1)), FALSE, 0 },
3335	/* routing settings */
3336	{ "forward_port", "Port Forward", validate_forward_port, ARGV("0", XSTR(MAX_NVPARSE - 1)), FALSE, 0 },
3337	{ "autofw_port", "Application Specific Port Forward", validate_autofw_port, ARGV("0", XSTR(MAX_NVPARSE - 1)), FALSE, 0 },
3338	{ "dmz_ipaddr", "DMZ LAN IP Address", validate_ipaddr, ARGV("lan_ipaddr", "lan_netmask"), TRUE, 0 },
3339#endif	/* __CONFIG_NAT__ */
3340	/* ALL wl_XXXX variables are per-interface */
3341	{ "wl_ssid", "Network Name (SSID)", validate_name, ARGV("1", "32"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3342	{ "wl_closed", "Network Type", validate_choice, ARGV("0", "1"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3343        { "wl_ap_isolate", "AP Isolate", validate_choice, ARGV("0", "1"), FALSE, 0 },
3344	{ "wl_country_code", "Country Code", validate_country, NULL, FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3345	{ "wl_mode", "Mode", validate_wl_mode, ARGV("ap", "wds", "sta", "wet"), FALSE, 0 },
3346	{ "wl_infra", "Network", validate_choice, ARGV("0", "1"), FALSE, 0 },
3347	{ "wl_lazywds", "Bridge Restrict", validate_wl_lazywds, ARGV("0", "1"), FALSE, 0 },
3348	{ "wl_wds", "Bridges", validate_wl_wds_hwaddrs, NULL, TRUE, 0 },
3349	{ "wl_wds_timeout", "Link Timeout Interval", NULL, NULL, TRUE, 0 },
3350	{ "wl_macmode", "MAC Restrict Mode", validate_choice, ARGV("disabled", "allow", "deny"), FALSE, 0 },
3351	{ "wl_maclist", "Allowed MAC Address", validate_hwaddrs, NULL, TRUE, 0 },
3352	{ "wl_radio", "Radio Enable", validate_choice, ARGV("0", "1"), FALSE, 0 },
3353	{ "wl_phytype", "Radio Band", validate_choice, ARGV("a", "b", "g"), TRUE, 0 },
3354	{ "wl_antdiv", "Antenna Diversity", validate_choice, ARGV("-1", "0", "1", "3"), FALSE, 0 },
3355	/* Channel and rate are fixed in wlconf() if incorrect */
3356	{ "wl_channel", "Channel", validate_range, ARGV("0", "216"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3357	{ "wl_rate", "Rate", validate_range, ARGV("0", "54000000"), FALSE, 0 },
3358	{ "wl_rateset", "Supported Rates", validate_choice, ARGV("all", "default", "12"), FALSE, 0 },
3359	{ "wl_mrate", "Multicast Rate", validate_range, ARGV("0", "54000000"), FALSE, 0 },
3360	{ "wl_frag", "Fragmentation Threshold", validate_range, ARGV("256", "2346"), FALSE, 0 },
3361	{ "wl_rts", "RTS Threshold", validate_range, ARGV("0", "2347"), FALSE, 0 },
3362	{ "wl_dtim", "DTIM Period", validate_range, ARGV("1", "255"), FALSE, 0 },
3363	{ "wl_bcn", "Beacon Interval", validate_range, ARGV("1", "65535"), FALSE, 0 },
3364	{ "wl_plcphdr", "Preamble Type", validate_choice, ARGV("long", "short"), FALSE, 0 },
3365	{ "wl_maxassoc", "Max Assocation Limit", validate_range, ARGV("1", "256"), FALSE, 0 },
3366	{ "wl_gmode", "54g Mode", validate_choice, ARGV(XSTR(GMODE_AUTO), XSTR(GMODE_ONLY), XSTR(GMODE_PERFORMANCE), XSTR(GMODE_LRS), XSTR(GMODE_LEGACY_B)), FALSE, 0 },
3367	{ "wl_gmode_protection", "54g Protection", validate_choice, ARGV("off", "auto"), FALSE, 0 },
3368	{ "wl_frameburst", "XPress Technology", validate_choice, ARGV("off", "on"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3369	{ "wl_afterburner", "AfterBurner Technology", validate_wl_afterburner, ARGV("off", "auto"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3370	{ "wl_wme", "WME Support", validate_choice, ARGV("off", "on"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3371	{ "wl_wme_no_ack", "No-Acknowledgement", validate_noack, ARGV("off", "on"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3372	{ "wl_wme_ap_bk", "WME AP BK", validate_wl_wme_params, NULL, TRUE, 0 },
3373	{ "wl_wme_ap_be", "WME AP BE", validate_wl_wme_params, NULL, TRUE, 0 },
3374	{ "wl_wme_ap_vi", "WME AP VI", validate_wl_wme_params, NULL, TRUE, 0 },
3375	{ "wl_wme_ap_vo", "WME AP VO", validate_wl_wme_params, NULL, TRUE, 0 },
3376	{ "wl_wme_sta_bk", "WME STA BK", validate_wl_wme_params, NULL, TRUE, 0 },
3377	{ "wl_wme_sta_be", "WME STA BE", validate_wl_wme_params, NULL, TRUE, 0 },
3378	{ "wl_wme_sta_vi", "WME STA VI", validate_wl_wme_params, NULL, TRUE, 0 },
3379	{ "wl_wme_sta_vo", "WME STA VO", validate_wl_wme_params, NULL, TRUE, 0 },
3380	/* security parameters */
3381	{ "wl_key", "Network Key Index", validate_range, ARGV("1", "4"), FALSE, EZC_FLAGS_WRITE },
3382	{ "wl_key1", "Network Key 1", validate_wl_key, NULL, TRUE, EZC_FLAGS_WRITE},
3383	{ "wl_key2", "Network Key 2", validate_wl_key, NULL, TRUE, EZC_FLAGS_WRITE},
3384	{ "wl_key3", "Network Key 3", validate_wl_key, NULL, TRUE, EZC_FLAGS_WRITE},
3385	{ "wl_key4", "Network Key 4", validate_wl_key, NULL, TRUE, EZC_FLAGS_WRITE},
3386	{ "wl_auth", "802.11 Authentication", validate_wl_auth, ARGV("0", "1"), FALSE, 0},
3387	{ "wl_auth_mode", "Network Authentication", validate_wl_auth_mode, ARGV("radius", "none"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3388	{ "wl_akm", "Authenticated Key Management", validate_wl_akm, NULL, FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3389	{ "wl_wep", "WEP Encryption", validate_wl_wep, ARGV("disabled", "enabled"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3390	{ "wl_crypto", "WPA Encryption", validate_wl_crypto, ARGV("tkip", "aes", "tkip+aes"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3391	{ "wl_net_reauth", "Network Re-auth Interval", NULL, NULL, TRUE, 0 },
3392	{ "wl_preauth", "Network Preauthentication Support", validate_wl_preauth, ARGV("disabled", "enabled"), FALSE, 0 },
3393	{ "wl_radius_ipaddr", "RADIUS Server", validate_ipaddr, NULL, TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3394	{ "wl_radius_port", "RADIUS Port", validate_range, ARGV("0", "65535"), FALSE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3395	{ "wl_radius_key", "RADIUS Shared Secret", validate_name, ARGV("0", "255"), TRUE, EZC_FLAGS_WRITE},
3396	{ "wl_wpa_psk", "WPA Pre-Shared Key", validate_wl_wpa_psk, ARGV("64"), TRUE, EZC_FLAGS_WRITE},
3397	{ "wl_wpa_gtk_rekey", "Network Key Rotation Interval", NULL, NULL, TRUE, EZC_FLAGS_READ | EZC_FLAGS_WRITE },
3398	/* MUST leave this entry here after all wl_XXXX variables */
3399	{ "wl_unit", "802.11 Instance", wl_unit, NULL, TRUE, 0 },
3400	/* Internal variables */
3401	{ "os_server", "OS Server", NULL, NULL, TRUE, 0 },
3402	{ "stats_server", "Stats Server", NULL, NULL, TRUE, 0 },
3403	{ "timer_interval", "Timer Interval", NULL, NULL, TRUE, 0 },
3404};
3405
3406int
3407variables_arraysize(void)
3408{
3409	return ARRAYSIZE(variables);
3410}
3411
3412static void
3413validate_cgi(webs_t wp)
3414{
3415	struct variable *v;
3416	char *value;
3417
3418	websBufferInit(wp);
3419	if (!webs_buf) {
3420		websWrite(wp, "out of memory\n");
3421		websDone(wp, 0);
3422		return;
3423	}
3424
3425
3426	/* Validate and set variables in table order */
3427	for (v = variables; v < &variables[ARRAYSIZE(variables)]; v++) {
3428		if (!(value = websGetVar(wp, v->name, NULL)))
3429			continue;
3430		if ((!*value && v->nullok) || !v->validate)
3431			nvram_set(v->name, value);
3432		else
3433			v->validate(wp, value, v);
3434	}
3435
3436	websBufferFlush(wp);
3437}
3438
3439static int
3440apply_cgi(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
3441	  char_t *url, char_t *path, char_t *query)
3442{
3443	int action = NOTHING;
3444	char *value;
3445
3446	websHeader(wp);
3447	websWrite(wp, (char_t *) apply_header);
3448
3449	value = websGetVar(wp, "action", "");
3450
3451	/* Apply values */
3452	if (!strcmp(value, "Apply")) {
3453		websWrite(wp, "Validating values...");
3454		validate_cgi(wp);
3455		websWrite(wp, "done<br>");
3456		websWrite(wp, "Committing values...");
3457		nvram_set("is_modified", "1");
3458		nvram_set("is_default", "0");
3459		nvram_commit();
3460		websWrite(wp, "done<br>");
3461		action = RESTART;
3462	}
3463
3464	/* Restore defaults */
3465	else if (!strncmp(value, "Restore", 7)) {
3466		websWrite(wp, "Restoring defaults...");
3467		nvram_set("sdram_ncdl", "0");
3468		nvram_set("restore_defaults", "1");
3469		nvram_commit();
3470		websWrite(wp, "done<br>");
3471		action = REBOOT;
3472	}
3473
3474	/* Release lease */
3475	else if (!strcmp(value, "Release")) {
3476		websWrite(wp, "Releasing lease...");
3477		if (sys_release())
3478			websWrite(wp, "error<br>");
3479		else
3480			websWrite(wp, "done<br>");
3481		action = NOTHING;
3482	}
3483
3484	/* Renew lease */
3485	else if (!strcmp(value, "Renew")) {
3486		websWrite(wp, "Renewing lease...");
3487		if (sys_renew())
3488			websWrite(wp, "error<br>");
3489		else
3490			websWrite(wp, "done<br>");
3491		action = NOTHING;
3492	}
3493
3494	/* Reboot router */
3495	else if (!strcmp(value, "Reboot")) {
3496		websWrite(wp, "Rebooting...");
3497		action = REBOOT;
3498	}
3499
3500	/* Upgrade image */
3501	else if (!strcmp(value, "Upgrade")) {
3502		char *os_name = nvram_safe_get("os_name");
3503		char *os_server = websGetVar(wp, "os_server", nvram_safe_get("os_server"));
3504		char *os_version = websGetVar(wp, "os_version", "current");
3505		char url[PATH_MAX];
3506		if (!*os_version)
3507			os_version = "current";
3508		snprintf(url, sizeof(url), "%s/%s/%s/%s.trx",
3509			 os_server, os_name, os_version, os_name);
3510		websWrite(wp, "Retrieving %s...", url);
3511		if (sys_upgrade(url, NULL, NULL)) {
3512			websWrite(wp, "error<br>");
3513			goto footer;
3514		} else {
3515			websWrite(wp, "done<br>");
3516			action = REBOOT;
3517		}
3518	}
3519
3520	/* Report stats */
3521	else if (!strcmp(value, "Stats")) {
3522		char *server = websGetVar(wp, "stats_server", nvram_safe_get("stats_server"));
3523		websWrite(wp, "Contacting %s...", server);
3524		if (sys_stats(server)) {
3525			websWrite(wp, "error<br>");
3526			goto footer;
3527		} else {
3528			websWrite(wp, "done<br>");
3529			nvram_set("stats_server", server);
3530		}
3531	}
3532	/* Radio On Off  */
3533	else if (!strcmp(value, "RadioOff")) {
3534		websWrite(wp, "Turing Off Radio...");
3535		wl_radio_onoff(wp, 1);
3536		action = NOTHING;
3537	}
3538	else if (!strcmp(value, "RadioOn")) {
3539		websWrite(wp, "Radio on...");
3540		wl_radio_onoff(wp, 0);
3541		action = NOTHING;
3542	}
3543#ifdef __CONFIG_SES__
3544	/* SES events */
3545	else if (!strcmp(value, "NewSesNW")) {
3546		websWrite(wp, "Creating SES Network");
3547		nvram_set("ses_event", "3");
3548		action = NOTHING;
3549	}
3550	else if (!strcmp(value, "OpenWindow")) {
3551		websWrite(wp, "Opening SES Window");
3552		nvram_set("ses_event", "4");
3553		action = NOTHING;
3554	}
3555	else if (!strcmp(value, "NewSesNWAndOW")) {
3556		websWrite(wp, "Creating SES Network and Opening SES Window");
3557		nvram_set("ses_event", "6");
3558		action = NOTHING;
3559	}
3560	else if (!strcmp(value, "ResetNWToDefault")) {
3561		websWrite(wp, "Restoring Network to Default");
3562		nvram_set("ses_event", "7");
3563		action = NOTHING;
3564	}
3565#endif /* __CONFIG_SES__ */
3566#ifdef __CONFIG_NAT__
3567	/* Delete connection */
3568	else if (!strcmp(value, "Delete")) {
3569		int unit;
3570		if (!(value = websGetVar(wp, "wan_unit", NULL)) ||
3571		    (unit = atoi(value)) < 0 || unit >= MAX_NVPARSE) {
3572			websWrite(wp, "Unable to delete connection, index error.");
3573			action = NOTHING;
3574		}
3575		else {
3576			struct nvram_tuple *t;
3577			char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
3578			int unit2, units = 0;
3579			/*
3580			* We cann't delete the last connection since we can't differentiate
3581			* the cases where user does not want any connection (user deletes
3582			* the last one) vs. the router is booted the first time when there
3583			* is no connection at all (where a default one is created anyway).
3584			*/
3585			for (unit2 = 0; unit2 < MAX_NVPARSE; unit2 ++) {
3586				wan_prefix(unit2, prefix);
3587				if (nvram_get(strcat_r(prefix, "unit", tmp)) && units++ > 0)
3588					break;
3589			}
3590			if (units < 2) {
3591				websWrite(wp, "Can not delete the last connection.");
3592				action = NOTHING;
3593			}
3594			else {
3595				/* set prefix */
3596				wan_prefix(unit, prefix);
3597				/* remove selected wan%d_ set */
3598				websWrite(wp, "Deleting connection...");
3599				for (t = router_defaults; t->name; t ++) {
3600					if (!strncmp(t->name, "wan_", 4))
3601						nvram_unset(strcat_r(prefix, &t->name[4], tmp));
3602				}
3603				/* fix unit number */
3604				unit2 = unit;
3605				for (; unit < MAX_NVPARSE; unit ++) {
3606					wan_prefix(unit, prefix);
3607					if (nvram_get(strcat_r(prefix, "unit", tmp)))
3608						break;
3609				}
3610				if (unit >= MAX_NVPARSE) {
3611					unit = unit2 - 1;
3612					for (; unit >= 0; unit --) {
3613						wan_prefix(unit, prefix);
3614						if (nvram_get(strcat_r(prefix, "unit", tmp)))
3615							break;
3616					}
3617				}
3618				snprintf(tmp, sizeof(tmp), "%d", unit);
3619				nvram_set("wan_unit", tmp);
3620				/* check if there is any primary connection - see comment in wan_unit() */
3621				for (unit = 0; unit < MAX_NVPARSE; unit ++) {
3622					wan_prefix(unit, prefix);
3623					if (!nvram_get(strcat_r(prefix, "unit", tmp)))
3624						continue;
3625					if (nvram_invmatch(strcat_r(prefix, "proto", tmp), "disabled") &&
3626					    nvram_match(strcat_r(prefix, "primary", tmp), "1"))
3627						break;
3628				}
3629				/* no one is primary, pick the first enabled one as primary */
3630				if (unit >= MAX_NVPARSE)
3631					wan_primary(wp);
3632				/* save the change */
3633				nvram_set("is_modified", "1");
3634				nvram_set("is_default", "0");
3635				nvram_commit();
3636				websWrite(wp, "done<br>");
3637				action = RESTART;
3638			}
3639		}
3640	}
3641#endif	/* __CONFIG_NAT__ */
3642
3643	/* Invalid action */
3644	else
3645		websWrite(wp, "Invalid action %s<br>", value);
3646
3647 footer:
3648	websWrite(wp, (char_t *) apply_footer, websGetVar(wp, "page", ""));
3649	websFooter(wp);
3650	websDone(wp, 200);
3651
3652	if (action == RESTART)
3653		sys_restart();
3654	else if (action == REBOOT)
3655		sys_reboot();
3656
3657	return 1;
3658}
3659
3660static int
3661wireless_asp(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
3662	     char_t *url, char_t *path, char_t *query)
3663{
3664	struct variable *v;
3665	char *value;
3666	char name[IFNAMSIZ], *next;
3667	int unit = 0;
3668	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
3669
3670	/* Can enter this function through GET or POST */
3671	if ((value = websGetVar(wp, "action", NULL))) {
3672		if (strcmp(value, "Select"))
3673			return apply_cgi(wp, urlPrefix, webDir, arg, url, path, query);
3674	}
3675
3676	/* copy wl%d_XXXXXXXX to wl_XXXXXXXX */
3677	if ((value = websGetVar(wp, "wl_unit", NULL))) {
3678		unit = atoi(value);
3679	} else {
3680		char ifnames[256];
3681		snprintf(ifnames, sizeof(ifnames), "%s %s",
3682			nvram_safe_get("lan_ifnames"),
3683			nvram_safe_get("wan_ifnames"));
3684		/* Probe for first wl interface */
3685		foreach(name, ifnames, next) {
3686			if (wl_probe(name) == 0 &&
3687			    wl_ioctl(name, WLC_GET_INSTANCE, &unit, sizeof(unit)) == 0)
3688				break;
3689		}
3690		if (!*name)
3691			unit = -1;
3692	}
3693	if (unit >= 0) {
3694		snprintf(prefix, sizeof(prefix), "wl%d_", unit);
3695		for (v = variables; v < &variables[ARRAYSIZE(variables)]; v++) {
3696			if (!strncmp(v->name, "wl_", 3))
3697				nvram_set(v->name, nvram_safe_get(strcat_r(prefix, &v->name[3], tmp)));
3698			if (!strncmp(v->name, "wl_unit", 7))
3699				break;
3700		}
3701	}
3702
3703	/* Set currently selected unit */
3704	snprintf(tmp, sizeof(tmp), "%d", unit);
3705	nvram_set("wl_unit", tmp);
3706
3707	/* Display the page */
3708	return websDefaultHandler(wp, urlPrefix, webDir, arg, url, path, query);
3709}
3710
3711static int
3712internal_asp(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
3713	     char_t *url, char_t *path, char_t *query)
3714{
3715	struct variable *v;
3716	char *value;
3717	char name[IFNAMSIZ], *next;
3718	int unit = 0;
3719	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
3720
3721	/* Can enter this function through GET or POST */
3722	if ((value = websGetVar(wp, "action", NULL))) {
3723		if (strcmp(value, "Select"))
3724			return apply_cgi(wp, urlPrefix, webDir, arg, url, path, query);
3725	}
3726
3727	/* copy wl%d_XXXXXXXX to wl_XXXXXXXX */
3728	if ((value = websGetVar(wp, "wl_unit", NULL))) {
3729		unit = atoi(value);
3730	} else {
3731		char ifnames[256];
3732		snprintf(ifnames, sizeof(ifnames), "%s %s",
3733			nvram_safe_get("lan_ifnames"),
3734			nvram_safe_get("wan_ifnames"));
3735		/* Probe for first wl interface */
3736		foreach(name, ifnames, next) {
3737			if (wl_probe(name) == 0 &&
3738			    wl_ioctl(name, WLC_GET_INSTANCE, &unit, sizeof(unit)) == 0)
3739				break;
3740		}
3741		if (!*name)
3742			unit = -1;
3743	}
3744	if (unit >= 0) {
3745		snprintf(prefix, sizeof(prefix), "wl%d_", unit);
3746		for (v = variables; v < &variables[ARRAYSIZE(variables)]; v++) {
3747			if (!strncmp(v->name, "wl_", 3))
3748				nvram_set(v->name, nvram_safe_get(strcat_r(prefix, &v->name[3], tmp)));
3749			if (!strncmp(v->name, "wl_unit", 7))
3750				break;
3751		}
3752	}
3753
3754	/* Set currently selected unit */
3755	snprintf(tmp, sizeof(tmp), "%d", unit);
3756	nvram_set("wl_unit", tmp);
3757
3758	/* Display the page */
3759	return (websDefaultHandler(wp, urlPrefix, webDir, arg, url, path, query));
3760}
3761
3762static int
3763security_asp(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
3764	     char_t *url, char_t *path, char_t *query)
3765{
3766	struct variable *v;
3767	char *value;
3768	char name[IFNAMSIZ], *next;
3769	int unit = 0;
3770	char tmp[NVRAM_BUFSIZE], prefix[] = "wlXXXXXXXXXX_";
3771
3772	/* Can enter this function through GET or POST */
3773	if ((value = websGetVar(wp, "action", NULL))) {
3774		if (strcmp(value, "Select"))
3775			return apply_cgi(wp, urlPrefix, webDir, arg, url, path, query);
3776	}
3777
3778	/* copy wl%d_XXXXXXXX to wl_XXXXXXXX */
3779	if ((value = websGetVar(wp, "wl_unit", NULL))) {
3780		unit = atoi(value);
3781	} else {
3782		char ifnames[256];
3783		snprintf(ifnames, sizeof(ifnames), "%s %s",
3784			nvram_safe_get("lan_ifnames"),
3785			nvram_safe_get("wan_ifnames"));
3786		/* Probe for first wl interface */
3787		foreach(name, ifnames, next) {
3788			if (wl_probe(name) == 0 &&
3789			    wl_ioctl(name, WLC_GET_INSTANCE, &unit, sizeof(unit)) == 0)
3790				break;
3791		}
3792		if (!*name)
3793			unit = -1;
3794	}
3795	if (unit >= 0) {
3796		snprintf(prefix, sizeof(prefix), "wl%d_", unit);
3797		for (v = variables; v < &variables[ARRAYSIZE(variables)]; v++) {
3798			if (!strncmp(v->name, "wl_", 3))
3799				nvram_set(v->name, nvram_safe_get(strcat_r(prefix, &v->name[3], tmp)));
3800			if (!strncmp(v->name, "wl_unit", 7))
3801				break;
3802		}
3803	}
3804
3805	/* Set currently selected unit */
3806	snprintf(tmp, sizeof(tmp), "%d", unit);
3807	nvram_set("wl_unit", tmp);
3808
3809	/* Display the page */
3810	return websDefaultHandler(wp, urlPrefix, webDir, arg, url, path, query);
3811}
3812
3813#ifdef __CONFIG_NAT__
3814static int
3815wan_asp(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
3816	     char_t *url, char_t *path, char_t *query)
3817{
3818	struct variable *v;
3819	char *value;
3820	int unit = 0;
3821	char tmp[NVRAM_BUFSIZE], prefix[] = "wanXXXXXXXXXX_";
3822	char ustr[16];
3823	struct nvram_tuple *t;
3824
3825	/* Can enter this function through GET or POST */
3826	if ((value = websGetVar(wp, "action", NULL))) {
3827		if (!strcmp(value, "New")) {
3828			/* pick one that 'does not' exist */
3829			for (unit = 0; unit < MAX_NVPARSE; unit ++) {
3830				wan_prefix(unit, prefix);
3831				if (!nvram_get(strcat_r(prefix, "unit", tmp)))
3832					break;
3833			}
3834			if (unit >= MAX_NVPARSE) {
3835				websHeader(wp);
3836				websWrite(wp, (char_t *) apply_header);
3837				websWrite(wp, "Unable to create new connection. Maximum %d.", MAX_NVPARSE);
3838				websWrite(wp, (char_t *) apply_footer, websGetVar(wp, "page", ""));
3839				websFooter(wp);
3840				websDone(wp, 200);
3841				return 1;
3842			}
3843			/* copy default to newly created wan%d_ set */
3844			for (t = router_defaults; t->name; t ++) {
3845				if (!strncmp(t->name, "wan_", 4))
3846					nvram_set(strcat_r(prefix, &t->name[4], tmp), t->value);
3847			}
3848			/* the following variables must be overwritten */
3849			snprintf(ustr, sizeof(ustr), "%d", unit);
3850			nvram_set(strcat_r(prefix, "unit", tmp), ustr);
3851			nvram_set(strcat_r(prefix, "proto", tmp), "disabled");
3852			nvram_set(strcat_r(prefix, "ifname", tmp), nvram_safe_get("wan_ifname"));
3853			nvram_set(strcat_r(prefix, "hwaddr", tmp), nvram_safe_get("wan_hwaddr"));
3854			nvram_set(strcat_r(prefix, "ifnames", tmp), nvram_safe_get("wan_ifnames"));
3855			/* commit change */
3856			nvram_set("is_modified", "1");
3857			nvram_set("is_default", "0");
3858			nvram_commit();
3859		}
3860		else if (!strcmp(value, "Select")) {
3861			if ((value = websGetVar(wp, "wan_unit", NULL)))
3862				unit = atoi(value);
3863			else
3864				unit = -1;
3865		}
3866		else
3867			return apply_cgi(wp, urlPrefix, webDir, arg, url, path, query);
3868	}
3869	else if ((value = websGetVar(wp, "wan_unit", NULL)))
3870		unit = atoi(value);
3871	else
3872		unit = atoi(nvram_safe_get("wan_unit"));
3873	if (unit < 0 || unit >= MAX_NVPARSE)
3874		unit = 0;
3875
3876	/* Set prefix */
3877	wan_prefix(unit, prefix);
3878
3879	/* copy wan%d_ set to wan_ set */
3880	for (v = variables; v < &variables[ARRAYSIZE(variables)]; v++) {
3881		if (strncmp(v->name, "wan_", 4))
3882			continue;
3883		value = nvram_get(strcat_r(prefix, &v->name[4], tmp));
3884		if (value)
3885			nvram_set(v->name, value);
3886		if (!strncmp(v->name, "wan_unit", 8))
3887			break;
3888	}
3889
3890	/* Set currently selected unit */
3891	snprintf(tmp, sizeof(tmp), "%d", unit);
3892	nvram_set("wan_unit", tmp);
3893
3894	/* Display the page */
3895	return websDefaultHandler(wp, urlPrefix, webDir, arg, url, path, query);
3896}
3897#endif	/* __CONFIG_NAT__ */
3898
3899#ifdef WEBS
3900
3901void
3902initHandlers(void)
3903{
3904	websAspDefine("nvram_get", ej_nvram_get);
3905	websAspDefine("nvram_match", ej_nvram_match);
3906	websAspDefine("nvram_invmatch", ej_nvram_invmatch);
3907	websAspDefine("nvram_list", ej_nvram_list);
3908	websAspDefine("filter_client", ej_filter_client);
3909	websAspDefine("forward_port", ej_forward_port);
3910	websAspDefine("static_route", ej_static_route);
3911	websAspDefine("localtime", ej_localtime);
3912	websAspDefine("dumplog", ej_dumplog);
3913	websAspDefine("syslog", ej_syslog);
3914	websAspDefine("dumpleases", ej_dumpleases);
3915	websAspDefine("link", ej_link);
3916	websAspDefine("wme_match_op", ej_wme_match_op);
3917
3918	websUrlHandlerDefine("/apply.cgi", NULL, 0, apply_cgi, 0);
3919	websUrlHandlerDefine("/wireless.asp", NULL, 0, wireless_asp, 0);
3920
3921	websSetPassword(nvram_safe_get("http_passwd"));
3922
3923	websSetRealm("Broadcom Home Gateway Reference Design");
3924}
3925
3926#else /* !WEBS */
3927
3928static void
3929do_auth(char *userid, char *passwd, char *realm)
3930{
3931	strncpy(userid, nvram_safe_get("http_username"), AUTH_MAX);
3932	strncpy(passwd, nvram_safe_get("http_passwd"), AUTH_MAX);
3933	strncpy(realm, "Broadcom Home Gateway Reference Design", AUTH_MAX);
3934}
3935
3936char post_buf[POST_BUF_SIZE];
3937char ezc_version[128];
3938char no_cache[] =
3939"Cache-Control: no-cache\r\n"
3940"Pragma: no-cache\r\n"
3941"Expires: 0"
3942;
3943
3944
3945static void
3946do_apply_post(char *url, FILE *stream, int len, char *boundary)
3947{
3948	/* Get query */
3949	if (!fgets(post_buf, MIN(len + 1, sizeof(post_buf)), stream))
3950		return;
3951	len -= strlen(post_buf);
3952
3953	/* Initialize CGI */
3954	init_cgi(post_buf);
3955
3956	/* Slurp anything remaining in the request */
3957	while (len--)
3958		(void) fgetc(stream);
3959}
3960
3961static void
3962do_apply_cgi(char *url, FILE *stream)
3963{
3964	char *path, *query;
3965
3966	/* Parse path */
3967	query = url;
3968	path = strsep(&query, "?") ? : url;
3969
3970	apply_cgi(stream, NULL, NULL, 0, url, path, query);
3971
3972	/* Reset CGI */
3973	init_cgi(NULL);
3974}
3975
3976static int upgrade_ret;
3977
3978static void
3979do_upgrade_post(char *url, FILE *stream, int len, char *boundary)
3980{
3981	char buf[1024];
3982
3983	upgrade_ret = EINVAL;
3984
3985	/* Look for our part */
3986	while (len > 0) {
3987		if (!fgets(buf, MIN(len + 1, sizeof(buf)), stream))
3988			return;
3989		len -= strlen(buf);
3990		if (!strncasecmp(buf, "Content-Disposition:", 20) &&
3991		    strstr(buf, "name=\"file\""))
3992			break;
3993	}
3994
3995	/* Skip boundary and headers */
3996	while (len > 0) {
3997		if (!fgets(buf, MIN(len + 1, sizeof(buf)), stream))
3998			return;
3999		len -= strlen(buf);
4000		if (!strcmp(buf, "\n") || !strcmp(buf, "\r\n"))
4001			break;
4002	}
4003
4004	upgrade_ret = sys_upgrade(NULL, stream, &len);
4005
4006	/* Slurp anything remaining in the request */
4007	while (len--)
4008		(void) fgetc(stream);
4009}
4010
4011static void
4012do_upgrade_cgi(char *url, FILE *stream)
4013{
4014	websHeader(stream);
4015	websWrite(stream, (char_t *) apply_header);
4016
4017	/* We could probably be more informative here... */
4018	if (upgrade_ret)
4019		websWrite(stream, "Error during upgrade<br>");
4020	else
4021		websWrite(stream, "Upgrade complete. Rebooting...<br>");
4022
4023	websWrite(stream, (char_t *) apply_footer, "firmware.asp");
4024	websFooter(stream);
4025	websDone(stream, 200);
4026
4027	/* Reboot if successful */
4028	if (upgrade_ret == 0)
4029		sys_reboot();
4030}
4031
4032static void
4033do_wireless_asp(char *url, FILE *stream)
4034{
4035	char *path, *query;
4036
4037	/* Parse path */
4038	query = url;
4039	path = strsep(&query, "?") ? : url;
4040
4041	wireless_asp(stream, NULL, NULL, 0, url, path, query);
4042
4043	/* Reset CGI */
4044	init_cgi(NULL);
4045}
4046
4047static void
4048do_security_asp(char *url, FILE *stream)
4049{
4050	char *path, *query;
4051
4052	/* Parse path */
4053	query = url;
4054	path = strsep(&query, "?") ? : url;
4055
4056	security_asp(stream, NULL, NULL, 0, url, path, query);
4057
4058	/* Reset CGI */
4059	init_cgi(NULL);
4060}
4061
4062static void
4063do_internal_asp(char *url, FILE *stream)
4064{
4065	char *path, *query;
4066
4067	/* Parse path */
4068	query = url;
4069	path = strsep(&query, "?") ? : url;
4070
4071	internal_asp(stream, NULL, NULL, 0, url, path, query);
4072
4073	/* Reset CGI */
4074	init_cgi(NULL);
4075}
4076
4077#ifdef __CONFIG_NAT__
4078static void
4079do_wan_asp(char *url, FILE *stream)
4080{
4081	char *path, *query;
4082
4083	/* Parse path */
4084	query = url;
4085	path = strsep(&query, "?") ? : url;
4086
4087	wan_asp(stream, NULL, NULL, 0, url, path, query);
4088
4089	/* Reset CGI */
4090	init_cgi(NULL);
4091}
4092#endif	/* __CONFIG_NAT__ */
4093
4094struct mime_handler mime_handlers[] = {
4095#ifdef __CONFIG_NAT__
4096	{ "wan.asp", "text/html", no_cache, do_apply_post, do_wan_asp, do_auth },
4097#endif	/* __CONFIG_NAT__ */
4098	{ "wireless.asp", "text/html", no_cache, do_apply_post, do_wireless_asp, do_auth },
4099	{ "security.asp", "text/html", no_cache, do_apply_post, do_security_asp, do_auth },
4100	{ "internal.asp", "text/html", no_cache, do_apply_post, do_internal_asp, do_auth },
4101#ifdef __CONFIG_EZC__
4102	{ "ezconfig.asp", "text/html", ezc_version, do_apply_ezconfig_post, do_ezconfig_asp, do_auth },
4103#endif /* __CONFIG_EZC__ */
4104	{ "**.asp", "text/html", no_cache, NULL, do_ej, do_auth },
4105	{ "**.css", "text/css", NULL, NULL, do_file, do_auth },
4106	{ "**.gif", "image/gif", NULL, NULL, do_file, do_auth },
4107	{ "**.jpg", "image/jpeg", NULL, NULL, do_file, do_auth },
4108	{ "**.js", "text/javascript", NULL, NULL, do_file, do_auth },
4109	{ "apply.cgi*", "text/html", no_cache, do_apply_post, do_apply_cgi, do_auth },
4110	{ "upgrade.cgi*", "text/html", no_cache, do_upgrade_post, do_upgrade_cgi, do_auth },
4111	{ NULL, NULL, NULL, NULL, NULL, NULL }
4112};
4113
4114struct ej_handler ej_handlers[] = {
4115	{ "nvram_get", ej_nvram_get },
4116	{ "nvram_match", ej_nvram_match },
4117	{ "nvram_invmatch", ej_nvram_invmatch },
4118	{ "nvram_list", ej_nvram_list },
4119	{ "nvram_inlist", ej_nvram_inlist },
4120	{ "nvram_invinlist", ej_nvram_invinlist },
4121#ifdef __CONFIG_NAT__
4122	{ "wan_list", ej_wan_list },
4123	{ "wan_iflist", ej_wan_iflist },
4124	{ "wan_route", ej_wan_route },
4125	{ "wan_link", ej_wan_link },
4126	{ "wan_lease", ej_wan_lease },
4127	{ "filter_client", ej_filter_client },
4128	{ "forward_port", ej_forward_port },
4129	{ "autofw_port", ej_autofw_port },
4130#endif	/*  __CONFIG_NAT__ */
4131	{ "localtime", ej_localtime },
4132	{ "sysuptime", ej_sysuptime },
4133	{ "dumplog", ej_dumplog },
4134	{ "syslog", ej_syslog },
4135	{ "wl_list", ej_wl_list },
4136	{ "wl_phytypes", ej_wl_phytypes },
4137	{ "wl_radioid", ej_wl_radioid },
4138	{ "wl_corerev", ej_wl_corerev },
4139	{ "wl_cur_channel", ej_wl_cur_channel },
4140	{ "wl_cur_phytype", ej_wl_cur_phytype },
4141	{ "wl_cur_country", ej_wl_cur_country },
4142	{ "wl_country_list", ej_wl_country_list },
4143	{ "wl_channel_list", ej_wl_channel_list },
4144	{ "wl_auth_list", ej_wl_auth_list },
4145	{ "wl_mode_list", ej_wl_mode_list },
4146	{ "wl_inlist", ej_wl_inlist },
4147	{ "wl_wds_status", ej_wl_wds_status },
4148	{ "wl_radio_roam_option", ej_wl_radio_roam_option},
4149	{ "ses_button_display", ej_ses_button_display},
4150	{ "lan_route", ej_lan_route },
4151	{ "lan_leases", ej_lan_leases },
4152	{ "asp_list", ej_asp_list },
4153	{ "wme_match_op", ej_wme_match_op },
4154	{ NULL, NULL }
4155};
4156
4157#endif /* !WEBS */
4158
4159/*
4160 * Country names and abbreviations from ISO 3166
4161 */
4162country_name_t country_names[] = {
4163
4164{"AFGHANISTAN",		 "AF"},
4165{"ALBANIA",		 "AL"},
4166{"ALGERIA",		 "DZ"},
4167{"AMERICAN SAMOA", 	 "AS"},
4168{"ANDORRA",		 "AD"},
4169{"ANGOLA",		 "AO"},
4170{"ANGUILLA",		 "AI"},
4171{"ANTARCTICA",		 "AQ"},
4172{"ANTIGUA AND BARBUDA",	 "AG"},
4173{"ARGENTINA",		 "AR"},
4174{"ARMENIA",		 "AM"},
4175{"ARUBA",		 "AW"},
4176{"AUSTRALIA",		 "AU"},
4177{"AUSTRIA",		 "AT"},
4178{"AZERBAIJAN",		 "AZ"},
4179{"BAHAMAS",		 "BS"},
4180{"BAHRAIN",		 "BH"},
4181{"BANGLADESH",		 "BD"},
4182{"BARBADOS",		 "BB"},
4183{"BELARUS",		 "BY"},
4184{"BELGIUM",		 "BE"},
4185{"BELIZE",		 "BZ"},
4186{"BENIN",		 "BJ"},
4187{"BERMUDA",		 "BM"},
4188{"BHUTAN",		 "BT"},
4189{"BOLIVIA",		 "BO"},
4190{"BOSNIA AND HERZEGOVINA","BA"},
4191{"BOTSWANA",		 "BW"},
4192{"BOUVET ISLAND",	 "BV"},
4193{"BRAZIL",		 "BR"},
4194{"BRITISH INDIAN OCEAN TERRITORY", 	"IO"},
4195{"BRUNEI DARUSSALAM",	 "BN"},
4196{"BULGARIA",		 "BG"},
4197{"BURKINA FASO",	 "BF"},
4198{"BURUNDI",		 "BI"},
4199{"CAMBODIA",		 "KH"},
4200{"CAMEROON",		 "CM"},
4201{"CANADA",		 "CA"},
4202{"CAPE VERDE",		 "CV"},
4203{"CAYMAN ISLANDS",	 "KY"},
4204{"CENTRAL AFRICAN REPUBLIC","CF"},
4205{"CHAD",		 "TD"},
4206{"CHILE",		 "CL"},
4207{"CHINA",		 "CN"},
4208{"CHRISTMAS ISLAND",	 "CX"},
4209{"COCOS (KEELING) ISLANDS","CC"},
4210{"COLOMBIA",		 "CO"},
4211{"COMOROS",		 "KM"},
4212{"CONGO",		 "CG"},
4213{"CONGO, THE DEMOCRATIC REPUBLIC OF THE", "CD"},
4214{"COOK ISLANDS",	 "CK"},
4215{"COSTA RICA",		 "CR"},
4216{"COTE D'IVOIRE",	 "CI"},
4217{"CROATIA",		 "HR"},
4218{"CUBA",		 "CU"},
4219{"CYPRUS",		 "CY"},
4220{"CZECH REPUBLIC",	 "CZ"},
4221{"DENMARK",		 "DK"},
4222{"DJIBOUTI",		 "DJ"},
4223{"DOMINICA",		 "DM"},
4224{"DOMINICAN REPUBLIC", 	 "DO"},
4225{"ECUADOR",		 "EC"},
4226{"EGYPT",		 "EG"},
4227{"EL SALVADOR",		 "SV"},
4228{"EQUATORIAL GUINEA",	 "GQ"},
4229{"ERITREA",		 "ER"},
4230{"ESTONIA",		 "EE"},
4231{"ETHIOPIA",		 "ET"},
4232{"FALKLAND ISLANDS (MALVINAS)",	"FK"},
4233{"FAROE ISLANDS",	 "FO"},
4234{"FIJI",		 "FJ"},
4235{"FINLAND",		 "FI"},
4236{"FRANCE",		 "FR"},
4237{"FRENCH GUIANA",	 "GF"},
4238{"FRENCH POLYNESIA",	 "PF"},
4239{"FRENCH SOUTHERN TERRITORIES",	 "TF"},
4240{"GABON",		 "GA"},
4241{"GAMBIA",		 "GM"},
4242{"GEORGIA",		 "GE"},
4243{"GERMANY",		 "DE"},
4244{"GHANA",		 "GH"},
4245{"GIBRALTAR",		 "GI"},
4246{"GREECE",		 "GR"},
4247{"GREENLAND",		 "GL"},
4248{"GRENADA",		 "GD"},
4249{"GUADELOUPE",		 "GP"},
4250{"GUAM",		 "GU"},
4251{"GUATEMALA",		 "GT"},
4252{"GUINEA",		 "GN"},
4253{"GUINEA-BISSAU",	 "GW"},
4254{"GUYANA",		 "GY"},
4255{"HAITI",		 "HT"},
4256{"HEARD ISLAND AND MCDONALD ISLANDS",	"HM"},
4257{"HOLY SEE (VATICAN CITY STATE)", 	"VA"},
4258{"HONDURAS",		 "HN"},
4259{"HONG KONG",		 "HK"},
4260{"HUNGARY",		 "HU"},
4261{"ICELAND",		 "IS"},
4262{"INDIA",		 "IN"},
4263{"INDONESIA",		 "ID"},
4264{"IRAN, ISLAMIC REPUBLIC OF",		"IR"},
4265{"IRAQ",		 "IQ"},
4266{"IRELAND",		 "IE"},
4267{"ISRAEL",		 "IL"},
4268{"ITALY",		 "IT"},
4269{"JAMAICA",		 "JM"},
4270{"JAPAN",		 "JP"},
4271{"JORDAN",		 "JO"},
4272{"KAZAKHSTAN",		 "KZ"},
4273{"KENYA",		 "KE"},
4274{"KIRIBATI",		 "KI"},
4275{"KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF", "KP"},
4276{"KOREA, REPUBLIC OF",	 "KR"},
4277{"KUWAIT",		 "KW"},
4278{"KYRGYZSTAN",		 "KG"},
4279{"LAO PEOPLE'S DEMOCRATIC REPUBLIC", 	"LA"},
4280{"LATVIA",		 "LV"},
4281{"LEBANON",		 "LB"},
4282{"LESOTHO",		 "LS"},
4283{"LIBERIA",		 "LR"},
4284{"LIBYAN ARAB JAMAHIRIYA","LY"},
4285{"LIECHTENSTEIN",	 "LI"},
4286{"LITHUANIA",		 "LT"},
4287{"LUXEMBOURG",		 "LU"},
4288{"MACAO",		 "MO"},
4289{"MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF",	 "MK"},
4290{"MADAGASCAR",		 "MG"},
4291{"MALAWI",		 "MW"},
4292{"MALAYSIA",		 "MY"},
4293{"MALDIVES",		 "MV"},
4294{"MALI",		 "ML"},
4295{"MALTA",		 "MT"},
4296{"MARSHALL ISLANDS",	 "MH"},
4297{"MARTINIQUE",		 "MQ"},
4298{"MAURITANIA",		 "MR"},
4299{"MAURITIUS",		 "MU"},
4300{"MAYOTTE",		 "YT"},
4301{"MEXICO",		 "MX"},
4302{"MICRONESIA, FEDERATED STATES OF", 	"FM"},
4303{"MOLDOVA, REPUBLIC OF", "MD"},
4304{"MONACO",		 "MC"},
4305{"MONGOLIA",		 "MN"},
4306{"MONTSERRAT",		 "MS"},
4307{"MOROCCO",		 "MA"},
4308{"MOZAMBIQUE",		 "MZ"},
4309{"MYANMAR",		 "MM"},
4310{"NAMIBIA",		 "NA"},
4311{"NAURU",		 "NR"},
4312{"NEPAL",		 "NP"},
4313{"NETHERLANDS",		 "NL"},
4314{"NETHERLANDS ANTILLES", "AN"},
4315{"NEW CALEDONIA",	 "NC"},
4316{"NEW ZEALAND",		 "NZ"},
4317{"NICARAGUA",		 "NI"},
4318{"NIGER",		 "NE"},
4319{"NIGERIA",		 "NG"},
4320{"NIUE",		 "NU"},
4321{"NORFOLK ISLAND",	 "NF"},
4322{"NORTHERN MARIANA ISLANDS","MP"},
4323{"NORWAY",		 "NO"},
4324{"OMAN",		 "OM"},
4325{"PAKISTAN",		 "PK"},
4326{"PALAU",		 "PW"},
4327{"PALESTINIAN TERRITORY, OCCUPIED", 	"PS"},
4328{"PANAMA",		 "PA"},
4329{"PAPUA NEW GUINEA",	 "PG"},
4330{"PARAGUAY",		 "PY"},
4331{"PERU",		 "PE"},
4332{"PHILIPPINES",		 "PH"},
4333{"PITCAIRN",		 "PN"},
4334{"POLAND",		 "PL"},
4335{"PORTUGAL",		 "PT"},
4336{"PUERTO RICO",		 "PR"},
4337{"QATAR",		 "QA"},
4338{"REUNION",		 "RE"},
4339{"ROMANIA",		 "RO"},
4340{"RUSSIAN FEDERATION",	 "RU"},
4341{"RWANDA",		 "RW"},
4342{"SAINT HELENA",	 "SH"},
4343{"SAINT KITTS AND NEVIS","KN"},
4344{"SAINT LUCIA",		 "LC"},
4345{"SAINT PIERRE AND MIQUELON",	 	"PM"},
4346{"SAINT VINCENT AND THE GRENADINES", 	"VC"},
4347{"SAMOA",		 "WS"},
4348{"SAN MARINO",		 "SM"},
4349{"SAO TOME AND PRINCIPE","ST"},
4350{"SAUDI ARABIA",	 "SA"},
4351{"SENEGAL",		 "SN"},
4352{"SEYCHELLES",		 "SC"},
4353{"SIERRA LEONE",	 "SL"},
4354{"SINGAPORE",		 "SG"},
4355{"SLOVAKIA",		 "SK"},
4356{"SLOVENIA",		 "SI"},
4357{"SOLOMON ISLANDS",	 "SB"},
4358{"SOMALIA",		 "SO"},
4359{"SOUTH AFRICA",	 "ZA"},
4360{"SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS", "GS"},
4361{"SPAIN",		 "ES"},
4362{"SRI LANKA",		 "LK"},
4363{"SUDAN",		 "SD"},
4364{"SURINAME",		 "SR"},
4365{"SVALBARD AND JAN MAYEN","SJ"},
4366{"SWAZILAND",		 "SZ"},
4367{"SWEDEN",		 "SE"},
4368{"SWITZERLAND",		 "CH"},
4369{"SYRIAN ARAB REPUBLIC", "SY"},
4370{"TAIWAN, PROVINCE OF CHINA", 		"TW"},
4371{"TAJIKISTAN",		 "TJ"},
4372{"TANZANIA, UNITED REPUBLIC OF",	"TZ"},
4373{"THAILAND",		 "TH"},
4374{"TIMOR-LESTE",		 "TL"},
4375{"TOGO",		 "TG"},
4376{"TOKELAU",		 "TK"},
4377{"TONGA",		 "TO"},
4378{"TRINIDAD AND TOBAGO",	 "TT"},
4379{"TUNISIA",		 "TN"},
4380{"TURKEY",		 "TR"},
4381{"TURKMENISTAN",	 "TM"},
4382{"TURKS AND CAICOS ISLANDS",		"TC"},
4383{"TUVALU",		 "TV"},
4384{"UGANDA",		 "UG"},
4385{"UKRAINE",		 "UA"},
4386{"UNITED ARAB EMIRATES", "AE"},
4387{"UNITED KINGDOM",	 "GB"},
4388{"UNITED STATES",	 "US"},
4389{"UNITED STATES MINOR OUTLYING ISLANDS","UM"},
4390{"URUGUAY",		 "UY"},
4391{"UZBEKISTAN",		 "UZ"},
4392{"VANUATU",		 "VU"},
4393{"VENEZUELA",		 "VE"},
4394{"VIET NAM",		 "VN"},
4395{"VIRGIN ISLANDS, BRITISH", "VG"},
4396{"VIRGIN ISLANDS, U.S.", "VI"},
4397{"WALLIS AND FUTUNA",	 "WF"},
4398{"WESTERN SAHARA", 	 "EH"},
4399{"YEMEN",		 "YE"},
4400{"YUGOSLAVIA",		 "YU"},
4401{"ZAMBIA",		 "ZM"},
4402{"ZIMBABWE",		 "ZW"},
4403{"ALL",		 	 "ALL"},
4404{NULL, 			 NULL}
4405};
4406