]> git.ipfire.org Git - people/ms/network.git/commitdiff
dhcp: Add support for DHCPv6 over Ethernet
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 2 May 2015 15:26:31 +0000 (15:26 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 2 May 2015 15:26:31 +0000 (15:26 +0000)
Makefile.am
src/dhclient-helper
src/functions/functions.dhclient
src/functions/functions.list
src/functions/functions.ports
src/hooks/configs/ipv6-dhcp [new file with mode: 0644]

index bf3886e585b5950a9fa997ea9df5c3679e87187b..2bb27e2f28cf55d11a892310e97d93d953cff8b0 100644 (file)
@@ -182,6 +182,7 @@ dist_hooks_configs_SCRIPTS = \
        src/hooks/configs/ipv4-dhcp \
        src/hooks/configs/ipv4-static \
        src/hooks/configs/ipv6-auto \
+       src/hooks/configs/ipv6-dhcp \
        src/hooks/configs/ipv6-static \
        src/hooks/configs/pppoe-server
 
index 8401146a706b9ea0554d83daed12b56227d8e2a4..7029b5759eb9c15b7f92075750868a2b3f1a729d 100644 (file)
@@ -16,6 +16,8 @@ assert zone_exists ${interface}
 
 case "${action}" in
        start)
+               leases_file=""
+
                # Create dhclient configuration file.
                case "${proto}" in
                        ipv4)
@@ -23,12 +25,15 @@ case "${action}" in
                                ;;
                        ipv6)
                                config_file="${RUN_DIR}/dhclient/${interface}/dhclient6.conf"
+                               leases_file="/var/lib/dhclient/dhclient6-${interface}.leases"
                                ;;
                esac
                assert isset config_file
 
-               dhclient_write_config ${interface} ${config_file} \
-                       --hostname="${HOSTNAME%%.*}"
+               # Update the dhclient configuration files
+               dhclient_write_config "${interface}" \
+                       --config-file="${config_file}" \
+                       --leases-file="${leases_file}" || exit $?
 
                exit ${EXIT_OK}
                ;;
index 40cc61c52a5e760da5b34978ecc788a88e1fb992..db23aff6a7c42e3e0e185b694a5e4d7b4cdb6cd4 100644 (file)
@@ -19,6 +19,8 @@
 #                                                                             #
 ###############################################################################
 
+NETWORK_DHCP_DUID_FILE="${NETWORK_CONFIG_DIR}/dhcp-duid"
+
 dhclient_start() {
        local interface=${1}
        local proto=${2}
@@ -74,23 +76,30 @@ dhclient_proto2service() {
 }
 
 dhclient_write_config() {
-       local interface=${1}
-       local file=${2}
-       shift 2
+       local interface="${1}"
+       shift
 
        assert isset interface
-       assert isset file
 
-       local hostname=${HOSTNAME%%.*}
-       local vendor=$(distro_get_pretty_name)
+       local hostname
+       local vendor
+
+       local config_file
+       local leases_file
 
        while [ $# -gt 0 ]; do
                case "${1}" in
+                       --config-file=*)
+                               config_file="$(cli_get_val "${1}")"
+                               ;;
                        --hostname=*)
-                               hostname=$(cli_get_val ${1})
+                               hostname="$(cli_get_val "${1}")"
+                               ;;
+                       --leases-file=*)
+                               leases_file="$(cli_get_val "${1}")"
                                ;;
                        --vendor=*)
-                               vendor=$(cli_get_val ${1})
+                               vendor="$(cli_get_val "${1}")"
                                ;;
                        *)
                                log WARNING $"Unknown configuration option passed: ${1}."
@@ -99,8 +108,19 @@ dhclient_write_config() {
                shift
        done
 
+       assert isset config_file
+
+       # Set default values
+       if ! isset hostname; then
+               hostname="$(config_hostname)"
+       fi
+
+       if ! isset vendor; then
+               vendor="$(distro_get_pretty_name)"
+       fi
+
        # Clear configuration file (if any).
-       mkdir -p $(dirname ${file}) 2>/dev/null
+       mkdir -p $(dirname ${config_file}) 2>/dev/null
        : > ${file}
 
        # Print the header.
@@ -112,11 +132,11 @@ dhclient_write_config() {
                echo "# $(date -u)"
                echo "#"
                echo
-       ) >>${file}
+       ) >> "${config_file}"
 
        # Global options.
-       echo "send vendor-class-identifier \"${vendor}\";" >>${file}
-       echo
+       fwrite "${config_file}" "send vendor-class-identifier \"${vendor}\";"
+       fwrite "${config_file}" # empty line
 
        # Interface options.
        (
@@ -128,7 +148,104 @@ dhclient_write_config() {
                fi
 
                echo "}"
-       ) >>${file}
+       ) >> "${config_file}"
+
+       # Update leases file
+       if isset leases_file; then
+               __dhclient_update_leases_file "${leases_file}" || return $?
+       fi
+
+       return ${EXIT_OK}
+}
+
+dhclient_get_duid() {
+       # If the DUID already exists, we do not do anything at all.
+       if [ -s "${NETWORK_DHCP_DUID_FILE}" ]; then
+               print "$(<${NETWORK_DHCP_DUID_FILE})"
+               return ${EXIT_OK}
+       fi
+
+       # If no DUID exists, we will need to create a new one
+       local duid="$(dhclient_generate_duid)"
+       log DEBUG "Created new DHCP DUID: ${duid}"
+
+       # Save the new DUID to file and return it
+       print "${duid}" > "${NETWORK_DHCP_DUID_FILE}"
+
+       print "${duid}"
+       return ${EXIT_OK}
+}
+
+
+dhclient_generate_duid() {
+       # Find lowest MAC/link-local address
+       local address="$(ports_lowest_address)"
+
+       # Use a random MAC address if no physical address could
+       # be found.
+       if ! isset address; then
+               log WARNING "Could not determine the lowest MAC/link-local address"
+               address="$(mac_generate)"
+       fi
 
+       log DEBUG "Using '${address}' to generate the DHCP DUID"
+
+       print "00030001${address//\:/}"
        return ${EXIT_OK}
 }
+
+__dhclient_update_leases_file() {
+       local file="${1}"
+
+       local duid="$(dhclient_get_duid)"
+
+       if [ -e "${leases_file}" ]; then
+               local old_duid="$(__dhclient_get_duid_from_leases_file "${leases_file}")"
+
+               if [ "${duid}" = "${old_duid}" ]; then
+                       log DEBUG "DUID in leases file matches. Nothing to do"
+                       return ${EXIT_OK}
+               fi
+       fi
+
+       # If the leases file does not exist, yet, or the
+       # DUID in there is different, we will create/overwrite
+       # the leases file with the correct DUID.
+
+       (
+               printf "default-duid \""
+
+               local i=0
+               while [ ${i} -lt ${#duid} ]; do
+                       printf "\\\\\\\\"
+                       printf "x${duid:${i}:2}"
+                       i=$(( ${i} + 2 ))
+               done
+
+               print "\";"
+       ) > "${leases_file}"
+
+       return ${EXIT_OK}
+}
+
+__dhclient_get_duid_from_leases_file() {
+       local file="${1}"
+
+       # Do nothing if the leases file cannot be read
+       [ -r "${file}" ] || return ${EXIT_OK}
+
+       local line
+       while read line; do
+               if [[ ${line} =~ ^default-duid ]]; then
+                       line="${line/default-duid/}"
+                       line="${line//\\\\x/}"
+                       line="${line//;/}"
+
+                       line="$(strip "${line}")"
+                       unquote "${line}"
+                       return ${EXIT_OK}
+               fi
+       done < "${file}"
+
+       return ${EXIT_ERROR}
+}
index 0f11c32c3b8f38b423d1670d90eeb1553942135d..5515edb746e1195696c9c389883dff92eb1cfdf8 100644 (file)
@@ -142,3 +142,13 @@ function list_reverse() {
        print "${reversed}"
        return ${EXIT_OK}
 }
+
+function list_head() {
+       local arg
+       for arg in $@; do
+               print "${arg}"
+               return ${EXIT_OK}
+       done
+
+       return ${EXIT_ERROR}
+}
index 1edd1c30b24c11579e0a866282abd3e7d46d71ce..e3ea1f619cb387e5e4c25a653d6041b1d86e8df4 100644 (file)
@@ -403,3 +403,27 @@ function port_uses_phy() {
        local port_phy="$(port_get_phy "${port}")"
        [ "${port_phy}" = "${phy}" ]
 }
+
+function ports_lowest_address() {
+       local address
+       local addresses
+
+       local port
+       for port in $(port_list); do
+               # Skip all ports that do not exist
+               # any more or are not plugged in
+               device_exists "${port}" || continue
+
+               # Skip all ports that are not proper ethernet devices
+               device_is_wireless "${port}" && continue
+               device_is_ethernet "${port}" || continue
+
+               list_append addresses "$(device_get_address "${port}")"
+       done
+
+       # Sort the list
+       addresses="$(list_sort ${addresses})"
+
+       # Get the first element which is the lowest MAC address
+       list_head ${addresses}
+}
diff --git a/src/hooks/configs/ipv6-dhcp b/src/hooks/configs/ipv6-dhcp
new file mode 100644 (file)
index 0000000..7743775
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2010  Michael Tremer & Christian Schmidt                      #
+#                                                                             #
+# 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-config
+
+HOOK_CONFIG_SETTINGS="HOOK"
+
+function hook_create() {
+       local zone="${1}"
+       shift
+
+       zone_config_settings_write "${zone}" "${HOOK}"
+
+       exit ${EXIT_OK}
+}
+
+function hook_up() {
+       local zone="${1}"
+       local config="${2}"
+       shift 2
+
+       if ! device_exists "${zone}"; then
+               error "Zone '${zone}' doesn't exist."
+               exit ${EXIT_ERROR}
+       fi
+
+       # Start dhclient for IPv6 on this zone.
+       dhclient_start "${zone}" "ipv6"
+
+       exit ${EXIT_OK}
+}
+
+function hook_down() {
+       local zone="${1}"
+       local config="${2}"
+       shift 2
+
+       if ! device_exists "${zone}"; then
+               error "Zone '${zone}' doesn't exist."
+               exit ${EXIT_ERROR}
+       fi
+
+       # Stop dhclient for IPv6 on this zone.
+       dhclient_stop "${zone}" "ipv6"
+
+       exit ${EXIT_OK}
+}
+
+function hook_status() {
+       local zone="${1}"
+       local config="${2}"
+       shift 2
+
+       if ! device_exists "${zone}"; then
+               error "Zone '${zone}' doesn't exist."
+               exit ${EXIT_ERROR}
+       fi
+
+       zone_config_settings_read "${zone}" "${config}"
+
+       local status
+       if dhclient_status "${zone}" "ipv6"; then
+               status="${MSG_HOOK_UP}"
+       else
+               status="${MSG_HOOK_DOWN}"
+       fi
+       cli_statusline 3 "${HOOK}" "${status}"
+
+       cli_print_fmt1 3 "IPv6 address" "$(routing_db_get "${zone}" "ipv6" "local-ip-address")"
+       local gateway=$(routing_db_get ${zone} ipv6 remote-ip-address)
+       if isset gateway; then
+               cli_print_fmt1 3 "Gateway" "${gateway}"
+       fi
+       cli_space
+
+       exit ${EXIT_OK}
+}