1/*
2 * udhcpc scripts
3 *
4 * Copyright (C) 2009, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: udhcpc.c,v 1.27 2009/12/02 20:06:40 Exp $
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
27#include <errno.h>
28#include <time.h>
29#include <unistd.h>
30#include <net/ethernet.h>
31
32#include <bcmnvram.h>
33#include <shutils.h>
34#include <rc.h>
35
36/* Support for Domain Search List */
37#undef DHCP_RFC3397
38
39/* returns: length of hex value
40 * dest size must be lagre enough to accept n bytes from
41   src in hex representation plus one \0 byte */
42static int
43bin2hex(char *dest, size_t size, const void *src, size_t n)
44{
45	unsigned char *sptr = (unsigned char *) src;
46	unsigned char *send = sptr + n;
47	char *dptr = dest;
48
49	while (sptr < send && size > 2) {
50		n = snprintf(dptr, size, "%02x", *sptr++);
51		dptr += n;
52		size -= n;
53	}
54	return dptr - dest;
55}
56
57#ifdef RTCONFIG_TR069
58/* returns:
59 *  NULL on NULL value or alloc error
60 *  binary representation of hex value on success
61 * if size if specified, returns actual size of parsed value
62 * note: value is alloced and needs to be free() */
63static char *
64hex2bin(const char *value, size_t *size)
65{
66	char buf[3], *src, *tmp;
67	unsigned char *dst, *ptr;
68
69	if (!(src = (char *) value) ||
70	    !(dst = calloc(1, strlen(src)/2 + 1)))
71		return NULL;
72
73	memset(buf, 0, sizeof(buf));
74	for (ptr = dst; src[0] && src[1]; ptr++) {
75		buf[0] = *src++;
76		buf[1] = *src++;
77		*ptr = strtoul(buf, &tmp, 16);
78		if (tmp == buf || *tmp)
79			break;
80	}
81	if (size)
82		*size = ptr - dst;
83
84	return (char *) dst;
85}
86#endif
87
88/* set nvram to value
89 * returns:
90 *  0 if not changed
91 *  1 if new/changed/removed */
92static int
93nvram_set_check(const char *name, const char *value)
94{
95	char *nvalue = nvram_get(name);
96
97	if (nvalue == value || strcmp(nvalue ? : "", value ? : "") == 0)
98		return 0;
99
100	nvram_set(name, value);
101	return 1;
102}
103
104/* set nvram to env value
105 * returns:
106 *  0 if not changed
107 *  1 if new/changed/removed */
108static int
109nvram_set_env(const char *name, const char *env)
110{
111	char *evalue = getenv(env);
112
113	if (evalue)
114		evalue = trim_r(evalue);
115
116	return nvram_set_check(name, evalue);
117}
118
119struct viopt_hdr {
120	unsigned int entnum;
121	unsigned char len;
122	unsigned char data[0];
123} __attribute__ ((__packed__));
124
125struct opt_hdr {
126	unsigned char id;
127	unsigned char len;
128	unsigned char data[0];
129} __attribute__ ((__packed__));
130
131#ifdef RTCONFIG_TR069
132#ifdef RTCONFIG_TR181
133static struct viopt_hdr *
134viopt_get(const void *buf, size_t size, unsigned int entnum)
135{
136	struct viopt_hdr *opt;
137	unsigned char *ptr = (unsigned char *) buf;
138	unsigned char *end = (unsigned char *) buf + size;
139
140	while (ptr + sizeof(*opt) <= end) {
141		opt = (struct viopt_hdr *) ptr;
142		if ((ptr += sizeof(*opt) + opt->len) > end)
143			break;
144		if (opt->entnum == entnum)
145			return opt;
146	}
147
148	return NULL;
149}
150#endif
151
152static struct opt_hdr *
153opt_get(const void *buf, size_t size, unsigned char id)
154{
155	struct opt_hdr *opt;
156	unsigned char *ptr = (unsigned char *) buf;
157	unsigned char *end = (unsigned char *) buf + size;
158
159	while (ptr + sizeof(*opt) <= end) {
160		opt = (struct opt_hdr *) ptr;
161		if (opt->id == 0) {
162			ptr++;
163			continue;
164		} else if (opt->id == 255)
165			break;
166		if ((ptr += sizeof(*opt) + opt->len) > end)
167			break;
168		if (opt->id == id)
169			return opt;
170	}
171
172	return NULL;
173}
174
175static char
176*stropt(const struct opt_hdr *opt, char *buf)
177{
178	strncpy(buf, (char *) opt->data, opt->len);
179	buf[opt->len] = '\0';
180	return buf;
181}
182
183#ifdef RTCONFIG_TR181
184static int
185opt_add(const void *buf, size_t size, unsigned char id, void *data, unsigned char len)
186{
187	struct opt_hdr *opt = (struct opt_hdr *) buf;
188
189	if (size >= sizeof(*opt) + len) {
190		opt->id = id;
191		opt->len = len;
192		memcpy(opt->data, data, len);
193		return sizeof(*opt) + len;
194	}
195
196	return 0;
197}
198#endif
199#endif
200
201struct duid {
202	uint16_t type;
203	uint16_t hwtype;
204	unsigned char ea[ETHER_ADDR_LEN];
205} __attribute__ ((__packed__));
206
207/* Generate DUID-LL */
208int get_duid(struct duid *duid)
209{
210	if (!duid || !ether_atoe(get_lan_hwaddr(), duid->ea))
211		return 0;
212
213	duid->type = htons(3);		/* DUID-LL */
214	duid->hwtype = htons(1);	/* Ethernet */
215
216	return ETHER_ADDR_LEN;
217}
218
219static int
220expires(char *wan_ifname, unsigned int in)
221{
222	char tmp[100], prefix[sizeof("wanXXXXXXXXXX_")];
223	int unit;
224	time_t now;
225	FILE *fp;
226
227	if ((unit = wan_prefix(wan_ifname, prefix)) < 0)
228		return -1;
229	if (wan_ifunit(wan_ifname) < 0)
230		snprintf(prefix, sizeof(prefix), "wan%d_x", unit);
231
232	nvram_set_int(strcat_r(prefix, "expires", tmp), (unsigned int) uptime() + in);
233
234	snprintf(tmp, sizeof(tmp), "/tmp/udhcpc%d.expires", unit);
235	if ((fp = fopen(tmp, "w")) == NULL) {
236		perror(tmp);
237		return errno;
238	}
239	time(&now);
240	fprintf(fp, "%d", (unsigned int) now + in);
241	fclose(fp);
242
243	return 0;
244}
245
246/*
247 * deconfig: This argument is used when udhcpc starts, and when a
248 * leases is lost. The script should put the interface in an up, but
249 * deconfigured state.
250*/
251static int
252deconfig(int zcip)
253{
254	char *wan_ifname = safe_getenv("interface");
255	char tmp[100], prefix[sizeof("wanXXXXXXXXXX_")];
256	int unit = wan_ifunit(wan_ifname);
257	int end_wan_sbstate = WAN_STOPPED_REASON_DHCP_DECONFIG;
258
259	/* Figure out nvram variable name prefix for this i/f */
260	if (wan_prefix(wan_ifname, prefix) < 0)
261		return -1;
262	if ((unit < 0) &&
263	    (nvram_match(strcat_r(prefix, "proto", tmp), "l2tp") ||
264	     nvram_match(strcat_r(prefix, "proto", tmp), "pptp"))) {
265		logmessage(zcip ? "zcip client" : "dhcp client", "skipping resetting IP address to 0.0.0.0");
266	} else
267		ifconfig(wan_ifname, IFUP, "0.0.0.0", NULL);
268
269	expires(wan_ifname, 0);
270	wan_down(wan_ifname);
271
272#if (defined(RTCONFIG_JFFS2) || defined(RTCONFIG_BRCM_NAND_JFFS2) || defined(RTCONFIG_UBIFS))
273	if(nvram_get_int(strcat_r(prefix, "sbstate_t", tmp)) == WAN_STOPPED_REASON_DATALIMIT)
274		end_wan_sbstate = WAN_STOPPED_REASON_DATALIMIT;
275#endif
276
277	/* Skip physical VPN subinterface */
278	if (!(unit < 0))
279		update_wan_state(prefix, WAN_STATE_STOPPED, end_wan_sbstate);
280
281	_dprintf("udhcpc:: %s done\n", __FUNCTION__);
282	return 0;
283}
284
285/*
286 * bound: This argument is used when udhcpc moves from an unbound, to
287 * a bound state. All of the paramaters are set in enviromental
288 * variables, The script should configure the interface, and set any
289 * other relavent parameters (default gateway, dns server, etc).
290*/
291static int
292bound(void)
293{
294	char *wan_ifname = safe_getenv("interface");
295	char *value, *gateway;
296	char tmp[100], prefix[sizeof("wanXXXXXXXXXX_")];
297	char wanprefix[sizeof("wanXXXXXXXXXX_")];
298	int unit, ifunit;
299	int changed = 0;
300#ifdef RTCONFIG_TR069
301	size_t size = 0;
302#endif
303
304	/* Figure out nvram variable name prefix for this i/f */
305	if ((ifunit = wan_prefix(wan_ifname, wanprefix)) < 0)
306		return -1;
307	if ((unit = wan_ifunit(wan_ifname)) < 0)
308		snprintf(prefix, sizeof(prefix), "wan%d_x", ifunit);
309	else	snprintf(prefix, sizeof(prefix), "wan%d_", ifunit);
310
311	/* Stop zcip to avoid races */
312	stop_zcip(ifunit);
313
314	changed += nvram_set_env(strcat_r(prefix, "ipaddr", tmp), "ip");
315#if defined(RTCONFIG_USB_MODEM) && defined(RTCONFIG_INTERNAL_GOBI)
316	if (get_dualwan_by_unit(ifunit) == WANS_DUALWAN_IF_USB &&
317	    nvram_match("usb_modem_act_type", "gobi")) {
318		changed += nvram_set_check(strcat_r(prefix, "netmask", tmp), "255.255.255.255");
319		if ((gateway = getenv("ip")))
320			nvram_set(strcat_r(prefix, "gateway", tmp), trim_r(gateway));
321	} else
322#endif
323{
324	changed += nvram_set_env(strcat_r(prefix, "netmask", tmp), "subnet");
325	if ((gateway = getenv("router")))
326		nvram_set(strcat_r(prefix, "gateway", tmp), trim_r(gateway));
327}
328
329	if (nvram_get_int(strcat_r(wanprefix, "dnsenable_x", tmp))) {
330		/* ex: android phone, the gateway is the DNS server. */
331		if ((value = getenv("dns") ? : getenv("router")))
332			nvram_set(strcat_r(prefix, "dns", tmp), trim_r(value));
333#ifdef DHCP_RFC3397
334		if ((value = getenv("search")) && *value) {
335			char *domain, *result;
336			if ((domain = getenv("domain")) && *domain &&
337			    find_word(value, trim_r(domain)) == NULL) {
338				result = alloca(strlen(domain) + strlen(value) + 2);
339				sprintf(result, "%s %s", domain, value);
340				value = result;
341			}
342			nvram_set(strcat_r(prefix, "domain", tmp), trim_r(value));
343		} else
344#endif
345		nvram_set_env(strcat_r(prefix, "domain", tmp), "domain");
346	}
347	if ((value = getenv("wins")))
348		nvram_set(strcat_r(prefix, "wins", tmp), trim_r(value));
349	//if ((value = getenv("hostname")))
350	//	sethostname(value, strlen(value) + 1);
351	if ((value = getenv("lease"))) {
352		unsigned int lease = atoi(value);
353		nvram_set_int(strcat_r(prefix, "lease", tmp), lease);
354		expires(wan_ifname, lease);
355	}
356
357	/* classful static routes */
358	nvram_set(strcat_r(prefix, "routes", tmp), getenv("routes"));
359	/* ms classless static routes */
360	nvram_set(strcat_r(prefix, "routes_ms", tmp), getenv("msstaticroutes"));
361	/* rfc3442 classless static routes */
362	nvram_set(strcat_r(prefix, "routes_rfc", tmp), getenv("staticroutes"));
363
364#ifdef RTCONFIG_IPV6
365	if ((value = getenv("ip6rd")) &&
366	    (get_ipv6_service() == IPV6_6RD && nvram_match(ipv6_nvname("ipv6_6rd_dhcp"), "1"))) {
367		char *ptr, *values[4];
368		int i;
369
370		ptr = value = strdup(value);
371		for (i = 0; value && i < 4; i++)
372			values[i] = strsep(&value, " ");
373		if (i == 4) {
374			nvram_set(strcat_r(wanprefix, "6rd_ip4size", tmp), values[0]);
375			nvram_set(strcat_r(wanprefix, "6rd_prefixlen", tmp), values[1]);
376			nvram_set(strcat_r(wanprefix, "6rd_prefix", tmp), values[2]);
377			nvram_set(strcat_r(wanprefix, "6rd_router", tmp), values[3]);
378		}
379		free(ptr);
380	}
381#endif
382
383#ifdef RTCONFIG_TR069
384	if ((value = getenv("opt43")) && nvram_get_int("tr_discovery") &&
385	    (value = hex2bin(value, &size))) {
386		struct opt_hdr *opt;
387		char buf[256], *url = NULL;
388		if ((opt = opt_get(value, size, 1)) &&
389		    strstr(stropt(opt, buf), "://") > buf)
390			url = buf;
391		else if (strstr(value, "://") > value)
392			url = trim_r(value);
393		if (url && *url) {
394			nvram_set("tr_acs_url", url);
395			nvram_set_int("tr_enable", 1);
396		}
397		//if ((opt = opt_get(value, size, 1)))
398		//	nvram_set(strcat_r(wanprefix, "tr_acs_url", tmp), stropt(opt, buf));
399		//if ((opt = opt_get(value, size, 2)))
400		//	nvram_set(strcat_r(wanprefix, "tr_pvgcode", tmp), stropt(opt, buf));
401		free(value);
402	}
403#ifdef RTCONFIG_TR181
404	nvram_unset("vivso");
405	if ((value = hex2bin(getenv("opt125"), &size))) {
406		struct viopt_hdr *viopt;
407		struct opt_hdr *oui, *serial, *class;
408		if ((viopt = viopt_get(value, size, htonl(3561))) &&
409		    (oui = opt_get(viopt->data, viopt->len, 4)) &&
410		    (serial = opt_get(viopt->data, viopt->len, 5))) {
411			char vivso[6 + 64 + 64 + 3];
412			char *ptr = vivso;
413			char *end = ptr + sizeof(vivso);
414			ptr += snprintf(ptr, end - ptr, "%s,", stropt(oui, tmp));
415			ptr += snprintf(ptr, end - ptr, "%s,", stropt(serial, tmp));
416			if ((class = opt_get(viopt->data, viopt->len, 6)))
417				ptr += snprintf(ptr, end - ptr, "%s", stropt(class, tmp));
418			nvram_set("vivso", vivso);
419		}
420		free(value);
421	}
422#endif
423#endif
424
425	// check if the ipaddr is safe to apply
426	// only handle one lan instance so far
427	// update_wan_state(prefix, WAN_STATE_STOPPED, WAN_STOPPED_REASON_INVALID_IPADDR)
428	if (inet_equal(nvram_safe_get(strcat_r(prefix, "ipaddr", tmp)), nvram_safe_get(strcat_r(prefix, "netmask", tmp)),
429		       nvram_safe_get("lan_ipaddr"), nvram_safe_get("lan_netmask"))) {
430		update_wan_state(prefix, WAN_STATE_STOPPED, WAN_STOPPED_REASON_INVALID_IPADDR);
431		return 0;
432	}
433
434	/* Clean nat conntrack for this interface,
435	 * but skip physical VPN subinterface for PPTP/L2TP */
436	if (changed && !(unit < 0 &&
437	    (nvram_match(strcat_r(wanprefix, "proto", tmp), "l2tp") ||
438	     nvram_match(strcat_r(wanprefix, "proto", tmp), "pptp"))))
439		ifconfig(wan_ifname, IFUP, "0.0.0.0", NULL);
440	ifconfig(wan_ifname, IFUP,
441		 nvram_safe_get(strcat_r(prefix, "ipaddr", tmp)),
442		 nvram_safe_get(strcat_r(prefix, "netmask", tmp)));
443
444	wan_up(wan_ifname);
445
446	logmessage("dhcp client", "bound %s via %s during %d seconds.",
447		nvram_safe_get(strcat_r(prefix, "ipaddr", tmp)),
448		nvram_safe_get(strcat_r(prefix, "gateway", tmp)),
449		nvram_get_int(strcat_r(prefix, "lease", tmp))
450		);
451
452	_dprintf("udhcpc:: %s done\n", __FUNCTION__);
453	return 0;
454}
455
456/*
457 * renew: This argument is used when a DHCP lease is renewed. All of
458 * the paramaters are set in enviromental variables. This argument is
459 * used when the interface is already configured, so the IP address,
460 * will not change, however, the other DHCP paramaters, such as the
461 * default gateway, subnet mask, and dns server may change.
462 */
463static int
464renew(void)
465{
466	char *wan_ifname = safe_getenv("interface");
467	char *value, *gateway;
468	char tmp[100], prefix[sizeof("wanXXXXXXXXXX_")];
469	char wanprefix[sizeof("wanXXXXXXXXXX_")];
470	int unit, ifunit;
471	int changed = 0;
472
473	/* Figure out nvram variable name prefix for this i/f */
474	if ((ifunit = wan_prefix(wan_ifname, wanprefix)) < 0)
475		return -1;
476	if ((unit = wan_ifunit(wan_ifname)) < 0)
477		snprintf(prefix, sizeof(prefix), "wan%d_x", ifunit);
478	else	snprintf(prefix, sizeof(prefix), "wan%d_", ifunit);
479
480	if ((value = getenv("ip")) == NULL ||
481	    !nvram_match(strcat_r(prefix, "ipaddr", tmp), trim_r(value)))
482		return bound();
483#if defined(RTCONFIG_USB_MODEM) && defined(RTCONFIG_INTERNAL_GOBI)
484	if (get_dualwan_by_unit(ifunit) == WANS_DUALWAN_IF_USB &&
485	    nvram_match("usb_modem_act_type", "gobi")) {
486		if (!nvram_match(strcat_r(prefix, "netmask", tmp), "255.255.255.255"))
487			return bound();
488		if ((gateway = getenv("ip")) == NULL ||
489		    !nvram_match(strcat_r(prefix, "gateway", tmp), trim_r(gateway)))
490			return bound();
491	} else
492#endif
493{
494	if ((value = getenv("subnet")) == NULL ||
495	    !nvram_match(strcat_r(prefix, "netmask", tmp), trim_r(value)))
496		return bound();
497	if ((gateway = getenv("router")) == NULL ||
498	    !nvram_match(strcat_r(prefix, "gateway", tmp), trim_r(gateway)))
499		return bound();
500}
501
502	if (nvram_get_int(strcat_r(wanprefix, "dnsenable_x", tmp))) {
503		/* ex: android phone, the gateway is the DNS server. */
504		if ((value = getenv("dns") ? : getenv("router"))) {
505			changed += !nvram_match(strcat_r(prefix, "dns", tmp), trim_r(value));
506			nvram_set(strcat_r(prefix, "dns", tmp), trim_r(value));
507		}
508#ifdef DHCP_RFC3397
509		if ((value = getenv("search")) && *value) {
510			char *domain, *result;
511			if ((domain = getenv("domain")) && *domain &&
512			    find_word(value, trim_r(domain)) == NULL) {
513				result = alloca(strlen(domain) + strlen(value) + 2);
514				sprintf(result, "%s %s", domain, value);
515				value = result;
516			}
517			changed += !nvram_match(strcat_r(prefix, "domain", tmp), trim_r(value));
518			nvram_set(strcat_r(prefix, "domain", tmp), trim_r(value));
519		} else
520#endif
521		changed += nvram_set_env(strcat_r(prefix, "domain", tmp), "domain");
522	}
523	if ((value = getenv("wins")))
524		nvram_set(strcat_r(prefix, "wins", tmp), trim_r(value));
525	//if ((value = getenv("hostname")))
526	//	sethostname(value, strlen(value) + 1);
527	if ((value = getenv("lease"))) {
528		unsigned int lease = atoi(value);
529		nvram_set_int(strcat_r(prefix, "lease", tmp), lease);
530		expires(wan_ifname, lease);
531	}
532
533	/* Update actual DNS or delayed for DHCP+PPP */
534	if (changed)
535		update_resolvconf();
536
537	/* Update connected state and DNS for WEB UI,
538	 * but skip physical VPN subinterface */
539	if (changed && !(unit < 0))
540		update_wan_state(wanprefix, WAN_STATE_CONNECTED, 0);
541
542	_dprintf("udhcpc:: %s done\n", __FUNCTION__);
543	return 0;
544}
545
546static int
547leasefail(void)
548{
549	char *wan_ifname = safe_getenv("interface");
550	char tmp[100], prefix[sizeof("wanXXXXXXXXXX_")];
551	char pid[sizeof("/var/run/zcipXXXXXXXXXX.pid")];
552	int unit;
553
554	/* Figure out nvram variable name prefix for this i/f */
555	if ((unit = wan_prefix(wan_ifname, prefix)) < 0)
556		return -1;
557
558	/* Start zcip for pppoe only */
559	if (!nvram_match(strcat_r(prefix, "proto", tmp), "pppoe"))
560		return 0;
561
562	snprintf(pid, sizeof(pid), "/var/run/zcip%d.pid", unit);
563	if (kill_pidfile_s(pid, 0) == 0)
564		return 0;
565
566	return start_zcip(wan_ifname, unit, NULL);
567}
568
569int
570udhcpc_wan(int argc, char **argv)
571{
572	_dprintf("%s:: %s\n", __FUNCTION__, argv[1] ? : "");
573	if (!argv[1])
574		return EINVAL;
575	else if (strstr(argv[1], "deconfig"))
576		return deconfig(0);
577	else if (strstr(argv[1], "bound"))
578		return bound();
579	else if (strstr(argv[1], "renew"))
580		return renew();
581	else if (strstr(argv[1], "leasefail"))
582		return leasefail();
583/*	else if (strstr(argv[1], "nak")) */
584
585	return 0;
586}
587
588int
589start_udhcpc(char *wan_ifname, int unit, pid_t *ppid)
590{
591	char tmp[100], prefix[sizeof("wanXXXXXXXXXX_")];
592	char pid[sizeof("/var/run/udhcpcXXXXXXXXXX.pid")];
593	char clientid[sizeof("61:") + (32+32+1)*2];
594#ifdef RTCONFIG_TR069
595	char vendorid[32+32+1+sizeof(",dslforum.org")];
596#ifdef RTCONFIG_TR181
597	unsigned char optbuf[sizeof(struct viopt_hdr) + 128];
598	unsigned char hwaddr[6];
599	char vivopts[sizeof("125:") + sizeof(optbuf)*2];
600#endif
601#endif
602	struct duid duid;
603	char *value;
604	char *dhcp_argv[] = { "/sbin/udhcpc",
605		"-i", wan_ifname,
606		"-p", (snprintf(pid, sizeof(pid), "/var/run/udhcpc%d.pid", unit), pid),
607		"-s", "/tmp/udhcpc",
608		NULL,		/* -t2 */
609		NULL,		/* -T5 */
610		NULL,		/* -A120 */
611		NULL,		/* -b */
612		NULL, NULL,	/* -H wan_hostname */
613		NULL,		/* -Oroutes */
614		NULL,		/* -Ostaticroutes */
615		NULL,		/* -Omsstaticroutes */
616#ifdef RTCONFIG_IPV6
617		NULL,		/* -Oip6rd rfc */
618		NULL,		/* -Oip6rd comcast */
619#endif
620		NULL, NULL,	/* -x 61:wan_clientid */
621		NULL, NULL,	/* -V wan_vendorid */
622#ifdef RTCONFIG_TR069
623		NULL,		/* -O43 */
624#ifdef RTCONFIG_TR181
625		NULL, NULL,	/* -x 125:vivopts */
626#endif
627#endif
628		NULL };
629	int index = 7;		/* first NULL */
630	int len, dr_enable;
631
632	/* Use unit */
633	snprintf(prefix, sizeof(prefix), "wan%d_", unit);
634
635	/* Stop zcip to avoid races */
636	stop_zcip(unit);
637
638	/* Skip dhcp and start zcip for pppoe, if desired */
639	if (nvram_match(strcat_r(prefix, "proto", tmp), "pppoe") &&
640	    nvram_match(strcat_r(prefix, "vpndhcp", tmp), "0"))
641		return start_zcip(wan_ifname, unit, ppid);
642
643	if (nvram_get_int("dhcpc_mode") == 0) {
644		/* 2 discover packets max (default 3 discover packets) */
645		dhcp_argv[index++] = "-t2";
646		/* 5 seconds between packets (default 3 seconds) */
647		dhcp_argv[index++] = "-T5";
648		/* Wait 160 seconds before trying again (default 20 seconds) */
649		/* set to 160 to accomodate new timings enforced by Charter cable */
650		dhcp_argv[index++] = "-A160";
651	}
652
653	if (ppid == NULL)
654		dhcp_argv[index++] = "-b";
655
656	value = nvram_safe_get(strcat_r(prefix, "hostname", tmp));
657	if (*value && is_valid_hostname(value)) {
658		dhcp_argv[index++] = "-H";
659		dhcp_argv[index++] = value;
660	}
661
662	/* 0: disable, 1: MS routes, 2: RFC routes, 3: Both */
663	dr_enable = nvram_get_int("dr_enable_x");
664	if (dr_enable != 0) {
665		dhcp_argv[index++] = "-O33";		/* routes */
666		if (dr_enable & (1 << 0))
667			dhcp_argv[index++] = "-O249";	/* "msstaticroutes" */
668		if (dr_enable & (1 << 1))
669			dhcp_argv[index++] = "-O121";	/* "staticroutes" */
670	}
671
672#ifdef RTCONFIG_IPV6
673	if (get_ipv6_service() == IPV6_6RD && nvram_match(ipv6_nvname("ipv6_6rd_dhcp"), "1")) {
674		dhcp_argv[index++] = "-O212";		/* ip6rd rfc */
675		dhcp_argv[index++] = "-O150";		/* ip6rd comcast */
676	}
677#endif
678
679	/* Client ID */
680	value = nvram_safe_get(strcat_r(prefix, "clientid", tmp));
681	if (nvram_get_int(strcat_r(prefix, "clientid_type", tmp))) {
682		if (get_duid(&duid)) {
683			/* RFC4361 implementation, use WAN number as IAID.
684			 * This also fits odhcp6c behavior for IA_NA for the WAN0 */
685			unsigned long iaid = htonl(unit + 1);
686			len = snprintf(clientid, sizeof(clientid), "61:ff");
687			len += bin2hex(clientid + len, sizeof(clientid) - len, &iaid, sizeof(iaid));
688			bin2hex(clientid + len, sizeof(clientid) - len, &duid, sizeof(duid));
689			dhcp_argv[index++] = "-x";
690			dhcp_argv[index++] = clientid;
691		}
692	} else if (*value) {
693		len = snprintf(clientid, sizeof(clientid), "61:");
694		bin2hex(clientid + len, sizeof(clientid) - len, value, strlen(value));
695		dhcp_argv[index++] = "-x";
696		dhcp_argv[index++] = clientid;
697	}
698
699	/* Vendor ID */
700	value = nvram_safe_get(strcat_r(prefix, "vendorid", tmp));
701#ifdef RTCONFIG_TR069
702	if (!nvram_invmatch("tr_acs_url", ""))
703		nvram_set_int("tr_discovery", 1);
704	if (nvram_get_int("tr_enable") && nvram_get_int("tr_discovery")) {
705		/* Add dslforum.org to VCI and request VSI */
706		snprintf(vendorid, sizeof(vendorid) - sizeof(",dslforum.org") + 1, "%s", value);
707		len = strlen(vendorid);
708		snprintf(vendorid + len, sizeof(vendorid) - len, len ? ",%s" : "%s", "dslforum.org");
709		value = vendorid;
710		dhcp_argv[index++] = "-O43";
711	}
712#endif
713	if (*value) {
714		dhcp_argv[index++] = "-V";
715		dhcp_argv[index++] = value;
716	}
717
718#ifdef RTCONFIG_TR069
719#ifdef RTCONFIG_TR181
720	if (ether_atoe(get_lan_hwaddr(), hwaddr)) {
721		struct viopt_hdr *viopt = (struct viopt_hdr *) optbuf;
722		unsigned char *ptr = viopt->data;
723		unsigned char *end = optbuf + sizeof(optbuf);
724
725		/* OUI */
726		len = snprintf(tmp, sizeof(tmp), "%02X%02X%02X", hwaddr[0], hwaddr[1], hwaddr[2]);
727		ptr += opt_add(ptr, end - ptr, 1, tmp, len);
728		/* Serial */
729		len = snprintf(tmp, sizeof(tmp), "%02X%02X%02X%02X%02X%02X",
730			       hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
731		ptr += opt_add(ptr, end - ptr, 2, tmp, len);
732		/* Class */
733		len = snprintf(tmp, sizeof(tmp), "%s", nvram_safe_get("productid"));
734		ptr += opt_add(ptr, end - ptr, 3, tmp, len);
735
736		viopt->entnum = htonl(3561);
737		viopt->len = ptr - viopt->data;
738
739		len = snprintf(vivopts, sizeof(vivopts), "125:");
740		bin2hex(vivopts + len, sizeof(vivopts) - len, viopt, sizeof(*viopt) + viopt->len);
741
742		dhcp_argv[index++] = "-x";
743		dhcp_argv[index++] = vivopts;
744	}
745#endif
746#endif
747
748	return _eval(dhcp_argv, NULL, 0, ppid);
749}
750
751void
752stop_udhcpc(int unit)
753{
754	char pid[sizeof("/var/run/udhcpcXXXXXXXXXX.pid")];
755
756	/* Stop zcip before udhcpc to avoid races */
757	stop_zcip(unit);
758
759	/* Stop all instances */
760	if (unit < 0) {
761		killall_tk("udhcpc");
762		return;
763	}
764
765	/* Stop unit instance only */
766	snprintf(pid, sizeof(pid), "/var/run/udhcpc%d.pid", unit);
767	if (kill_pidfile_s(pid, SIGUSR2) == 0) {
768		usleep(10000);
769		kill_pidfile_s(pid, SIGTERM);
770	}
771}
772
773/*
774 * config: This argument is used when zcip moves to configured state.
775 * All of the paramaters are set in enviromental variables, the script
776 * should configure the interface.
777*/
778static int
779config(void)
780{
781	char *wan_ifname = safe_getenv("interface");
782	char *value;
783	char tmp[100], prefix[sizeof("wanXXXXXXXXXX_")];
784	char wanprefix[sizeof("wanXXXXXXXXXX_")];
785	int unit, ifunit;
786	int changed = 0;
787
788	/* Figure out nvram variable name prefix for this i/f */
789	if ((ifunit = wan_prefix(wan_ifname, wanprefix)) < 0)
790		return -1;
791	if ((unit = wan_ifunit(wan_ifname)) < 0)
792		snprintf(prefix, sizeof(prefix), "wan%d_x", ifunit);
793	else	snprintf(prefix, sizeof(prefix), "wan%d_", ifunit);
794
795	if ((value = getenv("ip"))) {
796		changed = !nvram_match(strcat_r(prefix, "ipaddr", tmp), trim_r(value));
797		nvram_set(strcat_r(prefix, "ipaddr", tmp), trim_r(value));
798	}
799	nvram_set(strcat_r(prefix, "netmask", tmp), "255.255.0.0");
800	nvram_set(strcat_r(prefix, "gateway", tmp), "");
801	if (nvram_get_int(strcat_r(wanprefix, "dnsenable_x", tmp)))
802		nvram_set(strcat_r(prefix, "dns", tmp), "");
803	nvram_unset(strcat_r(prefix, "wins", tmp));
804	nvram_unset(strcat_r(prefix, "domain", tmp));
805	nvram_unset(strcat_r(prefix, "lease", tmp));
806	nvram_unset(strcat_r(prefix, "expires", tmp));
807
808	nvram_unset(strcat_r(prefix, "routes", tmp));
809	nvram_unset(strcat_r(prefix, "routes_ms", tmp));
810	nvram_unset(strcat_r(prefix, "routes_rfc", tmp));
811
812#ifdef RTCONFIG_TR069
813//	nvram_unset(strcat_r(prefix, "tr_acs_url", tmp));
814//	nvram_unset(strcat_r(prefix, "tr_pvgcode", tmp));
815#ifdef RTCONFIG_TR181
816	nvram_unset("vivso");
817#endif
818#endif
819	/* Clean nat conntrack for this interface,
820	 * but skip physical VPN subinterface for PPTP/L2TP */
821	if (changed && !(unit < 0 &&
822	    (nvram_match(strcat_r(wanprefix, "proto", tmp), "l2tp") ||
823	     nvram_match(strcat_r(wanprefix, "proto", tmp), "pptp"))))
824		ifconfig(wan_ifname, IFUP, "0.0.0.0", NULL);
825	ifconfig(wan_ifname, IFUP,
826		 nvram_safe_get(strcat_r(prefix, "ipaddr", tmp)),
827		 nvram_safe_get(strcat_r(prefix, "netmask", tmp)));
828
829	wan_up(wan_ifname);
830
831	logmessage("zcip client", "configured %s",
832		nvram_safe_get(strcat_r(prefix, "ipaddr", tmp)));
833
834	_dprintf("zcip:: %s done\n", __FUNCTION__);
835	return 0;
836}
837
838int
839zcip_wan(int argc, char **argv)
840{
841	_dprintf("%s:: %s\n", __FUNCTION__, argv[1] ? : "");
842	if (!argv[1])
843		return EINVAL;
844	else if (strstr(argv[1], "deconfig"))
845		return deconfig(1);
846	else if (strstr(argv[1], "config"))
847		return config();
848/*	else if (strstr(argv[1], "init")) */
849
850	return 0;
851}
852
853int
854start_zcip(char *wan_ifname, int unit, pid_t *ppid)
855{
856	char pid[sizeof("/var/run/zcipXXXXXXXXXX.pid")];
857	char *zcip_argv[] = { "/sbin/zcip",
858		"-p", (snprintf(pid, sizeof(pid), "/var/run/zcip%d.pid", unit), pid),
859		wan_ifname,
860		"/tmp/zcip",
861		NULL };
862
863	return _eval(zcip_argv, NULL, 0, ppid);
864}
865
866void
867stop_zcip(int unit)
868{
869	char pid[sizeof("/var/run/zcipXXXXXXXXXX.pid")];
870
871	/* Stop all instances */
872	if (unit < 0) {
873		killall_tk("zcip");
874		return;
875	}
876
877	/* Stop unit instance only */
878	snprintf(pid, sizeof(pid), "/var/run/zcip%d.pid", unit);
879	kill_pidfile_s(pid, SIGTERM);
880}
881
882static int
883expires_lan(char *lan_ifname, unsigned int in)
884{
885	time_t now;
886	FILE *fp;
887	char tmp[100];
888
889	time(&now);
890	snprintf(tmp, sizeof(tmp), "/tmp/udhcpc-%s.expires", lan_ifname);
891	if (!(fp = fopen(tmp, "w"))) {
892		perror(tmp);
893		return errno;
894	}
895	fprintf(fp, "%d", (unsigned int) now + in);
896	fclose(fp);
897	return 0;
898}
899
900/*
901 * deconfig: This argument is used when udhcpc starts, and when a
902 * leases is lost. The script should put the interface in an up, but
903 * deconfigured state.
904*/
905static int
906deconfig_lan(void)
907{
908	char *lan_ifname = safe_getenv("interface");
909
910	//ifconfig(lan_ifname, IFUP, "0.0.0.0", NULL);
911_dprintf("%s: IFUP.\n", __FUNCTION__);
912#ifdef RTCONFIG_DHCP_OVERRIDE
913	if (nvram_get_int("sw_mode") == SW_MODE_AP)
914		;
915	else
916#endif
917	if(nvram_match("lan_proto", "static"))
918		ifconfig(lan_ifname, IFUP | IFF_ALLMULTI, nvram_safe_get("lan_ipaddr"), nvram_safe_get("lan_netmask"));
919	else
920		ifconfig(lan_ifname, IFUP | IFF_ALLMULTI, nvram_default_get("lan_ipaddr"), nvram_default_get("lan_netmask"));
921
922	expires_lan(lan_ifname, 0);
923
924	lan_down(lan_ifname);
925
926	_dprintf("done\n");
927	return 0;
928}
929
930/*
931 * bound: This argument is used when udhcpc moves from an unbound, to
932 * a bound state. All of the paramaters are set in enviromental
933 * variables, The script should configure the interface, and set any
934 * other relavent parameters (default gateway, dns server, etc).
935*/
936static int
937bound_lan(void)
938{
939	char *lan_ifname = safe_getenv("interface");
940	char *value;
941#if defined(RTCONFIG_TR069) && defined(RTCONFIG_TR181)
942	char tmp[100];
943	int size;
944#endif
945
946	if ((value = getenv("ip"))) {
947		/* restart httpd after lan_ipaddr udpating through lan dhcp client */
948		if (!nvram_match("lan_ipaddr", trim_r(value))) {
949			stop_httpd();
950			start_httpd();
951		}
952		nvram_set("lan_ipaddr", trim_r(value));
953	}
954	if ((value = getenv("subnet")))
955		nvram_set("lan_netmask", trim_r(value));
956	if ((value = getenv("router")))
957		nvram_set("lan_gateway", trim_r(value));
958	if ((value = getenv("lease"))) {
959		nvram_set("lan_lease", trim_r(value));
960		expires_lan(lan_ifname, atoi(value));
961	}
962	if (nvram_get_int("lan_dnsenable_x") && (value = getenv("dns")))
963		nvram_set("lan_dns", trim_r(value));
964
965#if defined(RTCONFIG_TR069) && defined(RTCONFIG_TR181)
966	nvram_unset("vivso");
967	if ((value = hex2bin(getenv("opt125"), &size))) {
968		struct viopt_hdr *viopt;
969		struct opt_hdr *oui, *serial, *class;
970		if ((viopt = viopt_get(value, size, htonl(3561))) &&
971		    (oui = opt_get(viopt->data, viopt->len, 4)) &&
972		    (serial = opt_get(viopt->data, viopt->len, 5))) {
973			char vivso[6 + 64 + 64 + 3];
974			char *ptr = vivso;
975			char *end = ptr + sizeof(vivso);
976			ptr += snprintf(ptr, end - ptr, "%s,", stropt(oui, tmp));
977			ptr += snprintf(ptr, end - ptr, "%s,", stropt(serial, tmp));
978			if ((class = opt_get(viopt->data, viopt->len, 6)))
979				ptr += snprintf(ptr, end - ptr, "%s", stropt(class, tmp));
980			nvram_set("vivso", vivso);
981		}
982		free(value);
983	}
984#endif
985
986_dprintf("%s: IFUP.\n", __FUNCTION__);
987#ifdef RTCONFIG_WIRELESSREPEATER
988	if(nvram_get_int("sw_mode") == SW_MODE_REPEATER && nvram_get_int("wlc_mode") == 0){
989		update_lan_state(LAN_STATE_CONNECTED, 0);
990		_dprintf("done\n");
991		return 0;
992	}
993#endif
994
995#ifdef RTCONFIG_DHCP_OVERRIDE
996	if (nvram_get_int("sw_mode") == SW_MODE_AP && nvram_match("dnsqmode", "2")) {
997		nvram_set("dnsqmode", "1");
998		restart_dnsmasq(1);
999	}
1000#endif
1001
1002	ifconfig(lan_ifname, IFUP | IFF_ALLMULTI, nvram_safe_get("lan_ipaddr"),
1003		nvram_safe_get("lan_netmask"));
1004
1005	lan_up(lan_ifname);
1006
1007	_dprintf("done\n");
1008	return 0;
1009}
1010
1011/*
1012 * renew: This argument is used when a DHCP lease is renewed. All of
1013 * the paramaters are set in enviromental variables. This argument is
1014 * used when the interface is already configured, so the IP address,
1015 * will not change, however, the other DHCP paramaters, such as the
1016 * default gateway, subnet mask, and dns server may change.
1017 */
1018static int
1019renew_lan(void)
1020{
1021	bound_lan();
1022
1023	_dprintf("done\n");
1024	return 0;
1025}
1026
1027/* dhcp client script entry for LAN (AP) */
1028int
1029udhcpc_lan(int argc, char **argv)
1030{
1031	_dprintf("%s:: %s\n", __FUNCTION__, argv[1] ? : "");
1032	if (!argv[1])
1033		return EINVAL;
1034	else if (strstr(argv[1], "deconfig"))
1035		return deconfig_lan();
1036	else if (strstr(argv[1], "bound"))
1037		return bound_lan();
1038	else if (strstr(argv[1], "renew"))
1039		return renew_lan();
1040/*	else if (strstr(argv[1], "leasefail")) */
1041/*	else if (strstr(argv[1], "nak")) */
1042
1043	return EINVAL;
1044}
1045
1046// -----------------------------------------------------------------------------
1047
1048#ifdef RTCONFIG_IPV6
1049static int
1050deconfig6(char *wan_ifname)
1051{
1052	char *lan_ifname = nvram_safe_get("lan_ifname");
1053
1054	if (nvram_invmatch(ipv6_nvname("ipv6_wan_addr"), "")) {
1055		eval("ip", "-6", "addr", "del", nvram_safe_get(ipv6_nvname("ipv6_wan_addr")), "dev", wan_ifname);
1056		nvram_set(ipv6_nvname("ipv6_wan_addr"), "");
1057	}
1058
1059	if (get_ipv6_service() == IPV6_NATIVE_DHCP &&
1060		nvram_get_int(ipv6_nvname("ipv6_dhcp_pd"))) {
1061		if (nvram_invmatch(ipv6_nvname("ipv6_prefix"), "") ||
1062		    nvram_get_int(ipv6_nvname("ipv6_prefix_length")) != 0) {
1063			eval("ip", "-6", "addr", "flush", "scope", "global", "dev", lan_ifname);
1064			nvram_set(ipv6_nvname("ipv6_rtr_addr"), "");
1065			nvram_set(ipv6_nvname("ipv6_prefix"), "");
1066			nvram_set(ipv6_nvname("ipv6_prefix_length"), "");
1067		}
1068	}
1069
1070	if (nvram_invmatch(ipv6_nvname("ipv6_get_dns"), "") ||
1071	    nvram_invmatch(ipv6_nvname("ipv6_get_domain"), "")) {
1072		nvram_set(ipv6_nvname("ipv6_get_dns"), "");
1073		nvram_set(ipv6_nvname("ipv6_get_domain"), "");
1074		if (nvram_get_int(ipv6_nvname("ipv6_dnsenable")))
1075			update_resolvconf();
1076#ifdef RTCONFIG_6RELAYD
1077		if (get_ipv6_service() == IPV6_PASSTHROUGH)
1078			stop_6relayd();
1079#endif
1080	}
1081
1082	return 0;
1083}
1084
1085static int
1086bound6(char *wan_ifname, int bound)
1087{
1088	char *lan_ifname = nvram_safe_get("lan_ifname");
1089	struct in6_addr range;
1090	char addr[INET6_ADDRSTRLEN + 1], *value;
1091	char tmp[100], *next;
1092	int wanaddr_changed, prefix_changed, dns_changed;
1093	int size, start, end, intval;
1094
1095	value = safe_getenv("RA_HOPLIMIT");
1096	if (*value && (intval = atoi(value)))
1097		ipv6_sysconf(wan_ifname, "hop_limit", intval);
1098
1099	value = safe_getenv("RA_MTU");
1100	if (*value && (intval = atoi(value)) && intval < ifconfig_mtu(wan_ifname, 0)) {
1101		ipv6_sysconf(wan_ifname, "mtu", intval);
1102		ipv6_sysconf(lan_ifname, "mtu", intval);
1103	} else if ((intval = ipv6_getconf(wan_ifname, "mtu")))
1104		ipv6_sysconf(lan_ifname, "mtu", intval);
1105
1106	value = safe_getenv("ADDRESSES");
1107	if (*value) {
1108		foreach(tmp, value, next) {
1109			char *ptr = tmp;
1110			value = strsep(&ptr,",");
1111			break; /* only first address at the moment */
1112		}
1113	}
1114	wanaddr_changed = !nvram_match(ipv6_nvname("ipv6_wan_addr"), value);
1115	if (wanaddr_changed) {
1116		if (nvram_invmatch(ipv6_nvname("ipv6_wan_addr"), ""))
1117			eval("ip", "-6", "addr", "del", nvram_safe_get(ipv6_nvname("ipv6_wan_addr")), "dev", wan_ifname);
1118		nvram_set(ipv6_nvname("ipv6_wan_addr"), value);
1119	}
1120	if (*value)
1121		eval("ip", "-6", "addr", "add", value, "dev", wan_ifname);
1122
1123	prefix_changed = 0;
1124	if (get_ipv6_service() == IPV6_NATIVE_DHCP &&
1125		nvram_get_int(ipv6_nvname("ipv6_dhcp_pd"))) {
1126		value = safe_getenv("PREFIXES");
1127		if (*value) {
1128			foreach(tmp, value, next) {
1129				char *ptr = tmp;
1130				value = strsep(&ptr,",");
1131				break; /* only first prefix at the moment */
1132			}
1133		}
1134		if (sscanf(value, "%[^/]/%d", addr, &size) != 2)
1135			goto skip;
1136
1137		prefix_changed = (!nvram_match(ipv6_nvname("ipv6_prefix"), addr) ||
1138				  nvram_get_int(ipv6_nvname("ipv6_prefix_length")) != size);
1139		if (prefix_changed) {
1140			eval("ip", "-6", "addr", "flush", "scope", "global", "dev", lan_ifname);
1141			nvram_set(ipv6_nvname("ipv6_rtr_addr"), "");
1142			nvram_set(ipv6_nvname("ipv6_prefix"), addr);
1143			nvram_set_int(ipv6_nvname("ipv6_prefix_length"), size);
1144		}
1145		if (*addr)
1146			add_ip6_lanaddr();
1147
1148		if (prefix_changed && nvram_get_int(ipv6_nvname("ipv6_autoconf_type"))) {
1149			/* TODO: rework WEB UI to specify ranges without prefix
1150			 * TODO: add size checking, now range takes all of 16 bit */
1151			start = (inet_pton(AF_INET6, nvram_safe_get(ipv6_nvname("ipv6_dhcp_start")), &range) > 0) ?
1152			    ntohs(range.s6_addr16[7]) : 0x1000;
1153			end = (inet_pton(AF_INET6, nvram_safe_get(ipv6_nvname("ipv6_dhcp_end")), &range) > 0) ?
1154			    ntohs(range.s6_addr16[7]) : 0x2000;
1155
1156			value = nvram_safe_get(ipv6_nvname("ipv6_prefix"));
1157			inet_pton(AF_INET6, *value ? value : "::", &range);
1158
1159			range.s6_addr16[7] = (start < end) ? htons(start) : htons(end);
1160			inet_ntop(AF_INET6, &range, addr, sizeof(addr));
1161			nvram_set(ipv6_nvname("ipv6_dhcp_start"), addr);
1162			range.s6_addr16[7] = (start < end) ? htons(end) : htons(start);
1163			inet_ntop(AF_INET6, &range, addr, sizeof(addr));
1164			nvram_set(ipv6_nvname("ipv6_dhcp_end"), addr);
1165		}
1166	}
1167skip:
1168
1169	if (*safe_getenv("RDNSS")) {
1170		dns_changed = nvram_set_env(ipv6_nvname("ipv6_get_dns"), "RDNSS");
1171		dns_changed += nvram_set_env(ipv6_nvname("ipv6_get_domain"), "DOMAINS");
1172	} else {
1173		dns_changed = nvram_set_env(ipv6_nvname("ipv6_get_dns"), "RA_DNS");
1174		dns_changed += nvram_set_env(ipv6_nvname("ipv6_get_domain"), "RA_DOMAINS");
1175	}
1176	if (dns_changed && nvram_get_int(ipv6_nvname("ipv6_dnsenable")))
1177		update_resolvconf();
1178
1179#ifdef RTCONFIG_6RELAYD
1180	if (dns_changed && get_ipv6_service() == IPV6_PASSTHROUGH)
1181		start_6relayd();
1182#endif
1183
1184	if (bound == 1 || wanaddr_changed || prefix_changed) {
1185		char *address = nvram_safe_get(ipv6_nvname("ipv6_wan_addr"));
1186		char *prefix = nvram_safe_get(ipv6_nvname("ipv6_prefix"));
1187
1188		if (*prefix) {
1189			snprintf(addr, sizeof(addr), "%s/%d", prefix, nvram_get_int(ipv6_nvname("ipv6_prefix_length")));
1190			prefix = addr;
1191		}
1192		logmessage("dhcp6 client", "%s %s%s%s%s%s",
1193			bound ? "bound" : "informed",
1194			*address ? "address " : "", address, *address ? ", " : "",
1195			*prefix ? "prefix " : "", prefix);
1196	}
1197
1198	return 0;
1199}
1200
1201static int
1202ra_updated6(char *wan_ifname)
1203{
1204	char *lan_ifname = nvram_safe_get("lan_ifname");
1205	char *value;
1206	int dns_changed, intval;
1207
1208	value = safe_getenv("RA_HOPLIMIT");
1209	if (*value && (intval = atoi(value)))
1210		ipv6_sysconf(wan_ifname, "hop_limit", intval);
1211
1212	value = safe_getenv("RA_MTU");
1213	if (*value && (intval = atoi(value)) && intval < ifconfig_mtu(wan_ifname, 0)) {
1214		ipv6_sysconf(wan_ifname, "mtu", intval);
1215		ipv6_sysconf(lan_ifname, "mtu", intval);
1216	}
1217
1218	if (*safe_getenv("RDNSS")) {
1219		dns_changed = nvram_set_env(ipv6_nvname("ipv6_get_dns"), "RDNSS");
1220		dns_changed += nvram_set_env(ipv6_nvname("ipv6_get_domain"), "DOMAINS");
1221	} else {
1222		dns_changed = nvram_set_env(ipv6_nvname("ipv6_get_dns"), "RA_DNS");
1223		dns_changed += nvram_set_env(ipv6_nvname("ipv6_get_domain"), "RA_DOMAINS");
1224	}
1225	if (dns_changed && nvram_get_int(ipv6_nvname("ipv6_dnsenable")))
1226		update_resolvconf();
1227
1228#ifdef RTCONFIG_6RELAYD
1229	if (dns_changed && get_ipv6_service() == IPV6_PASSTHROUGH)
1230		start_6relayd();
1231#endif
1232
1233	return 0;
1234}
1235
1236int dhcp6c_wan(int argc, char **argv)
1237{
1238	if (!argv[1] || !argv[2])
1239		return EINVAL;
1240	else if (strcmp(argv[2], "started") == 0 ||
1241		 strcmp(argv[2], "stopped") == 0 ||
1242		 strcmp(argv[2], "unbound") == 0)
1243		return deconfig6(argv[1]);
1244	else if (strcmp(argv[2], "bound") == 0)
1245		return bound6(argv[1], 1);
1246	else if (strcmp(argv[2], "updated") == 0 ||
1247		 strcmp(argv[2], "rebound") == 0)
1248		return bound6(argv[1], 2);
1249	else if (strcmp(argv[2], "informed") == 0)
1250		return bound6(argv[1], 0);
1251	else if (strcmp(argv[2], "ra-updated") == 0)
1252		return ra_updated6(argv[2]);
1253
1254	return 0;
1255}
1256
1257int
1258start_dhcp6c(void)
1259{
1260	char *wan_ifname = (char *) get_wan6face();
1261	char *dhcp6c_argv[] = { "odhcp6c",
1262#ifndef RTCONFIG_BCMARM
1263		"-f",
1264#else
1265		"-df",
1266#endif
1267		"-R",
1268		"-s", "/tmp/dhcp6c",
1269		"-N", "try",
1270		NULL, NULL,	/* -c duid */
1271		NULL, NULL,	/* -FP len:iaidhex */
1272		NULL, NULL,	/* -rdns -rdomain */
1273		NULL, NULL, 	/* -rsolmaxrt -r infmaxrt */
1274		NULL,		/* -v */
1275		NULL,		/* interface */
1276		NULL };
1277	int index = 7;
1278	struct duid duid;
1279	char duid_arg[sizeof(duid)*2+1];
1280	char prefix_arg[sizeof("128:xxxxxxxx")];
1281	int i;
1282#ifndef RTCONFIG_BCMARM
1283	pid_t pid;
1284#endif
1285
1286	/* Check if enabled */
1287	if (get_ipv6_service() != IPV6_NATIVE_DHCP
1288#ifdef RTCONFIG_6RELAYD
1289		&& get_ipv6_service() != IPV6_PASSTHROUGH
1290#endif
1291	)
1292		return 0;
1293
1294	if (!wan_ifname || *wan_ifname == '\0')
1295		return -1;
1296
1297#ifdef RTCONFIG_6RELAYD
1298	stop_6relayd();
1299#endif
1300	stop_dhcp6c();
1301
1302	if (get_duid(&duid)) {
1303		bin2hex(duid_arg, sizeof(duid_arg), &duid, sizeof(duid));
1304		dhcp6c_argv[index++] = "-c";
1305		dhcp6c_argv[index++] = duid_arg;
1306	}
1307
1308	if (get_ipv6_service() == IPV6_NATIVE_DHCP &&
1309		nvram_get_int(ipv6_nvname("ipv6_dhcp_pd"))) {
1310		/* Generate IA_PD IAID from the last 7 digits of WAN MAC */
1311		unsigned long iaid = ether_atoe(nvram_safe_get("wan0_hwaddr"), duid.ea) ?
1312			((unsigned long)(duid.ea[3] & 0x0f) << 16) |
1313			((unsigned long)(duid.ea[4]) << 8) |
1314			((unsigned long)(duid.ea[5])) : 1;
1315		i = 64 - (nvram_get_int(ipv6_nvname("ipv6_prefix_length")) ? : 64);
1316		if (i < 0)
1317			i = 0;
1318		snprintf(prefix_arg, sizeof(prefix_arg), "%d:%lx", i, iaid);
1319		dhcp6c_argv[index++] = "-FP";
1320		dhcp6c_argv[index++] = prefix_arg;
1321	}
1322
1323	if (nvram_get_int(ipv6_nvname("ipv6_dnsenable"))
1324#ifdef RTCONFIG_6RELAYD
1325		|| get_ipv6_service() == IPV6_PASSTHROUGH
1326#endif
1327		) {
1328		dhcp6c_argv[index++] = "-r23";	/* dns */
1329		dhcp6c_argv[index++] = "-r24";	/* domain */
1330	}
1331	dhcp6c_argv[index++] = "-r82";	/* sol_max_rt */
1332	dhcp6c_argv[index++] = "-r83";	/* inf_max_rt */
1333
1334	if (nvram_get_int("ipv6_debug"))
1335		dhcp6c_argv[index++] = "-v";
1336
1337	dhcp6c_argv[index++] = wan_ifname;
1338
1339#ifndef RTCONFIG_BCMARM
1340	return _eval(dhcp6c_argv, NULL, 0, &pid);
1341#else
1342	return _eval(dhcp6c_argv, NULL, 0, NULL);
1343#endif
1344}
1345
1346void stop_dhcp6c(void)
1347{
1348	killall_tk("odhcp6c");
1349}
1350#endif // RTCONFIG_IPV6
1351