commit 31c43fd38bf9a2a1fce8a6219a33ab0c8f8a25c4
Author: smos <seth.mos@dds.nl>
Date:   Fri Mar 30 10:08:28 2012 +0200

    Add 6to4 support for automatic tunneling.

diff --git a/etc/inc/interfaces.inc b/etc/inc/interfaces.inc
index f04acbbd1c..429290a98d 100644
--- a/etc/inc/interfaces.inc
+++ b/etc/inc/interfaces.inc
@@ -1129,6 +1129,13 @@ function interface_bring_down($interface = "wan", $destroy = false) {
 				pfSense_interface_flags($realif, -IFF_UP);
 		}		
 		break;
+	case "6to4":
+		if(does_interface_exist("$realif")) {
+			mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " delete", true);
+			if ($destroy == true)
+				pfSense_interface_flags($realif, -IFF_UP);
+		}		
+		break;
 	default:
 		if(does_interface_exist("$realif")) {
 			mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " delete", true);
@@ -2871,6 +2878,9 @@ function interface_configure($interface = "wan", $reloadall = false, $linkupeven
 		case '6rd':
 			interface_6rd_configure($interface);
 			break;
+		case '6to4':
+			interface_6to4_configure($interface);
+			break;
 		default:
 			if (is_ipaddr($wancfg['ipaddrv6']) && $wancfg['subnetv6'] <> "") {
 				pfSense_interface_setaddress($realif, "{$wancfg['ipaddrv6']}/{$wancfg['subnetv6']}");
@@ -3054,6 +3064,93 @@ function interface_6rd_configure($interface = "wan"){
 	return 0;
 }
 
+function interface_6to4_configure($interface = "wan"){
+	global $config, $g;
+	$iflist = get_configured_interface_with_descr(false, true);
+
+	/* because this is a tunnel interface we can only function 
+	 *	with a public IPv4 address on the interface */
+
+	$wancfg = $config['interfaces'][$interface];
+	$wanif = $wancfg['if'];
+	if (empty($wancfg))
+		$wancfg = array();
+
+	$wanif = get_real_interface($interface);
+	
+	$ip4address = find_interface_ip($wanif);
+	if((!is_ipaddrv4($ip4address)) || (is_private_ip($ip4address))) {
+		log_error("The interface IPv4 '{$ip4address}' address on interface '{$wanif}' is not public, not configuring 6RD tunnel");
+		return false;
+	}
+	
+	/* create the long prefix notation for math, save the prefix length */
+	$stfprefixlen = 16;
+	$stfprefix = Net_IPv6::uncompress("2002::");
+	$stfarr = explode(":", $stfprefix);
+	$v4prefixlen = "0";
+	
+	/* we need the hex form of the interface IPv4 address */
+	$ip4arr = explode(".", $ip4address);
+	$hexwanv4 = "";
+	foreach($ip4arr as $octet)
+		$hexwanv4 .= sprintf("%02x", $octet);
+
+	/* we need the hex form of the broker IPv4 address */
+	$ip4arr = explode(".", "192.88.99.1");
+	$hexbrv4 = "";
+	foreach($ip4arr as $octet)
+		$hexbrv4 .= sprintf("%02x", $octet);
+	
+	/* binary presentation of the prefix for all 128 bits. */
+	$stfprefixbin = "";
+	foreach($stfarr as $element) {
+		$stfprefixbin .= sprintf("%016b", hexdec($element));
+	}
+	/* just save the left prefix length bits */
+	$stfprefixstartbin = substr($stfprefixbin, 0, $stfprefixlen);
+
+	/* if the prefix length is not 32 bits we need to shave bits off from the left of the v4 address. */
+	$stfbrokerbin = substr(sprintf("%032b", hexdec($hexbrv4)), $v4prefixlen, 32);
+	$stfbrokerbin = str_pad($stfprefixstartbin . $stfbrokerbin, 128, "0", STR_PAD_RIGHT);;
+
+	/* for the local subnet too. */
+	$stflanbin = substr(sprintf("%032b", hexdec($hexwanv4)), $v4prefixlen, 32);
+	$stflanbin = str_pad($stfprefixstartbin . $stflanbin, 128, "0", STR_PAD_RIGHT);;
+
+	/* convert the 128 bits for the broker address back into a valid IPv6 address */ 
+	$stfbrarr = array();
+	$stfbrbinarr = array();
+	$stfbrbinarr = str_split($stfbrokerbin, 16);
+	foreach($stfbrbinarr as $bin)
+		$stfbrarr[] = dechex(bindec($bin));
+	$stfbrarr[7] = 1;
+	$stfbrgw = Net_IPv6::compress(implode(":", $stfbrarr));
+
+	/* convert the 128 bits for the broker address back into a valid IPv6 address */ 
+	$stflanarr = array();
+	$stflanbinarr = array();
+	$stflanbinarr = str_split($stflanbin, 16);
+	foreach($stflanbinarr as $bin)
+		$stflanarr[] = dechex(bindec($bin));
+	$stflanpr = Net_IPv6::compress(implode(":", $stflanarr));
+	$stflanarr[7] = 1;
+	$stflan = Net_IPv6::compress(implode(":", $stflanarr));
+
+	/* setup the stf interface */
+	mwexec("/sbin/ifconfig stf0 destroy");
+	mwexec("/sbin/ifconfig stf0 create");
+	mwexec("/sbin/ifconfig stf0 inet6 {$stflanpr} prefixlen 16");
+
+	log_error("Set IPv6 address inet6 {$stflanpr} prefixlen 16 for stf0, route {$stfbrgw}");
+	
+	/* write out a default router file */
+	file_put_contents("{$g['tmp_path']}/{$wanif}_routerv6", "{$stfbrgw}");
+	file_put_contents("{$g['tmp_path']}/{$wanif}_defaultgwv6", "{$stfbrgw}");
+	
+	return 0;
+}
+
 function interface_dhcpv6_configure($interface = "wan") {
 	global $config, $g;
 	$iflist = get_configured_interface_with_descr(false, true);
diff --git a/usr/local/www/interfaces.php b/usr/local/www/interfaces.php
index 6d5a5627d9..b0594df4dc 100755
--- a/usr/local/www/interfaces.php
+++ b/usr/local/www/interfaces.php
@@ -220,6 +220,9 @@ switch($wancfg['ipaddrv6']) {
 		$pconfig['dhcp6-ia-pd-len'] = $wancfg['dhcp6-ia-pd-len'];
 		$pconfig['type6'] = "dhcp6";
 		break;
+	case "6to4":
+		$pconfig['type6'] = "6to4";
+		break;
 	case "6rd":
 		$pconfig['prefix-6rd'] = $wancfg['prefix-6rd'];
 		if($wancfg['prefix-6rd-v4plen'] == "")
@@ -500,6 +503,10 @@ if ($_POST['apply']) {
 			if (in_array($wancfg['ipaddrv6'], array()))
 				$input_errors[] = sprintf(gettext("You have to reassign the interface to be able to configure as %s."),$_POST['type']);
 			break;
+		case "6to4":
+			if (in_array($wancfg['ipaddrv6'], array()))
+				$input_errors[] = sprintf(gettext("You have to reassign the interface to be able to configure as %s."),$_POST['type']);
+			break;
 	}
 
 	
@@ -669,6 +676,7 @@ if ($_POST['apply']) {
 		/* for dynamic interfaces we tack a gateway item onto the array to prevent system
 		 * log messages from appearing. They can also manually add these items */
 		/* 1st added gateway gets a default bit */
+		/* FIXME: Add address family check here! */
 		if(!empty($a_gateways)) {
 			$gateway_item = array();
 			/* check for duplicates */
@@ -816,6 +824,9 @@ if ($_POST['apply']) {
 					$a_gateways[] = $gateway_item;
 				}
 				break;
+			case "6to4":
+				$wancfg['ipaddrv6'] = "6to4";
+				break;
 			case "none":
 				break;
 		}
@@ -1068,7 +1079,7 @@ $statusurl = "status_interfaces.php";
 $closehead = false;
 include("head.inc");
 $types4 = array("none" => gettext("None"), "staticv4" => gettext("Static IPv4"), "dhcp" => gettext("DHCP"), "ppp" => gettext("PPP"), "pppoe" => gettext("PPPoE"), "pptp" => gettext("PPTP"), "l2tp" => gettext("L2TP") /* , "carpdev-dhcp" => "CarpDev"*/);
-$types6 = array("none" => gettext("None"), "staticv6" => gettext("Static IPv6"), "dhcp6" => gettext("DHCP6"), "6rd" => gettext("6RD"));
+$types6 = array("none" => gettext("None"), "staticv6" => gettext("Static IPv6"), "dhcp6" => gettext("DHCP6"), "6rd" => gettext("6rd"), "6to4" => gettext("6to4"));
 
 ?>
 
@@ -1115,19 +1126,23 @@ $types6 = array("none" => gettext("None"), "staticv6" => gettext("Static IPv6"),
 	function updateTypeSix(t) {
 		switch(t) {
 			case "none": {
-				jQuery('#staticv6, #dhcp6, #6rd').hide();
+				jQuery('#staticv6, #dhcp6, #6rd, #6to4').hide();
 				break;
 			}
 			case "staticv6": {
-				jQuery('#none, #dhcp6, #6rd').hide();
+				jQuery('#none, #dhcp6, #6rd, #6to4').hide();
 				break;
 			}
 			case "dhcp6": {
-				jQuery('#none, #staticv6, #6rd').hide();
+				jQuery('#none, #staticv6, #6rd, #6to4').hide();
 				break;
 			}
 			case "6rd": {
-				jQuery('#none, #dhcp6, #staticv6').hide();
+				jQuery('#none, #dhcp6, #staticv6, #6to4').hide();
+				break;
+			}
+			case "6to4": {
+				jQuery('#none, #dhcp6, #staticv6, #6rd').hide();
 				break;
 			}
 		}
