Subject: bin/21275: Dhclient weirdness
To: None <>
From: None <>
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
>Originator:     John
>Release:        NetBSD 1.6

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

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.


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.


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

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:



# 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

# Function to check for a valid IP address...
# Inputs like "" and " " will fail, so will "255.255.300.255".
# Of course, "" and "" and everything in between work.
# Returns 0 on valid input, 1 for a failure
Verify_IP_Addr() {
	if [ $# -ne 1 ]; then
		return 1
	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
		return 0 # The argument is indeed strictly an IP

# 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
	export old_ip_address

# 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
	export old_routers

unset real_old_ip_address real_old_routers