From 9390b61b3b5db8131db66e936ee3cb5dfc85ba1c Mon Sep 17 00:00:00 2001 From: Stefan Schantl Date: Thu, 9 May 2013 04:26:51 +0000 Subject: [PATCH] Add 6rd tunnel functionality. Introduces a hook that can connect to 6rd border relay servers. --- functions.ip-tunnel | 36 +++++++-- functions.ipv6 | 81 +++++++++++++++++++++ hooks/zones/6rd | 174 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 8 deletions(-) create mode 100755 hooks/zones/6rd diff --git a/functions.ip-tunnel b/functions.ip-tunnel index 4279d414..342be3c3 100644 --- a/functions.ip-tunnel +++ b/functions.ip-tunnel @@ -2,7 +2,7 @@ ############################################################################### # # # IPFire.org - A linux based firewall # -# Copyright (C) 2012 IPFire Network Development Team # +# Copyright (C) 2012-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 # @@ -34,17 +34,17 @@ function ip_tunnel_add() { while [ $# -gt 0 ]; do case "${1}" in --mode=*) - mode=$(cli_get_val ${1}) + mode="$(cli_get_val ${1})" ;; --ttl=*) - ttl=$(cli_get_val ${1}) + ttl="$(cli_get_val ${1})" ;; --remote-address=*) - remote_address=$(cli_get_val ${1}) + remote_address="$(cli_get_val ${1})" ;; --local-address=*) - local_address=$(cli_get_val ${1}) + local_address="$(cli_get_val ${1})" ;; esac shift @@ -56,7 +56,6 @@ function ip_tunnel_add() { # If TTL is set, make sure it is an integer. isset ttl && assert isinteger ttl - assert isset remote_address assert isset local_address local cmd_args @@ -66,15 +65,19 @@ function ip_tunnel_add() { cmd_args="${cmd_args} ttl ${ttl}" fi + # Apply remote address if a value has been set. + if isset remote_address; then + cmd_args="${cmd_args} remote ${remote_address}" + fi + log DEBUG "Creating tunnel device '${device}' (mode=${mode})..." # Create the device. cmd ip tunnel add ${device} mode ${mode} \ - remote ${remote_address} local ${local_address} ${cmd_args} + local ${local_address} ${cmd_args} assert [ $? -eq 0 ] } - function ip_tunnel_del() { local device=${1} assert device_exists ${device} @@ -87,3 +90,20 @@ function ip_tunnel_del() { ip tunnel del ${device} assert [ $? -eq 0 ] } + +function ip_tunnel_6rd_set_prefix() { + local device="${1}" + assert isset device + + local prefix="${2}" + assert isset prefix + + # Validate the prefix. + assert ipv6_is_valid "${prefix}" + + log INFO "Setting 6rd-prefix ${prefix} on ${device}" + + # Set the prefix. + cmd ip tunnel 6rd dev "${device}" 6rd-prefix "${prefix}" + assert [ $? -eq 0 ] +} diff --git a/functions.ipv6 b/functions.ipv6 index 38a74170..621af634 100644 --- a/functions.ipv6 +++ b/functions.ipv6 @@ -160,6 +160,14 @@ function ipv6_prefix_is_valid() { return ${EXIT_TRUE} } +function ipv6_get_prefix() { + ip_get_prefix "$@" +} + +function ipv6_split_prefix() { + ip_split_prefix "$@" +} + function ipv6_implode() { local address=${1} assert isset address @@ -252,3 +260,76 @@ function ipv6_get_network() { print "${PREFIX6}/${prefix}" } + +function ipv6_6rd_format_address() { + local isp_prefix="${1}" + assert ipv6_is_valid "${isp_prefix}" + + local client_address="${2}" + assert ipv4_is_valid "${client_address}" + + local prefix="$(ipv6_get_prefix "${isp_prefix}")" + isp_prefix="$(ipv6_split_prefix "${isp_prefix}")" + + # This only works for prefix lengths up to 32 bit. + assert [ "${prefix}" -le 32 ] + assert [ "${prefix}" -gt 0 ] + + # Explode the address and throw away the second 32 bit. + local address="$(ipv6_explode "${isp_prefix}")" + + client_address="$(ipv6_6rd_format_client_address ${client_address})" + assert isset client_address + + local block1="0x${address:0:4}" + local block2="0x${address:5:4}" + local block3="0x${address:10:4}" + local block4="0x${address:15:4}" + + address="$(( (${block1} << 48) + (${block2} << 32) + (${block3} << 16) + ${block4} ))" + assert [ "${address}" -gt 0 ] + + block1="0x${client_address:0:4}" + block2="0x${client_address:5:4}" + + client_address="$(( (${block1} << 48) + (${block2} << 32) ))" + + # Fix for numbers that are interpreted by bash as negative + # numbers and therefore filled up with ones when shifted to + # the right. Weird. + if [ "${client_address}" -gt 0 ]; then + client_address="$(( ${client_address} >> ${prefix} ))" + else + local bitmask="$(( 1 << 63 ))" + client_address="$(( ${client_address} >> 1 ))" + client_address="$(( ${client_address} ^ ${bitmask} ))" + client_address="$(( ${client_address} >> $(( ${prefix} - 1 )) ))" + fi + assert [ "${client_address}" -gt 0 ] + + # XOR everything together + address="$(( ${address} ^ ${client_address} ))" + prefix="$(( ${prefix} + 32 ))" + + local block formatted_address=":" + while [ ${address} -gt 0 ]; do + printf -v block "%x" "$(( ${address} & 0xffff ))" + formatted_address="${block}:${formatted_address}" + + address="$(( ${address} >> 16 ))" + done + + assert ipv6_is_valid "${formatted_address}" + + # Implode the output IP address. + formatted_address="$(ipv6_implode "${formatted_address}")" + + print "${formatted_address}/${prefix}" +} + +function ipv6_6rd_format_client_address() { + local address="${1}" + assert isset address + + print "%02x%02x:%02x%02x" ${address//\./ } +} diff --git a/hooks/zones/6rd b/hooks/zones/6rd new file mode 100755 index 00000000..b8aafa7d --- /dev/null +++ b/hooks/zones/6rd @@ -0,0 +1,174 @@ +#!/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 . # +# # +############################################################################### + +. /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="" + +function _check() { + 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_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 +} + +function _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-address=*) + PUBLIC_ADDRESS=$(cli_get_val ${1}) + ;; + *) + echo "Unknown option: ${1}" >&2 + exit ${EXIT_ERROR} + ;; + esac + shift + done +} + +function _up() { + local zone="${1}" + assert isset zone + + # Read configuration options. + zone_config_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. + routing_db_set "${zone}" ipv6 "type" "${HOOK}" + routing_db_set "${zone}" ipv6 "local-ip-address" "::${LOCAL_ADDRESS}" + routing_db_set "${zone}" ipv6 "remote-ip-address" "::${SERVER_ADDRESS}" + routing_db_set "${zone}" ipv6 "active" 1 + + # Update the routing database. + routing_update ${zone} ipv6 + routing_default_update + + exit ${EXIT_OK} +} + +function _down() { + local zone=${1} + assert isset zone + + # Remove everything from the routing db. + routing_db_remove ${zone} ipv6 + routing_update ${zone} ipv6 + routing_default_update + + # Remove the tunnel device. + ip_tunnel_del ${zone} + + exit ${EXIT_OK} +} + +function _status() { + local zone=${1} + assert isset zone + + cli_device_headline ${zone} + + zone_config_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} +} -- 2.39.2