#!/bin/bash ############################################################################### # # # IPFire.org - A linux based firewall # # 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 # # 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 . # # # ############################################################################### IP_TUNNEL_MODES="gre gretap sit vti" ip_tunnel_protocol_to_name() { local protocol="${1}" case "${protocol}" in gre) print "Generic Routing Encapsulation" ;; sit) print "Simple Internet Transition" ;; vti) print "Virtual Tunnel Interface" ;; *) print "${protocol}" ;; esac } # This function converts our modes into the type # the iproute2 tool uses ip_tunnel_convert_mode_to_iproute2_mode() { local mode=${1} local protocol=${2} if ! isset mode || ! isset protocol; then log ERROR "Did not get mode and/or protocol" return ${EXIT_ERROR} fi if [[ "${protocol}" = "ipv4" ]]; then # When we use IPv4 we can use our modes echo "${mode}" fi if [[ "${protocol}" = "ipv6" ]]; then # When we use IPv6 we have to convert case "${mode}" in "vti") echo "vti6" ;; "gre") echo "ip6gre" ;; "gretap") echo "ip6gretap" ;; esac fi } ip_tunnel_add() { local device="${1}" shift local mode local ttl=255 local address local remote_address local local_address local ikey local okey while [ $# -gt 0 ]; do case "${1}" in --address=*) address="$(cli_get_val "${1}")" # Validate input if ! isset address || ! mac_is_valid "${address}"; then error "Invalid MAC address: ${address}" return ${EXIT_ERROR} fi ;; --mode=*) mode="$(cli_get_val "${1}")" ;; --ttl=*) ttl="$(cli_get_val "${1}")" ;; --remote-address=*) remote_address="$(cli_get_val "${1}")" ;; --local-address=*) local_address="$(cli_get_val "${1}")" ;; # Keys for VTI --ikey=*) ikey="$(cli_get_val "${1}")" ;; --okey=*) okey="$(cli_get_val "${1}")" ;; esac shift done if ! isset mode; then error "--mode= is not set. Must be one of ${IP_TUNNEL_MODES}" return ${EXIT_ERROR} fi if ! isoneof mode ${IP_TUNNEL_MODES}; then error "Invalid mode: ${mode}" return ${EXIT_ERROR} fi # We cannot mix IPv6 and IPv4 if isset local_address && ! ip_protocol_match "${remote_address}" "${local_address}"; then log ERROR "Local and remote address are not of the same IP protocol" return ${EXIT_ERROR} fi # ikey and okey must be set for VTI devices if [ "${mode}" = "vti" ] && (! isset ikey || ! isset okey); then error "--ikey= and --okey= must be set for VTI device" return ${EXIT_ERROR} fi # Custom checks for certain modes case "${mode}" in gretap) # Generate a random MAC address if none was passed if ! isset address; then address="$(mac_generate)" fi ;; esac # If TTL is set, make sure it is an integer. if isset ttl && ! isinteger ttl; then error "TTL must be an integer: ${ttl}" return ${EXIT_ERROR} fi # Determine the mode based on the IP protocol local remote_address_protocol="$(ip_detect_protocol "${remote_address}")" mode=$(ip_tunnel_convert_mode_to_iproute2_mode "${mode}" "${remote_address_protocol}") local cmd_args=( name "${device}" ) if isset address; then cmd_args=( "${cmd_args[@]}" "address" "${address}" ) fi # Mode cmd_args=( "${cmd_args[@]}" "type" "${mode}" ) # Apply TTL if a value has been set. if isset ttl; then cmd_args=( "${cmd_args[@]}" "ttl" "${ttl}" ) fi # Apply local address if a value has been set. if isset local_address; then cmd_args=( "${cmd_args[@]}" "local" "${local_address}" ) fi # Apply remote address if a value has been set. if isset remote_address; then cmd_args=( "${cmd_args[@]}" "remote" "${remote_address}" ) fi # Add ikey and okey for VTI devices if [ "${mode}" = "vti" ]; then cmd_args=( "${cmd_args[@]}" "ikey" "${ikey}" "okey" "${okey}" ) fi log DEBUG "Creating tunnel device '${device}' (mode=${mode})..." # Create the device. if ! cmd ip link add "${cmd_args[@]}"; then error "Could not create tunnel device ${device}" return ${EXIT_ERROR} fi # Disable policy lookups for VTI devices if [ "${mode}" = "vti" ]; then sysctl_set "net.ipv4.conf.${device}.disable_policy" "1" fi return ${EXIT_OK} } ip_tunnel_del() { device_delete "$@" } ip_tunnel_change() { local device="${1}" shift if ! device_exists "${device}"; then log ERROR "No such device: ${device}" return ${EXIT_ERROR} fi # Determine the device type local type="$(device_tunnel_get_type ${device})" local local local remote while [ $# -gt 0 ]; do case "${1}" in --local=*) local="$(cli_get_val "${1}")" if ! ip_is_valid "${local}"; then error "Invalid IP address for --local: ${local}" return ${EXIT_ERROR} fi if ! isoneof "type" gre gre6 vti vti6; then log ERROR "Cannot change --local for devices of type ${type}" return ${EXIT_ERROR} fi ;; --remote=*) remote="$(cli_get_val "${1}")" if ! ip_is_valid "${remote}"; then error "Invalid IP address for --remote: ${remote}" return ${EXIT_ERROR} fi if ! isoneof "type" gre gre6 vti vti6; then log ERROR "Cannot change --remote for devices of type ${type}" return ${EXIT_ERROR} fi ;; esac shift done # XXX If a device is of an IP protocol and the protocol of remote and local # have changed, we will need to destroy the interface and recreate it with # the correct type local cmd_args if isset local; then cmd_args="${cmd_args} local ${local}" fi if isset remote; then cmd_args="${cmd_args} remote ${remote}" fi # Exit if there is nothing to do if ! isset cmd_args; then return ${EXIT_OK} fi # Run ip command cmd ip link change dev "${device}" type "${type}" ${cmd_args} }