Subject: bin/21275: Dhclient weirdness
To: None <gnats-bugs@gnats.netbsd.org>
From: None <from_netbsd@frear.com>
List: netbsd-bugs
Date: 04/23/2003 01:32:46
>Number: 21275
>Category: bin
>Synopsis: dhclient sometimes fails to set default route
>Confidential: no
>Severity: serious
>Priority: low
>Responsible: bin-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Apr 23 06:33:00 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator: John
>Release: NetBSD 1.6
>Organization:
>Environment:
System: NetBSD hal 1.6 NetBSD 1.6 (FREAR2) #0: Thu Apr 10 02:25:05 CDT 2003 user@hal:/home/user/sys/src/sys/arch/i386/compile/FREAR2 i386
Architecture: i386
Machine: i386
>Description:
Sometimes /sbin/dhclient does not pass a correct environment to
/sbin/dhclient-script and this causes the script to behave incorrectly.
I have two physical interfaces, an rtk with a static ip and a fxp with a
dynamic ip on the internet. In my case, the dynamic IP is assigned from a
DSL router unit that is the HomePortal 1000S made by 2-Wire. The router is
under my control and can be configured to assign my fxp a public or private
IP via dhcp.
When transitioning from a public to a private IP, the environment for
/sbin/dhclient-script contains reason=BOUND and does not contain the
variables old_ip_address or old_routers. Because those two variables
are unset, dhclient-script incorrectly decides not to adjust the default
route. When transitioning from a private IP to a public one, the
environment contains reason=BOUND and does have values set for old_ip_address
and old_routers, however these values are incorrect. $old_ip_address
reflects the new ip address (and is therefore equal to $new_ip_address)
and the same goes for $old_routers. This again causes dhclient-script to
decide not to update the default route.
>How-To-Repeat:
Use a netbsd 1.6 system with a dhclient configured interface, connect it
to a homeportal router and toggle the dmz setting for the netbsd interface.
>Fix:
I have fixed the problem by writing a /etc/dhclient-enter-hooks script
that correctly sets the old_ip_address and old_router variables.
This script attempts to be generic, however may not be as portable as I had
hoped.
This fix is a kludge, and a real fix is to correct /sbin/dhclient so that it
always sets the correct environment variables.
My /etc/dhclient-enter-hooks script is included:
----------------------------------------------------------------------------
#!/bin/sh
# It seems that if the IP we had been assigned is changed, there is a problem.
#
# NetBSD 1.6's /sbin/dhclient-script doesn't want to set the default route
# for a primary interface change in some circumstances.
#
# This script detects that situation and corrects it.
#
# For the detection part, the env during a failure looks like this:
# reason=BOUND
# old_ip_address != our current ip
# old_routers != the real default route
#
# For the correction part, we need to correctly set the variables
# old_ip_address
# old_routers
# First things first, the only dhclient case we need to fix is the BOUND case
# (ip addr chg). We'll do this at the VERY START because 99% of the times
# we are run will be reason=RENEW.
#
if [ \( "x$reason" != "xBOUND" \) ]; then
return 0
fi
# Function to check for a valid IP address...
#
# Inputs like " 1.2.3.4" and "1.2.3.4 " will fail, so will "255.255.300.255".
# Of course, "255.255.255.255" and "0.0.0.0" and everything in between work.
#
# Returns 0 on valid input, 1 for a failure
#
Verify_IP_Addr() {
local IP_NUM_REGEX IP_REGEX REGEX_RESULT
if [ $# -ne 1 ]; then
return 1
fi
IP_NUM_REGEX='([0-9]|[0-9][0-9]|[0,1][0-9][0-9]|2[0-4][0-9]|25[0-5])'
IP_REGEX="^$IP_NUM_REGEX\.$IP_NUM_REGEX\.$IP_NUM_REGEX\.$IP_NUM_REGEX$"
#
REGEX_RESULT="`echo "$1" | egrep "$IP_REGEX" > /dev/null 2>&1 ;echo $?`"
if [ "$REGEX_RESULT" -ne 0 ]; then
# echo Fatal: Couldn\'t determine IP\! >&2
# exit 1
return 1 # Signal a failure
else
return 0 # The argument is indeed strictly an IP
fi
}
# Load our existing IP address... Store it as $old_ip_address
#
# The line imm. below is buggy, so we use two vars and it works.. Humm??
#real_old_ip_address="`ifconfig $interface |grep inet| head -1 | cut -d\ -f 2`"
real_old_ip_address1="`ifconfig $interface |grep inet| head -1`"
real_old_ip_address="`echo $real_old_ip_address1 | cut -d\ -f 2`"
unset real_old_ip_address1
#
if Verify_IP_Addr "$real_old_ip_address"; then
old_ip_address="$real_old_ip_address"
export old_ip_address
fi
# Load our default route's IP addres... Store it as $old_routers
#
# Similarly, the line below is buggy, so the two var approach fixes it..
#real_old_routers="`netstat -nr | grep default | sed 's/^ *default *//' | \
# cut -d\ -f 1`"
real_old_routers1="`netstat -nr | grep default | head -1`"
real_old_routers="`echo $real_old_routers1 | sed 's/^ *default *//' | cut -d\ -f 1`"
unset real_old_routers1
#
if Verify_IP_Addr "$real_old_routers"; then
old_routers="$real_old_routers"
export old_routers
fi
unset real_old_ip_address real_old_routers
>Release-Note:
>Audit-Trail:
>Unformatted: