#!/usr/local/bin/php -f
<?php
/*
 * ppp-ipv6
 *
 * part of pfSense (https://www.pfsense.org)
 * Copyright (c) 2004-2013 BSD Perimeter
 * Copyright (c) 2013-2016 Electric Sheep Fencing
 * Copyright (c) 2014-2026 Rubicon Communications, LLC (Netgate)
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

require_once("globals.inc");
require_once("interfaces.inc");

function interface_ipv6_lower($interface_real) {
	global $g;

	if (empty($interface_real)) {
		return;
	}

	$interface = convert_real_interface_to_friendly_interface_name($interface_real);
	if (empty($interface)) {
		return;
	}

	$ifcfg = config_get_path("interfaces/{$interface}");
	if (empty($interface) || !is_array($ifcfg) ||
	    !interface_isppp_type($interface) || empty($ifcfg['ipaddrv6'])) {
		return;
	}

	switch ($ifcfg['ipaddrv6']) {
	case 'slaac':
	case 'dhcp6':
		// Take no action if dhcp6 is active on the parent interface, not the PPP interface
		if ((($ifcfg['ipaddrv6'] == 'dhcp6') && !isset($ifcfg['dhcp6usev4iface'])) ||
		    (($ifcfg['ipaddrv6'] == 'slaac') && !isset($ifcfg['slaacusev4iface'])) ||
		    !interface_isppp_type($interface)) {
			break;
		}
		// reconfigure dhcp6c if it is running
		interface_dhcpv6_configure($interface, $ifcfg, true);

		// disable router advertisements (and therefore SLAAC)
		mwexec("/sbin/ifconfig " . escapeshellarg($interface_real) . " inet6 -accept_rtadv");

		// remove any autoconfigured IPv6 addresses
		exec("/sbin/ifconfig " . escapeshellarg($interface_real) . " inet6", $ifconfig_output);
		foreach ($ifconfig_output as $output) {
			/* FIXME there should be a better way to do it */
			if (preg_match('{ \A \s+ inet6 \s+ (\S+) .* autoconf .* \Z}xmsi', $output, $matches)) {
				mwexec("/sbin/ifconfig " . escapeshellarg($interface_real) . " inet6 " . escapeshellarg($matches[1]) . " delete");
			}
		}
		break;
	default:
		break;
	}
}

function interface_ipv6_raise($interface_real) {
	if (empty($interface_real)) {
		return;
	}

	$interface = convert_real_interface_to_friendly_interface_name($interface_real);
	if (empty($interface)) {
		return;
	}

	$ifcfg = config_get_path("interfaces/{$interface}");
	if (empty($interface) || !is_array($ifcfg) ||
	    !interface_isppp_type($interface) || empty($ifcfg['ipaddrv6'])) {
		return;
	}

	switch ($ifcfg['ipaddrv6']) {
	case 'slaac':
	case 'dhcp6':
		// Take no action if dhcp6 is active on the parent interface, not the PPP interface
		if ((($ifcfg['ipaddrv6'] == 'dhcp6') && !isset($ifcfg['dhcp6usev4iface'])) ||
		    (($ifcfg['ipaddrv6'] == 'slaac') && !isset($ifcfg['slaacusev4iface'])) ||
		    !interface_isppp_type($interface)) {
			break;
		}
		interface_dhcpv6_configure($interface, $ifcfg);
		break;
	default:
		break;
	}
}

// main entry point
if ($argc != 3) {
	goto error;
}

$interface_real = trim($argv[1], " \n\t");
if (empty($interface_real)) {
	goto error;
}

switch (strtolower($argv[2])) {
case 'up':
	interface_ipv6_raise($interface_real);
	break;
case 'down':
	interface_ipv6_lower($interface_real);
	break;
default:
	goto error;
	break;
}

exit(0);

error:
if (!empty($argv[0])) {
	echo("Usage: " . substr(strrchr('/' . $argv[0], '/'), 1) . " <PPP interface> up|down\n");
}
exit(1);

?>
