Subject: Re: Dhclient weirdness
To: None <netbsd-users@netbsd.org>
From: Christos Zoulas <christos@zoulas.com>
List: netbsd-users
Date: 04/16/2003 23:37:53
In article <Pine.NEB.4.53.0304161657390.16358@frear.com>,
John <from_netbsd@frear.com> wrote:
Send-pr please.
christos
>Hello!
>
>I ran across some a very strange dhclient problem and went to some effort
>to fix it -- I want to share my fix with the public.
>I haven't decided if this is a bug in dhclient or not.
>
>My netbsd 1.6 i386 machine has two interfaces, an rtk on a private network
>with a static ip and an fxp on the internet with a dynamic ip. Naturally,
>I want my routing table's default route to be the one assigned via dhcp to
>the fxp interface.
>
>The problem was, my IP changed and dhclient automatically reconfigured the
>fxp to the new IP, but it did not alter the default route. (Which caused
>loss of inet connectivity, needless to say.)
>
>I determined this is due to the environment that /sbin/dhclient passes to
>/sbin/dhclient-script.
>
>I have control of the dhcp server (its a dsl router-gadget) and can
>instruct it to assign the fxp a public IP or a private network IP. When
>transitioning from a public IP to a private one, the environment passed to
>dhclient-script does not include the variables old_ip_address or
>old_routers, and so it decides not to adjust the default route. During
>transition from a private IP to a public one, the environment does include
>those two variables, but they are wrong. old_ip_address incorrectly
>equals new_ip_address and the same goes for old_routers. So, it seems the
>problem is influenced by the dhcp server device, although /sbin/dhclient
>should still set the correct old_ values regardless.
>
>I fixed the problem by writing a /etc/dhclient-enter-hooks script. The
>hook script runs before dhclient-script makes any changes to the active
>config, and if it is run during a BOUND event it uses ifconfig to
>determine the fxp's current IP address, and it uses "netstat -nr" to
>determine the current default route. It then assigns these determined
>values to the variables old_ip_address and old_routers. This has caused
>dhclient-script to work correctly in all my testing.
>
>If someone would be kind enough to let me know if I ougth to submit a PR,
>I will be happy to do so! Should I also forward this to the ISC??
>Thanks!
>
>Oh yeah.. I might have hit upon a second unrelated bug.. In my
>dhclient-enter-hooks script, I used a command like:
>
>old_ip_address=`ifconfig fxp0 | grep inet | head -1 | cut -d\ -f 2`
>
>and this command works great during interactive testing, but results in an
>empty old_ip_address variable when the script is run via dhclient-script.
>I fixed the problem with:
>
>old_ip_address1=`ifconfig fxp0 | grep inet | head -1`
>old_ip_address=`echo $old_ip_address1 | cut -d\ -f 2`
>unset old_ip_address1
>
>Very strange..
>
>Thanks again netbsd community!!
>
>Fyi, my dhclient-enter-hooks script is attached.
>
>- John
>
>----------------------------------------------------------------------------
>
>#!/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
>
>
># This is debugging stuff -- just ignore all of this.
># (Yes, really debugging - I think we hit a bug in /bin/sh or /usr/bin/head)
>#
>#
>#(
>#echo
>#echo Date is `date`
>#
>#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
>#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
>#
>#echo Got \[$real_old_ip_address\] and \[$real_old_routers\]
>#echo Was given \[$old_ip_address\] and \[$old_routers\]
>#unset real_old_ip_address real_old_routers
>#
>#) >> /tmp/out 2>&1