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: