1#!/bin/sh 2# 3# $OpenBSD: dhclient-script,v 1.6 2004/05/06 18:22:41 claudio Exp $ 4# 5# Copyright (c) 2003 Kenneth R Westerback <krw@openbsd.org> 6# 7# Permission to use, copy, modify, and distribute this software for any 8# purpose with or without fee is hereby granted, provided that the above 9# copyright notice and this permission notice appear in all copies. 10# 11# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18# 19# 20 21ARP=/usr/sbin/arp 22HOSTNAME=/bin/hostname 23IFCONFIG='/sbin/ifconfig -n' 24 25LOCALHOST=127.0.0.1 26 27if [ -x /usr/bin/logger ]; then 28 LOGGER="/usr/bin/logger -s -p user.notice -t dhclient" 29else 30 LOGGER=echo 31fi 32 33# 34# Helper functions that implement common actions. 35# 36 37check_hostname() { 38 current_hostname=`$HOSTNAME` 39 if [ -z "$current_hostname" ]; then 40 $LOGGER "New Hostname ($interface): $new_host_name" 41 $HOSTNAME $new_host_name 42 elif [ "$current_hostname" = "$old_host_name" -a \ 43 "$new_host_name" != "$old_host_name" ]; then 44 $LOGGER "New Hostname ($interface): $new_host_name" 45 $HOSTNAME $new_host_name 46 fi 47} 48 49arp_flush() { 50 arp -an -i $interface | \ 51 sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' | \ 52 sh >/dev/null 2>&1 53} 54 55delete_old_address() { 56 eval "$IFCONFIG $interface inet -alias $old_ip_address $medium" 57} 58 59add_new_address() { 60 eval "$IFCONFIG $interface \ 61 inet $new_ip_address \ 62 netmask $new_subnet_mask \ 63 broadcast $new_broadcast_address \ 64 $medium" 65 66 $LOGGER "New IP Address ($interface): $new_ip_address" 67 $LOGGER "New Subnet Mask ($interface): $new_subnet_mask" 68 $LOGGER "New Broadcast Address ($interface): $new_broadcast_address" 69 $LOGGER "New Routers ($interface): $new_routers" 70} 71 72delete_old_alias() { 73 if [ -n "$alias_ip_address" ]; then 74 $IFCONFIG $interface inet -alias $alias_ip_address > /dev/null 2>&1 75 #route delete $alias_ip_address $LOCALHOST > /dev/null 2>&1 76 fi 77} 78 79add_new_alias() { 80 if [ -n "$alias_ip_address" ]; then 81 $IFCONFIG $interface inet alias $alias_ip_address netmask \ 82 $alias_subnet_mask 83 #route add $alias_ip_address $LOCALHOST 84 fi 85} 86 87fill_classless_routes() { 88 set $1 89 while [ $# -ge 5 ]; do 90 if [ $1 -eq 0 ]; then 91 route="default" 92 elif [ $1 -le 8 ]; then 93 route="$2.0.0.0/$1" 94 shift 95 elif [ $1 -le 16 ]; then 96 route="$2.$3.0.0/$1" 97 shift; shift 98 elif [ $1 -le 24 ]; then 99 route="$2.$3.$4.0/$1" 100 shift; shift; shift 101 else 102 route="$2.$3.$4.$5/$1" 103 shift; shift; shift; shift 104 fi 105 shift 106 router="$1.$2.$3.$4" 107 classless_routes="$classless_routes $route $router" 108 shift; shift; shift; shift 109 done 110} 111 112delete_old_routes() { 113 #route delete "$old_ip_address" $LOCALHOST >/dev/null 2>&1 114 if [ -n "$old_classless_routes" ]; then 115 fill_classless_routes "$old_classless_routes" 116 set $classless_routes 117 while [ $# -gt 1 ]; do 118 route delete "$1" "$2" 119 shift; shift 120 done 121 return 0; 122 fi 123 124 # If we supported multiple default routes, we'd be removing each 125 # one here. We don't so just delete the default route if it's 126 # through our interface. 127 if is_default_interface; then 128 route delete default >/dev/null 2>&1 129 fi 130 131 if [ -n "$old_static_routes" ]; then 132 set $old_static_routes 133 while [ $# -gt 1 ]; do 134 route delete "$1" "$2" 135 shift; shift 136 done 137 fi 138 139 arp_flush 140} 141 142add_new_routes() { 143 #route add $new_ip_address $LOCALHOST >/dev/null 2>&1 144 145 # RFC 3442: If the DHCP server returns both a Classless Static 146 # Routes option and a Router option, the DHCP client MUST ignore 147 # the Router option. 148 # 149 # DHCP clients that support this option (Classless Static Routes) 150 # MUST NOT install the routes specified in the Static Routes 151 # option (option code 33) if both a Static Routes option and the 152 # Classless Static Routes option are provided. 153 154 if [ -n "$new_classless_routes" ]; then 155 fill_classless_routes "$new_classless_routes" 156 $LOGGER "New Classless Static Routes ($interface): $classless_routes" 157 set $classless_routes 158 while [ $# -gt 1 ]; do 159 if [ "0.0.0.0" = "$2" ]; then 160 route add "$1" -iface "$interface" 161 else 162 route add "$1" "$2" 163 fi 164 shift; shift 165 done 166 return 167 fi 168 169 for router in $new_routers; do 170 if is_default_interface; then 171 172 if [ "$new_ip_address" = "$router" ]; then 173 route add default -iface $router >/dev/null 2>&1 174 else 175 if [ "$new_subnet_mask" = "255.255.255.255" ]; then 176 route add "$router" -iface "$interface" >/dev/null 2>&1 177 fi 178 179 route add default $router >/dev/null 2>&1 180 fi 181 fi 182 # 2nd and subsequent default routers error out, so explicitly 183 # stop processing the list after the first one. 184 break 185 done 186 187 if [ -n "$new_static_routes" ]; then 188 $LOGGER "New Static Routes ($interface): $new_static_routes" 189 set $new_static_routes 190 while [ $# -gt 1 ]; do 191 route add $1 $2 192 shift; shift 193 done 194 fi 195} 196 197add_new_resolv_conf() { 198 # XXX Old code did not create/update resolv.conf unless both 199 # $new_domain_name and $new_domain_name_servers were provided. PR 200 # #3135 reported some ISP's only provide $new_domain_name_servers and 201 # thus broke the script. This code creates the resolv.conf if either 202 # are provided. 203 204 local tmpres=/var/run/resolv.conf.${interface} 205 rm -f $tmpres 206 207 if [ -n "$new_domain_search" ]; then 208 echo "search $new_domain_search" >>$tmpres 209 elif [ -n "$new_domain_name" ]; then 210 echo "search $new_domain_name" >>$tmpres 211 fi 212 213 if [ -n "$new_domain_name_servers" ]; then 214 for nameserver in $new_domain_name_servers; do 215 echo "nameserver $nameserver" >>$tmpres 216 done 217 fi 218 219 if [ -f $tmpres ]; then 220 if [ -f /etc/resolv.conf.tail ]; then 221 cat /etc/resolv.conf.tail >>$tmpres 222 fi 223 224 case $resolvconf_enable in 225 # "no", "false", "off", or "0" 226 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) 227 # When resolv.conf is not changed actually, we don't 228 # need to update it. 229 # If /usr is not mounted yet, we cannot use cmp, then 230 # the following test fails. In such case, we simply 231 # ignore an error and do update resolv.conf. 232 if cmp -s $tmpres /etc/resolv.conf; then 233 rm -f $tmpres 234 return 0 235 fi 2>/dev/null 236 237 # In case (e.g. during OpenBSD installs) 238 # /etc/resolv.conf is a symbolic link, take 239 # care to preserve the link and write the new 240 # data in the correct location. 241 242 if [ -f /etc/resolv.conf ]; then 243 cat /etc/resolv.conf > /etc/resolv.conf.save 244 fi 245 cat $tmpres > /etc/resolv.conf 246 247 # Try to ensure correct ownership and permissions. 248 chown -RL root:wheel /etc/resolv.conf 249 chmod -RL 644 /etc/resolv.conf 250 ;; 251 252 *) 253 /sbin/resolvconf -a ${interface} < $tmpres 254 ;; 255 esac 256 257 rm -f $tmpres 258 259 return 0 260 fi 261 262 return 1 263} 264 265# Must be used on exit. Invokes the local dhcp client exit hooks, if any. 266exit_with_hooks() { 267 exit_status=$1 268 if [ -f /etc/dhclient-exit-hooks ]; then 269 . /etc/dhclient-exit-hooks 270 fi 271 # probably should do something with exit status of the local script 272 exit $exit_status 273} 274 275# Get the interface with the current ipv4 default route on it using only 276# commands that are available prior to /usr being mounted. 277is_default_interface() 278{ 279 routeget="`route -n get -inet default`" 280 oldifs="$IFS" 281 IFS=" 282" 283 defif= 284 for line in $routeget ; do 285 case $line in 286 *interface:*) 287 defif=${line##*: } 288 ;; 289 esac 290 done 291 IFS=${oldifs} 292 293 if [ -z "$defif" -o "$defif" = "$interface" ]; then 294 return 0 295 else 296 return 1 297 fi 298} 299 300# 301# Start of active code. 302# 303 304# Invoke the local dhcp client enter hooks, if they exist. 305if [ -f /etc/dhclient-enter-hooks ]; then 306 exit_status=0 307 . /etc/dhclient-enter-hooks 308 # allow the local script to abort processing of this state 309 # local script must set exit_status variable to nonzero. 310 if [ $exit_status -ne 0 ]; then 311 exit $exit_status 312 fi 313fi 314 315: ${resolvconf_enable="YES"} 316 317case $reason in 318MEDIUM) 319 eval "$IFCONFIG $interface $medium" 320 sleep 1 321 ;; 322 323PREINIT) 324 delete_old_alias 325 eval "$IFCONFIG $interface up" 326 ;; 327 328ARPCHECK|ARPSEND) 329 ;; 330 331BOUND|RENEW|REBIND|REBOOT) 332 check_hostname 333 if [ -n "$old_ip_address" ]; then 334 if [ "$old_ip_address" != "$alias_ip_address" ]; then 335 delete_old_alias 336 fi 337 if [ "$old_ip_address" != "$new_ip_address" ]; then 338 delete_old_address 339 delete_old_routes 340 fi 341 fi 342 if [ "$reason" = BOUND ] || \ 343 [ "$reason" = REBOOT ] || \ 344 [ -z "$old_ip_address" ] || \ 345 [ "$old_ip_address" != "$new_ip_address" ]; then 346 add_new_address 347 add_new_routes 348 fi 349 if [ "$new_ip_address" != "$alias_ip_address" ]; then 350 add_new_alias 351 fi 352 if is_default_interface; then 353 add_new_resolv_conf 354 fi 355 ;; 356 357EXPIRE|FAIL) 358 delete_old_alias 359 if [ -n "$old_ip_address" ]; then 360 delete_old_address 361 delete_old_routes 362 fi 363 if [ -x $ARP ]; then 364 $ARP -d -a -i $interface 365 fi 366 # XXX Why add alias we just deleted above? 367 add_new_alias 368 if is_default_interface; then 369 case $resolvconf_enable in 370 # "no", "false", "off", or "0" 371 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) 372 if [ -f /etc/resolv.conf.save ]; then 373 cat /etc/resolv.conf.save > /etc/resolv.conf 374 fi 375 ;; 376 *) 377 /sbin/resolvconf -d ${interface} 378 ;; 379 esac 380 fi 381 ;; 382 383TIMEOUT) 384 delete_old_alias 385 add_new_address 386 sleep 1 387 if [ -n "$new_routers" ]; then 388 $LOGGER "New Routers ($interface): $new_routers" 389 set "$new_routers" 390 if ping -q -c 1 -t 1 "$1"; then 391 if [ "$new_ip_address" != "$alias_ip_address" ]; then 392 add_new_alias 393 fi 394 add_new_routes 395 if ! is_default_interface; then 396 exit_with_hooks 0 397 fi 398 if add_new_resolv_conf; then 399 exit_with_hooks 0 400 fi 401 fi 402 fi 403 eval "$IFCONFIG $interface inet -alias $new_ip_address $medium" 404 delete_old_routes 405 exit_with_hooks 1 406 ;; 407esac 408 409exit_with_hooks 0 410