471 lines
15 KiB
Plaintext
471 lines
15 KiB
Plaintext
|
#!/bin/sh
|
||
|
|
||
|
# Explicitly set the PATH to that of ENV_SUPATH in /etc/login.defs and unset
|
||
|
# various other variables. We need to do this so /sbin/dhclient cannot abuse
|
||
|
# the environment to escape AppArmor confinement via this script
|
||
|
# (LP: #1045986). This can be removed once AppArmor supports environment
|
||
|
# filtering (LP: #1045985)
|
||
|
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||
|
export ENV=
|
||
|
export BASH_ENV=
|
||
|
export CDPATH=
|
||
|
export GLOBIGNORE=
|
||
|
export BASH_XTRACEFD=
|
||
|
|
||
|
# dhclient-script for Linux. Dan Halbert, March, 1997.
|
||
|
# Updated for Linux 2.[12] by Brian J. Murrell, January 1999.
|
||
|
# Modified for Debian. Matt Zimmerman and Eloy Paris, December 2003
|
||
|
# Modified to remove useless tests for antiquated kernel versions that
|
||
|
# this doesn't even work with anyway, and introduces a dependency on /usr
|
||
|
# being mounted, which causes cosmetic errors on hosts that NFS mount /usr
|
||
|
# Andrew Pollock, February 2005
|
||
|
# Modified to work on point-to-point links. Andrew Pollock, June 2005
|
||
|
# Modified to support passing the parameters called with to the hooks. Andrew Pollock, November 2005
|
||
|
|
||
|
# The alias handling in here probably still sucks. -mdz
|
||
|
|
||
|
# log an error.
|
||
|
error() { logger -p daemon.err "$@"; }
|
||
|
|
||
|
# wait for given file to be writable
|
||
|
wait_for_rw() {
|
||
|
local file=$1
|
||
|
# Find out whether we are going to mount / rw
|
||
|
exec 9>&0 </etc/fstab
|
||
|
rootmode=rw
|
||
|
while read dev mnt type opts dump pass junk; do
|
||
|
[ "$mnt" != / ] && continue
|
||
|
case "$opts" in
|
||
|
ro|ro,*|*,ro|*,ro,*)
|
||
|
rootmode=ro
|
||
|
;;
|
||
|
esac
|
||
|
done
|
||
|
exec 0>&9 9>&-
|
||
|
|
||
|
# Wait for $file to become writable
|
||
|
if [ "$rootmode" = "rw" ]; then
|
||
|
while ! { : >> "$file"; } 2>/dev/null; do
|
||
|
sleep 0.1
|
||
|
done
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# update /etc/resolv.conf based on received values
|
||
|
make_resolv_conf() {
|
||
|
local new_resolv_conf
|
||
|
|
||
|
# DHCPv4
|
||
|
if [ -n "$new_domain_search" ] || [ -n "$new_domain_name" ] ||
|
||
|
[ -n "$new_domain_name_servers" ]; then
|
||
|
resolv_conf=$(readlink -f "/etc/resolv.conf" 2>/dev/null) ||
|
||
|
resolv_conf="/etc/resolv.conf"
|
||
|
|
||
|
new_resolv_conf="${resolv_conf}.dhclient-new.$$"
|
||
|
wait_for_rw "$new_resolv_conf"
|
||
|
rm -f $new_resolv_conf
|
||
|
|
||
|
if [ -n "$new_domain_name" ]; then
|
||
|
echo domain ${new_domain_name%% *} >>$new_resolv_conf
|
||
|
fi
|
||
|
|
||
|
if [ -n "$new_domain_search" ]; then
|
||
|
if [ -n "$new_domain_name" ]; then
|
||
|
domain_in_search_list=""
|
||
|
for domain in $new_domain_search; do
|
||
|
if [ "$domain" = "${new_domain_name}" ] ||
|
||
|
[ "$domain" = "${new_domain_name}." ]; then
|
||
|
domain_in_search_list="Yes"
|
||
|
fi
|
||
|
done
|
||
|
if [ -z "$domain_in_search_list" ]; then
|
||
|
new_domain_search="$new_domain_name $new_domain_search"
|
||
|
fi
|
||
|
fi
|
||
|
echo "search ${new_domain_search}" >> $new_resolv_conf
|
||
|
elif [ -n "$new_domain_name" ]; then
|
||
|
echo "search ${new_domain_name}" >> $new_resolv_conf
|
||
|
fi
|
||
|
|
||
|
if [ -n "$new_domain_name_servers" ]; then
|
||
|
for nameserver in $new_domain_name_servers; do
|
||
|
echo nameserver $nameserver >>$new_resolv_conf
|
||
|
done
|
||
|
else # keep 'old' nameservers
|
||
|
sed -n /^\w*[Nn][Aa][Mm][Ee][Ss][Ee][Rr][Vv][Ee][Rr]/p $resolv_conf >>$new_resolv_conf
|
||
|
fi
|
||
|
|
||
|
if [ -f $resolv_conf ]; then
|
||
|
chown --reference=$resolv_conf $new_resolv_conf
|
||
|
chmod --reference=$resolv_conf $new_resolv_conf
|
||
|
fi
|
||
|
mv -f $new_resolv_conf $resolv_conf
|
||
|
# DHCPv6
|
||
|
elif [ -n "$new_dhcp6_domain_search" ] || [ -n "$new_dhcp6_name_servers" ]; then
|
||
|
resolv_conf=$(readlink -f "/etc/resolv.conf" 2>/dev/null) ||
|
||
|
resolv_conf="/etc/resolv.conf"
|
||
|
|
||
|
new_resolv_conf="${resolv_conf}.dhclient-new.$$"
|
||
|
wait_for_rw "$new_resolv_conf"
|
||
|
rm -f $new_resolv_conf
|
||
|
|
||
|
if [ -n "$new_dhcp6_domain_search" ]; then
|
||
|
echo "search ${new_dhcp6_domain_search}" >> $new_resolv_conf
|
||
|
fi
|
||
|
|
||
|
if [ -n "$new_dhcp6_name_servers" ]; then
|
||
|
for nameserver in $new_dhcp6_name_servers; do
|
||
|
# append %interface to link-local-address nameservers
|
||
|
if [ "${nameserver##fe80::}" != "$nameserver" ] ||
|
||
|
[ "${nameserver##FE80::}" != "$nameserver" ]; then
|
||
|
nameserver="${nameserver}%${interface}"
|
||
|
fi
|
||
|
echo nameserver $nameserver >>$new_resolv_conf
|
||
|
done
|
||
|
else # keep 'old' nameservers
|
||
|
sed -n /^\w*[Nn][Aa][Mm][Ee][Ss][Ee][Rr][Vv][Ee][Rr]/p $resolv_conf >>$new_resolv_conf
|
||
|
fi
|
||
|
|
||
|
if [ -f $resolv_conf ]; then
|
||
|
chown --reference=$resolv_conf $new_resolv_conf
|
||
|
chmod --reference=$resolv_conf $new_resolv_conf
|
||
|
fi
|
||
|
mv -f $new_resolv_conf $resolv_conf
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# set host name
|
||
|
set_hostname() {
|
||
|
if [ -n "$new_host_name" ]; then
|
||
|
local current_hostname=$(hostname)
|
||
|
|
||
|
# current host name is empty, '(none)' or 'localhost' or differs from new one from DHCP
|
||
|
if [ -z "$current_hostname" ] ||
|
||
|
[ "$current_hostname" = '(none)' ] ||
|
||
|
[ "$current_hostname" = 'localhost' ] ||
|
||
|
[ "$current_hostname" = "$old_host_name" ]; then
|
||
|
if [ "$new_host_name" != "$current_host_name" ]; then
|
||
|
hostname "$new_host_name"
|
||
|
fi
|
||
|
fi
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# set the link up and wait for ipv6 link local dad to finish
|
||
|
ipv6_link_up_and_dad() {
|
||
|
local dev=$1 delay=${2:-0.1} attempts=${3:-60}
|
||
|
ip link set up dev "$dev" ||
|
||
|
{ error "$dev: failed to set link up"; return 1; }
|
||
|
local n=0
|
||
|
while :; do
|
||
|
n=$((n+1))
|
||
|
# note: busybox ip does not understand 'tentative' as input
|
||
|
# so we cannot just use the tentative flag and check for empty
|
||
|
out=$(ip -6 -o address show dev "$dev" scope link) || {
|
||
|
error "$dev: checking for link-local addresses failed";
|
||
|
return 1
|
||
|
}
|
||
|
# another note: the output may be empty if the link local tentative addr
|
||
|
# isn't up just yet, so we need to make sure there is at least one 'inet6'
|
||
|
# match before returning success. We need to keep checking for both
|
||
|
# 'tentative' case and default (no inet6 address) case. (LP: #1718568)
|
||
|
# Don't reorder tentative/inet6 - we need to check for tentative first.
|
||
|
case " $out " in
|
||
|
*\ dadfailed\ *)
|
||
|
error "$dev: ipv6 dad failed."
|
||
|
return 1;;
|
||
|
*\ tentative\ *) :;;
|
||
|
*\ inet6\ *) return 0;;
|
||
|
*) :;;
|
||
|
esac
|
||
|
[ $n -lt $attempts ] || {
|
||
|
error "$dev: time out waiting for permanent link-local address"
|
||
|
return 1;
|
||
|
}
|
||
|
sleep $delay
|
||
|
done
|
||
|
}
|
||
|
|
||
|
# run given script
|
||
|
run_hook() {
|
||
|
local script="$1"
|
||
|
local exit_status=0
|
||
|
|
||
|
if [ -f $script ]; then
|
||
|
. $script
|
||
|
exit_status=$?
|
||
|
fi
|
||
|
|
||
|
if [ -n "$exit_status" ] && [ "$exit_status" -ne 0 ]; then
|
||
|
logger -p daemon.err "$script returned non-zero exit status $exit_status"
|
||
|
fi
|
||
|
|
||
|
return $exit_status
|
||
|
}
|
||
|
|
||
|
# run scripts in given directory
|
||
|
run_hookdir() {
|
||
|
local dir="$1"
|
||
|
local exit_status=0
|
||
|
|
||
|
if [ -d "$dir" ]; then
|
||
|
for script in $(run-parts --list $dir); do
|
||
|
run_hook $script
|
||
|
exit_status=$((exit_status|$?))
|
||
|
done
|
||
|
fi
|
||
|
|
||
|
return $exit_status
|
||
|
}
|
||
|
|
||
|
# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
|
||
|
exit_with_hooks() {
|
||
|
local exit_status=$1
|
||
|
|
||
|
# Source the documented exit-hook script, if it exists
|
||
|
if ! run_hook /etc/dhcp/dhclient-exit-hooks; then
|
||
|
exit_status=$?
|
||
|
fi
|
||
|
|
||
|
# Now run scripts in the Debian-specific directory.
|
||
|
if ! run_hookdir /etc/dhcp/dhclient-exit-hooks.d; then
|
||
|
exit_status=$?
|
||
|
fi
|
||
|
|
||
|
exit $exit_status
|
||
|
}
|
||
|
|
||
|
|
||
|
# The 576 MTU is only used for X.25 and dialup connections
|
||
|
# where the admin wants low latency. Such a low MTU can cause
|
||
|
# problems with UDP traffic, among other things. As such,
|
||
|
# disallow MTUs from 576 and below by default, so that broken
|
||
|
# MTUs are ignored, but higher stuff is allowed (1492, 1500, etc).
|
||
|
if [ -z "$new_interface_mtu" ] || [ "$new_interface_mtu" -le 576 ]; then
|
||
|
new_interface_mtu=''
|
||
|
fi
|
||
|
|
||
|
|
||
|
# The action starts here
|
||
|
|
||
|
# Invoke the local dhcp client enter hooks, if they exist.
|
||
|
run_hook /etc/dhcp/dhclient-enter-hooks
|
||
|
run_hookdir /etc/dhcp/dhclient-enter-hooks.d
|
||
|
|
||
|
# Execute the operation
|
||
|
case "$reason" in
|
||
|
|
||
|
### DHCPv4 Handlers
|
||
|
|
||
|
MEDIUM|ARPCHECK|ARPSEND)
|
||
|
# Do nothing
|
||
|
;;
|
||
|
PREINIT)
|
||
|
# The DHCP client is requesting that an interface be
|
||
|
# configured as required in order to send packets prior to
|
||
|
# receiving an actual address. - dhclient-script(8)
|
||
|
|
||
|
# ensure interface is up
|
||
|
ip link set dev ${interface} up
|
||
|
|
||
|
if [ -n "$alias_ip_address" ]; then
|
||
|
# flush alias IP from interface
|
||
|
ip -4 addr flush dev ${interface} label ${interface}:0
|
||
|
fi
|
||
|
|
||
|
;;
|
||
|
|
||
|
BOUND|RENEW|REBIND|REBOOT)
|
||
|
set_hostname
|
||
|
|
||
|
if [ -n "$old_ip_address" ] && [ -n "$alias_ip_address" ] &&
|
||
|
[ "$alias_ip_address" != "$old_ip_address" ]; then
|
||
|
# alias IP may have changed => flush it
|
||
|
ip -4 addr flush dev ${interface} label ${interface}:0
|
||
|
fi
|
||
|
|
||
|
if [ -n "$old_ip_address" ] &&
|
||
|
[ "$old_ip_address" != "$new_ip_address" ]; then
|
||
|
# leased IP has changed => flush it
|
||
|
ip -4 addr flush dev ${interface} label ${interface}
|
||
|
fi
|
||
|
|
||
|
if [ -z "$old_ip_address" ] ||
|
||
|
[ "$old_ip_address" != "$new_ip_address" ] ||
|
||
|
[ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ]; then
|
||
|
# new IP has been leased or leased IP changed => set it
|
||
|
ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \
|
||
|
${new_broadcast_address:+broadcast $new_broadcast_address} \
|
||
|
dev ${interface} label ${interface}
|
||
|
|
||
|
if [ -n "$new_interface_mtu" ]; then
|
||
|
# set MTU
|
||
|
ip link set dev ${interface} mtu ${new_interface_mtu}
|
||
|
fi
|
||
|
|
||
|
# if we have $new_rfc3442_classless_static_routes then we have to
|
||
|
# ignore $new_routers entirely
|
||
|
if [ ! "$new_rfc3442_classless_static_routes" ]; then
|
||
|
# set if_metric if IF_METRIC is set or there's more than one router
|
||
|
if_metric="$IF_METRIC"
|
||
|
if [ "${new_routers%% *}" != "${new_routers}" ]; then
|
||
|
if_metric=${if_metric:-1}
|
||
|
fi
|
||
|
|
||
|
for router in $new_routers; do
|
||
|
if [ "$new_subnet_mask" = "255.255.255.255" ]; then
|
||
|
# point-to-point connection => set explicit route
|
||
|
ip -4 route add ${router} dev $interface >/dev/null 2>&1
|
||
|
fi
|
||
|
|
||
|
# set default route
|
||
|
ip -4 route add default via ${router} dev ${interface} \
|
||
|
${if_metric:+metric $if_metric} >/dev/null 2>&1
|
||
|
|
||
|
if [ -n "$if_metric" ]; then
|
||
|
if_metric=$((if_metric+1))
|
||
|
fi
|
||
|
done
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
if [ -n "$alias_ip_address" ] &&
|
||
|
[ "$new_ip_address" != "$alias_ip_address" ]; then
|
||
|
# separate alias IP given, which may have changed
|
||
|
# => flush it, set it & add host route to it
|
||
|
ip -4 addr flush dev ${interface} label ${interface}:0
|
||
|
ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \
|
||
|
dev ${interface} label ${interface}:0
|
||
|
ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
|
||
|
fi
|
||
|
|
||
|
# update /etc/resolv.conf
|
||
|
make_resolv_conf
|
||
|
|
||
|
;;
|
||
|
|
||
|
EXPIRE|FAIL|RELEASE|STOP)
|
||
|
if [ -n "$alias_ip_address" ]; then
|
||
|
# flush alias IP
|
||
|
ip -4 addr flush dev ${interface} label ${interface}:0
|
||
|
fi
|
||
|
|
||
|
if [ -n "$old_ip_address" ]; then
|
||
|
# flush leased IP
|
||
|
ip -4 addr flush dev ${interface} label ${interface}
|
||
|
fi
|
||
|
|
||
|
if [ -n "$alias_ip_address" ]; then
|
||
|
# alias IP given => set it & add host route to it
|
||
|
ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \
|
||
|
dev ${interface} label ${interface}:0
|
||
|
ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
|
||
|
fi
|
||
|
|
||
|
;;
|
||
|
|
||
|
TIMEOUT)
|
||
|
if [ -n "$alias_ip_address" ]; then
|
||
|
# flush alias IP
|
||
|
ip -4 addr flush dev ${interface} label ${interface}:0
|
||
|
fi
|
||
|
|
||
|
# set IP from recorded lease
|
||
|
ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \
|
||
|
${new_broadcast_address:+broadcast $new_broadcast_address} \
|
||
|
dev ${interface} label ${interface}
|
||
|
|
||
|
if [ -n "$new_interface_mtu" ]; then
|
||
|
# set MTU
|
||
|
ip link set dev ${interface} mtu ${new_interface_mtu}
|
||
|
fi
|
||
|
|
||
|
# if there is no router recorded in the lease or the 1st router answers pings
|
||
|
if [ -z "$new_routers" ] || ping -q -c 1 "${new_routers%% *}"; then
|
||
|
# if we have $new_rfc3442_classless_static_routes then we have to
|
||
|
# ignore $new_routers entirely
|
||
|
if [ ! "$new_rfc3442_classless_static_routes" ]; then
|
||
|
if [ -n "$alias_ip_address" ] &&
|
||
|
[ "$new_ip_address" != "$alias_ip_address" ]; then
|
||
|
# separate alias IP given => set up the alias IP & add host route to it
|
||
|
ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \
|
||
|
dev ${interface} label ${interface}:0
|
||
|
ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
|
||
|
fi
|
||
|
|
||
|
# set if_metric if IF_METRIC is set or there's more than one router
|
||
|
if_metric="$IF_METRIC"
|
||
|
if [ "${new_routers%% *}" != "${new_routers}" ]; then
|
||
|
if_metric=${if_metric:-1}
|
||
|
fi
|
||
|
|
||
|
# set default route
|
||
|
for router in $new_routers; do
|
||
|
ip -4 route add default via ${router} dev ${interface} \
|
||
|
${if_metric:+metric $if_metric} >/dev/null 2>&1
|
||
|
|
||
|
if [ -n "$if_metric" ]; then
|
||
|
if_metric=$((if_metric+1))
|
||
|
fi
|
||
|
done
|
||
|
fi
|
||
|
|
||
|
# update /etc/resolv.conf
|
||
|
make_resolv_conf
|
||
|
else
|
||
|
# flush all IPs from interface
|
||
|
ip -4 addr flush dev ${interface}
|
||
|
exit_with_hooks 2
|
||
|
fi
|
||
|
|
||
|
;;
|
||
|
|
||
|
### DHCPv6 Handlers
|
||
|
# TODO handle prefix change: ?based on ${old_ip6_prefix} and ${new_ip6_prefix}?
|
||
|
|
||
|
PREINIT6)
|
||
|
# ensure interface is up
|
||
|
ipv6_link_up_and_dad "$interface"
|
||
|
|
||
|
# flush any stale global permanent IPs from interface
|
||
|
ip -6 addr flush dev ${interface} scope global permanent
|
||
|
|
||
|
;;
|
||
|
|
||
|
BOUND6|RENEW6|REBIND6)
|
||
|
if [ "${new_ip6_address}" ]; then
|
||
|
# set leased IP
|
||
|
ip -6 addr add ${new_ip6_address} \
|
||
|
dev ${interface} scope global
|
||
|
fi
|
||
|
|
||
|
# update /etc/resolv.conf
|
||
|
if [ "${reason}" = BOUND6 ] ||
|
||
|
[ "${new_dhcp6_name_servers}" != "${old_dhcp6_name_servers}" ] ||
|
||
|
[ "${new_dhcp6_domain_search}" != "${old_dhcp6_domain_search}" ]; then
|
||
|
make_resolv_conf
|
||
|
fi
|
||
|
|
||
|
;;
|
||
|
|
||
|
DEPREF6)
|
||
|
# set preferred lifetime of leased IP to 0
|
||
|
ip -6 addr change ${cur_ip6_address} \
|
||
|
dev ${interface} scope global preferred_lft 0
|
||
|
|
||
|
;;
|
||
|
|
||
|
EXPIRE6|RELEASE6|STOP6)
|
||
|
if [ -z "${old_ip6_address}" ]; then
|
||
|
exit_with_hooks 2
|
||
|
fi
|
||
|
|
||
|
# delete leased IP
|
||
|
ip -6 addr del ${old_ip6_address} \
|
||
|
dev ${interface}
|
||
|
|
||
|
;;
|
||
|
esac
|
||
|
|
||
|
exit_with_hooks 0
|