]> git.ipfire.org Git - people/ms/network.git/commitdiff
Add 6rd tunnel functionality.
authorStefan Schantl <stefan.schantl@ipfire.org>
Thu, 9 May 2013 04:26:51 +0000 (04:26 +0000)
committerStefan Schantl <stefan.schantl@ipfire.org>
Thu, 9 May 2013 04:26:51 +0000 (04:26 +0000)
Introduces a hook that can connect to 6rd border relay servers.

functions.ip-tunnel
functions.ipv6
hooks/zones/6rd [new file with mode: 0755]

index 4279d414613703b828915366cbdfce43679c1ae3..342be3c3e3587e0b3ddef50f566182d2a1466aaa 100644 (file)
@@ -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 ]
+}
index 38a74170e761c69d634be4bd8a7c08f5410f2e78..621af6349c95936afd75370ba9b9b58921c648b1 100644 (file)
@@ -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 (executable)
index 0000000..b8aafa7
--- /dev/null
@@ -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 <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=""
+
+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}
+}