#!/bin/sh
<?
include "/htdocs/phplib/trace.php";
include "/htdocs/phplib/xnode.php";
include "/htdocs/phplib/phyinf.php";

function cmd($cmd)	{echo $cmd."\n";}
function msg($msg)	{cmd("echo [DHCP6C]: ".$msg." > /dev/console");}
function error($m)	{cmd("echo [RA-WAIT]: ERROR: ".$m); return 9;}

/***********************************************************************/
function dhcp_client($mode, $inf, $devnam, $opt, $router, $dns)
{
	$hlp = "/var/servd/".$inf."-dhcp6c.sh";
	$pid = "/var/servd/".$inf."-dhcp6c.pid";
	$cfg = "/var/servd/".$inf."-dhcp6c.cfg";

	/* DHCP over PPP session ? */
	$previnf = XNODE_get_var($inf."_PREVINF");
	XNODE_del_var($inf."_PREVINF");

	/* dslite ? */
	$nextinf = XNODE_get_var($inf."_NEXTINF");
	XNODE_del_var($inf."_NEXTINF");

	//if ($mode=="PPPDHCP" && $_GLOBALS["PREVINF"]!="")
	//msg("mode is ".$mode.", previnf is ".$previnf);
	msg("mode is ".$mode.", previnf is ".$previnf.", nextinf is ".$nextinf);
	if ($mode=="PPPDHCP" && $previnf!="")
	{
		//$pppdev = PHYINF_getruntimeifname($_GLOBALS["PREVINF"]);
		$pppdev = PHYINF_getruntimeifname($previnf);
		if ($pppdev=="") return error("no PPP device.");
		msg("PPP device = ".$pppdev);
	}
	msg("dhcpopt: ".$opt);

	/* Gererate DHCP-IAID from 32-bit of mac address*/
	$mac = PHYINF_getphymac($inf);
	$mac1 = cut($mac, 3, ":"); $mac2 = cut($mac, 0, ":"); $mac3 = cut($mac, 1, ":"); $mac4 = cut($mac, 2, ":");
	$iaidstr = $mac1.$mac2.$mac3.$mac4;
	$iaid = strtoul($iaidstr, 16);	
		
	/* Generate configuration file. */
	if ($mode=="INFOONLY")
	{
		$send="\tinformation-only;\n";
		$idas="";
	}
	else
	{
		//check if we have pd hint
		$stsp = XNODE_getpathbytarget("/runtime", "inf", "uid", $inf, 0);
		$pdhint_enable = query($stsp."/pdhint/enable");
		$pdhintmsg="\n";
		if($pdhint_enable=="1")
		{
			$pdhint_network = query($stsp."/pdhint/network");
			$pdhint_prefix = query($stsp."/pdhint/prefix");
			$pdhint_plft = query($stsp."/pdhint/preferlft");
			$pdhint_vlft = query($stsp."/pdhint/validlft");
			if($pdhint_vlft!="")
			{
				$pdhintmsg = "\tprefix ".$pdhint_network."/".$pdhint_prefix." ".$pdhint_plft." ".$pdhint_vlft.";\n";
			}
			else
			{
				$pdhintmsg = "\tprefix ".$pdhint_network."/".$pdhint_prefix." ".$pdhint_plft.";\n";
			}
		}

		//if (strstr($opt,"IA-NA")!="") {$send=$send."\tsend ia-na 0;\n"; $idas=$idas."id-assoc na {\n};\n";}
		if (strstr($opt,"IA-NA")!="") {$send=$send."\tsend ia-na ".$iaid.";\n"; $idas=$idas."id-assoc na ".$iaid."{\n};\n";}
		//if (strstr($opt,"IA-PD")!="") {$send=$send."\tsend ia-pd 0;\n"; $idas=$idas."id-assoc pd {\n};\n";}
		if (strstr($opt,"IA-PD")!="") {$send=$send."\tsend ia-pd 0;\n"; $idas=$idas."id-assoc pd {\n".$pdhintmsg."};\n";}
	}

	if($mode=="PPPDHCP") $dname = $pppdev;
	else $dname = $devnam;

	$nextinfp = XNODE_getpathbytarget("", "inf", "uid", $nextinf, 0);
	$nextinet = query($nextinfp."/inet");
	$nextinetp = XNODE_getpathbytarget("inet", "entry", "uid", $nextinet, 0);
	$nextmode = query($nextinetp."/ipv4/ipv4in6/mode");
		
	if($nextinf!="" && $nextmode=="dslite")
	{ 
		$rqstmsg = "\trequest aftr-server-domain-name;\n";
	}
	else	$rqstmsg = "";

	fwrite(w, $cfg,
		//"interface ".$devnam." {\n".
		"interface ".$dname." {\n".
		$send.
		"\trequest domain-name-servers;\n".
		"\trequest domain-name;\n".
		"\trequest ntp-servers;\n".
		//"\trequest aftr-server-domain-name;\n".
		$rqstmsg.
		"\tscript \"".$hlp."\";\n".
		"};\n".
		$idas);

	/* generate callback script */
	fwrite(w, $hlp,
		"#!/bin/sh\n".
		'if [ $new_addr != "" ] || [ $new_pd_prefix != "" ]; then\n'.
		"	echo [$0]: [$new_addr] [$new_pd_prefix] [$new_pd_plen] [$new_pd_pltime] [$new_pd_vltime] > /dev/console\n".
		"else\n".
		"	exit 0\n".
		"fi\n".
		//"echo [$0]: [$new_addr] [$new_pd_prefix] [$new_pd_plen] [$new_pd_pltime] [$new_pd_vltime] > /dev/console\n".
		"phpsh /etc/services/INET/inet6_dhcpc_helper.php".
			" INF=".$inf.
			" MODE=".$mode.
			//" DEVNAM=".$devnam.
			" DEVNAM=".$dname.
			" GATEWAY=".$router.
			" DHCPOPT=".$opt.
			' "NAMESERVERS=$new_domain_name_servers"'.
			' "DOMAIN=$new_domain_name"'.
			' "NEW_ADDR=$new_addr"'.
			' "NEW_PD_PREFIX=$new_pd_prefix"'.
			' "NEW_PD_PLEN=$new_pd_plen"'.
			' "NEW_PD_PLTIME=$new_pd_pltime"'.
			' "NEW_PD_VLTIME=$new_pd_vltime"'.
			' "DNS='.$dns.'"'.
			' "NEW_AFTR_NAME=$new_aftr_name"'.
			' "NTPSERVER=$new_ntp_servers"'.
			"\n");

	/* Start DHCP client */
	cmd("chmod +x ".$hlp);
	if ($pppdev=="")
		 cmd("dhcp6c -c ".$cfg." -p ".$pid." -t LL ".$devnam);
	else cmd("dhcp6c -c ".$cfg." -p ".$pid." -t LL -o ".$devnam." ".$pppdev);
	return 0;
}

/***********************************************************************/

function main_entry($inf, $phyinf, $devnam, $dhcpopt, $dns, $me)
{
	/* generate callback script */
	$pid = "/var/servd/".$inf."-rdisc6.pid";
	$hlp = "/var/servd/".$inf."-rdisc6.sh";
	fwrite(w, $hlp,
		"#!/bin/sh\n".
		"echo [$0]: [$IFNAME] [$MFLAG] [$OFLAG] > /dev/console\n".
		"phpsh /etc/services/INET/inet6_rdisc6_helper.php".
			' "IFNAME=$IFNAME"'.
			' "MFLAG=$MFLAG"'.
			' "OFLAG=$OFLAG"'.
			' "PREFIX=$PREFIX"'.
			' "PFXLEN=$PFXLEN"'.
			' "LLADDR=$LLADDR"'.
			' "RDNSS=$RDNSS"'.
			' "DNSSL=$DNSSL"'.
			"\n");

	cmd("chmod +x ".$hlp);

	/* run rdisc */
	//cmd("killall rdisc6");
	//cmd("sleep 1");
	
	/* INF status path. */
	$stsp = XNODE_getpathbytarget("/runtime", "inf", "uid", $inf, 0);
	if ($stsp=="") return error($inf." has not runtime nodes !");

	/* need infprev */
	$infprev = query($stsp."/infprevious");
	if($infprev!="")
	{
		$prevstsp = XNODE_getpathbytarget("/runtime", "inf", "uid", $infprev, 0);
		$prevdevnam = query($prevstsp."/devnam");
		$prevphyinf = query($prevstsp."/phyinf");
	}

	/* check if interface if ppp */
	if(strstr($prevdevnam,"ppp")=="")
	{
		if(strstr($prevdevnam,"sit")=="")
			cmd("rdisc6 -c ".$hlp." -p ".$pid." -q ".$devnam." &");	
		else
		{
			msg("SIT-Autoconf mode");
			/* auto in sit mode */
			$p = XNODE_getpathbytarget("/runtime", "phyinf", "uid", $prevphyinf, 0);
			if($p!="") $ipaddr = query($p."/ipv6/link/ipaddr");
			else return error("SIT tunnel need exist!! ");
			msg("ipaddr: ".$ipaddr." and devnam: ".$prevdevnam);
			cmd("rdisc6 -c ".$hlp." -p ".$pid." -q -e ".$ipaddr." ".$prevdevnam." &");	
		}
	}
	else
	{
		msg("PPP-Autoconf mode");
		/* need infprev */
		$infprev = query($stsp."/infprevious");
		$prevstsp = XNODE_getpathbytarget("/runtime", "inf", "uid", $infprev, 0);
		/* need infprev */
		$atype = query($prevstsp."/inet/addrtype");
		if($atype == "ipv6")
			$ipaddr = query($prevstsp."/inet/ipv6/ipaddr");/* share session with ipv4*/
		else if($atype == "ppp6")  	
			$ipaddr = query($prevstsp."/inet/ppp6/local");/* only ipv6 session */
		else return error($inf." has wrong addrtype of infprevious");
		cmd("rdisc6 -c ".$hlp." -p ".$pid." -q -e ".$ipaddr." ".$prevdevnam." &");	
	}

	cmd("sleep 5");

	/* Clear any old records. */
	del($stsp."/stateless");
	/* Preparing & Get M flag */
	$child	= query($stsp."/child/uid");
	if(strstr($prevdevnam,"ppp")=="") $conf	= "/var/run/".$devnam;
	else $conf = "/var/run/".$prevdevnam;
	$mflag	= fread("e", $conf.".ra_mflag");
	$oflag	= fread("e", $conf.".ra_oflag"); 
	$rdnss  = fread("e", $conf.".ra_rdnss");
	$dnssl  = fread("e", $conf.".ra_dnssl");

	/* need infnext */
	$infnext = query($stsp."/infnext");
	if($infnext!="")
	{
		XNODE_set_var($inf."_NEXTINF",$infnext);
	}

	/* need dnssl info */
	if($dnssl!="")
	{
		msg("DNSSL :".$dnssl);
		XNODE_set_var($child."_DOMAIN",$dnssl);
	}
	if(strstr($prevdevnam,"ppp")=="") msg($inf."/".$devnam.", M=[".$mflag."], O=[".$oflag."]");
	else msg($inf."/".$prevdevnam.", M=[".$mflag."], O=[".$oflag."]");

	/* according to TR-124 issue 2 */
	/* Ignore m and o flags, always start dhcp client */

	/* generate wait script */
	$ipwait = "/var/servd/INET.".$inf."-ipwait.sh";
	fwrite(w, $ipwait,
		"#!/bin/sh\n".
		"phpsh /etc/scripts/IP-WAIT.php".
		" INF=".$inf.
		" PHYINF=".$phyinf.
		" DEVNAM=".$devnam.
		' "DNS='.$dns.'"'.
		" ME=".$ipwait.
		"\n");
	/* start script */
	cmd("chmod +x ".$ipwait);
	cmd('xmldbc -t "ra.iptest.'.$inf.':10:'.$ipwait.'"');

	if($child!="")
	{
		/* always need dhcp client */
		$dhcpopt = "IA-NA+IA-PD";
		if(strstr($prevdevnam,"ppp")=="")
		{
			dhcp_client("STATEFUL", $inf, $devnam, $dhcpopt, $router, $dns);
		}
		else
		{
			XNODE_set_var($inf."_PREVINF",$infprev);
			dhcp_client("PPPDHCP", $inf, $devnam, $dhcpopt, $router, $dns);
		}
		return 0;
	}
	else
	{
		$dhcpopt = "IA-NA";
		if(strstr($prevdevnam,"ppp")=="")
		{
			dhcp_client("STATEFUL", $inf, $devnam, $dhcpopt, $router, $dns);
		}
		else
		{
			XNODE_set_var($inf."_PREVINF",$infprev);
			dhcp_client("PPPDHCP", $inf, $devnam, $dhcpopt, $router, $dns);
		}	
		return 0;
	}

	//not reach here
	if ($mflag=="1")
	{
		/* Stateful ... */
		if ($dhcpopt=="")
		{
			if ($child=="") $dhcpopt = "IA-NA";
			else $dhcpopt = "IA-NA+IA-PD";
		}
		//dhcp_client("STATEFUL", $inf, $devnam, $dhcpopt, $router, $dns);
		if(strstr($prevdevnam,"ppp")=="")
			dhcp_client("STATEFUL", $inf, $devnam, $dhcpopt, $router, $dns);
		else
		{
			XNODE_set_var($inf."_PREVINF",$infprev);
			dhcp_client("PPPDHCP", $inf, $devnam, $dhcpopt, $router, $dns);
		}
		return 0;
	}
	else if ($mflag=="0")
	{
		/* Stateless */
		$p = XNODE_getpathbytarget("/runtime", "phyinf", "uid", $phyinf, 0);
		if ($p=="") return error($phyinf." has not runtime nodes!");
		/* Get self-configured IP address. */
		/*$ipaddr = query($p."/ipv6/global/ipaddr");*/
		/*$prefix = query($p."/ipv6/global/prefix");*/
		
		$mac = PHYINF_getphymac($inf);
		$hostid = ipv6eui64($mac);
		$ra_prefix = fread("e", $conf.".ra_prefix");
		$prefix = fread("e", $conf.".ra_prefix_len");
		$ipaddr = ipv6ip($ra_prefix, $prefix, $hostid, 0, 0);
		$router	= fread("e", $conf.".ra_saddr");

		if(strstr($prevdevnam,"ppp")!="")
		{
			$p = XNODE_getpathbytarget("/runtime", "phyinf", "uid", $prevphyinf, 0);
			$llipaddr = query($p."/ipv6/link/ipaddr");
			$hostid = ipv6hostid($llipaddr,64);
			$ipaddr = ipv6ip($ra_prefix, $prefix, $hostid, 0, 0);
		}

		if ($ipaddr!="")
		{
			if ($oflag=="0" && $dns=="" && $rdnss!="") {$dns=$rdnss;}

			msg("Stateless Self-Config IP: ".$ipaddr."/".$prefix);
			//$router	= fread("e", $conf."/ra_saddr");
			if ($child!="")
			{
				set($stsp."/stateless/ipaddr",	$ipaddr);
				set($stsp."/stateless/prefix",	$prefix);
				set($stsp."/stateless/gateway",	$router);
				set($stsp."/stateless/dns",		$dns);
				if($dnssl!="")
				{
					set($stsp."/stateless/domain",		$dnssl);
				}
				/* run dhcpc6 for the child interface, and attach at the callback of dhcpc. */
				//dhcp_client("STATELESS", $inf, $devnam, "IA-PD", $router, $dns);
				if(strstr($prevdevnam,"ppp")=="")
					dhcp_client("STATELESS", $inf, $devnam, "IA-PD", $router, $dns);
				else
				{
					XNODE_set_var($inf."_PREVINF",$infprev);
					dhcp_client("PPPDHCP", $inf, $devnam, "IA-PD", $router, $dns);
				}
			}
			else
			{
				cmd("phpsh /etc/scripts/IPV6.INET.php ACTION=ATTACH".
						" MODE=STATELESS".
						" INF=".$inf.
						" DEVNAM=".$devnam.
						" IPADDR=".$ipaddr.
						" PREFIX=".$prefix.
						" GATEWAY=".$router.
						' "DNS='.$dns.'"');
				if ($oflag=="1" && $dns=="")
				{
					msg("STATELESS DHCP: information only.");
					dhcp_client("INFOONLY", $inf, $devnam, "", "", "");
				}
			}
			return 0;
		}
	}
	
	//cmd("killall rdisc6");
	cmd("/etc/scripts/killpid.sh ".$pid);
	cmd("sleep 1");

	/* Not configured, try later. */
	cmd('xmldbc -t "ra.iptest.'.$inf.':5:'.$me.'"');

	/* force to send RS */
	//$conf = "/proc/sys/net/ipv6/conf/".$_GLOBALS["DEVNAM"];
	//fwrite(w, $conf."/disable_ipv6",  "1");
	//fwrite(w, $conf."/disable_ipv6",  "0");

	return 0;
}

/* Main entry */
main_entry(
	$_GLOBALS["INF"],
	$_GLOBALS["PHYINF"],
	$_GLOBALS["DEVNAM"],
	$_GLOBALS["DHCPOPT"],
	$_GLOBALS["DNS"],
	$_GLOBALS["ME"]
	);
?>