#!/bin/bash
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2013  IPFire Network Development Team                         #
#                                                                             #
# This program is free software: you can redistribute it and/or modify        #
# it under the terms of the GNU General Public License as published by        #
# the Free Software Foundation, either version 3 of the License, or           #
# (at your option) any later version.                                         #
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
# GNU General Public License for more details.                                #
#                                                                             #
# You should have received a copy of the GNU General Public License           #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
#                                                                             #
###############################################################################

. /usr/lib/network/header-zone

HOOK_SETTINGS="HOOK SIX_RD_PREFIX LOCAL_ADDRESS PUBLIC_ADDRESS SERVER_ADDRESS"

# The address that is assigned to the tunnel device (with prefix).
SIX_RD_PREFIX=""

# The local IPv4 address of the tunnel endpoint.
# For usage if the endpoint is in a pre-routed network.
LOCAL_ADDRESS=""

# The IPv4 address of the tunnel endpoint where to connect to.
SERVER_ADDRESS=""

# The public IPv4 address of the tunnel client.
PUBLIC_ADDRESS=""

hook_check_settings() {
	assert isset SIX_RD_PREFIX
	assert isset PUBLIC_ADDRESS
	assert isset SERVER_ADDRESS

	# Check if an optional local address has been specified or use the public address instead.
	if [ -z "${LOCAL_ADDRESS}" ]; then
		LOCAL_ADDRESS="${PUBLIC_ADDRESS}"
	fi

	assert isset LOCAL_ADDRESS

	# Check input.
	if ! ipv6_net_is_valid "${SIX_RD_PREFIX}"; then
		log ERROR "Invalid 6rd prefix. Please use a valid IPv6 prefix."
		return ${EXIT_ERROR}
	fi

	if ! ipv4_is_valid "${SERVER_ADDRESS}"; then
		log ERROR "Invalid server address. Please use a valid IPv4 address."
		return ${EXIT_ERROR}
	fi

	if ! ipv4_is_valid "${PUBLIC_ADDRESS}"; then
		log ERROR "Invalid public address. Please use a valid IPv4 address."
		return ${EXIT_ERROR}
	fi

	if ! ipv4_is_valid "${LOCAL_ADDRESS}"; then
		log ERROR "Invalid local address. Please use a valid IPv4 address."
		return ${EXIT_ERROR}
	fi
}

hook_parse_cmdline() {
	local value

	while [ $# -gt 0 ]; do
		case "${1}" in
			--6rd-prefix=*)
				SIX_RD_PREFIX=$(cli_get_val ${1})
				;;
			--server-address=*)
				SERVER_ADDRESS=$(cli_get_val ${1})
				;;
			--local-ipv4-address=*)
				LOCAL_ADDRESS=$(cli_get_val ${1})
				;;
			--public-ipv4-address=*)
				PUBLIC_ADDRESS=$(cli_get_val ${1})
				;;
			*)
				echo "Unknown option: ${1}" >&2
				exit ${EXIT_ERROR}
				;;
		esac
		shift
	done
}

hook_up() {
	local zone="${1}"
	assert isset zone

	# Read configuration options.
	zone_settings_read "${zone}"

	# Configure the tunnel.
	if ! device_exists "${zone}"; then
		ip_tunnel_add "${zone}" \
			--ttl=64 \
			--local-address="${LOCAL_ADDRESS}"
	fi

	# Set 6rd prefix.
	ip_tunnel_6rd_set_prefix "${zone}" "${SIX_RD_PREFIX}"

	# Bring up the device.
	device_set_up "${zone}"

	# Update routing information.
	db_set "${zone}/ipv6/type" "${HOOK}"
	db_set "${zone}/ipv6/local-ip-address" "::${LOCAL_ADDRESS}"
	db_set "${zone}/ipv6/remote-ip-address" "::${SERVER_ADDRESS}"
	db_set "${zone}/ipv6/active" 1

	# Update the routing database.
	routing_update ${zone} ipv6
	routing_default_update

	exit ${EXIT_OK}
}

hook_down() {
	local zone=${1}
	assert isset zone

	# Remove everything from the routing db.
	db_delete "${zone}/ipv6"

	routing_update ${zone} ipv6
	routing_default_update

	# Remove the tunnel device.
	ip_tunnel_del ${zone}

	exit ${EXIT_OK}
}

hook_status() {
	local zone=${1}
	assert isset zone

	cli_device_headline ${zone}

	zone_settings_read "${zone}"

	local server_line="${SERVER_ADDRESS}"
	local server_hostname=$(dns_get_hostname ${SERVER_ADDRESS})
	if [ -n "${server_hostname}" ]; then
		server_line="${server_line} (Hostname: ${server_hostname})"
	fi

	cli_headline 2 "Configuration"
	cli_print_fmt1 2 "Server" "${server_line}"
	cli_print_fmt1 2 "6rd Prefix" "${SIX_RD_PREFIX}"
	cli_space

	# Generate the IPv6 prefix from the given 6rd Prefix and the Public IPv4 Address.
	local six_rd_address="$(ipv6_6rd_format_address "${SIX_RD_PREFIX}" "${PUBLIC_ADDRESS}")"

	cli_headline 2 "Tunnel properties"
	cli_print_fmt1 2 "IPv6 Subnet" "${six_rd_address}"
	cli_space

	exit ${EXIT_OK}
}
