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 87add_captive_portal() { 88 # DHCP option 114 (RFC 8910), with the legacy DHCP option 160 89 # (RFC 7710) as a fallback. 90 captive_portal_url="${new_captive_portal_url:-$new_captive_portal_legacy}" 91 92 if [ -z "$captive_portal_url" ]; then 93 rm -f /var/run/captive-portal.$interface 94 return 0 95 fi 96 97 $LOGGER "New Captive Portal ($interface): $captive_portal_url" 98 99 captive_portal_tmp=/var/run/captive-portal.${interface}.tmp 100 if ! (umask 022; echo "$captive_portal_url" > $captive_portal_tmp); then 101 rm -f $captive_portal_tmp 102 return 1 103 fi 104 if ! mv $captive_portal_tmp /var/run/captive-portal.$interface; then 105 rm -f $captive_portal_tmp 106 return 1 107 fi 108 return 0 109} 110 111delete_captive_portal() { 112 rm -f /var/run/captive-portal.$interface 113} 114 115fill_classless_routes() { 116 set $1 117 while [ $# -ge 5 ]; do 118 if [ $1 -eq 0 ]; then 119 route="default" 120 elif [ $1 -le 8 ]; then 121 route="$2.0.0.0/$1" 122 shift 123 elif [ $1 -le 16 ]; then 124 route="$2.$3.0.0/$1" 125 shift; shift 126 elif [ $1 -le 24 ]; then 127 route="$2.$3.$4.0/$1" 128 shift; shift; shift 129 else 130 route="$2.$3.$4.$5/$1" 131 shift; shift; shift; shift 132 fi 133 shift 134 router="$1.$2.$3.$4" 135 classless_routes="$classless_routes $route $router" 136 shift; shift; shift; shift 137 done 138} 139 140delete_old_routes() { 141 #route delete "$old_ip_address" $LOCALHOST >/dev/null 2>&1 142 if [ -n "$old_classless_routes" ]; then 143 fill_classless_routes "$old_classless_routes" 144 set $classless_routes 145 while [ $# -gt 1 ]; do 146 route delete "$1" "$2" 147 shift; shift 148 done 149 return 0; 150 fi 151 152 # If we supported multiple default routes, we'd be removing each 153 # one here. We don't so just delete the default route if it's 154 # through our interface. 155 if is_default_interface; then 156 route delete default >/dev/null 2>&1 157 fi 158 159 if [ -n "$old_static_routes" ]; then 160 set $old_static_routes 161 while [ $# -gt 1 ]; do 162 route delete "$1" "$2" 163 shift; shift 164 done 165 fi 166 167 arp_flush 168} 169 170add_new_routes() { 171 #route add $new_ip_address $LOCALHOST >/dev/null 2>&1 172 173 # RFC 3442: If the DHCP server returns both a Classless Static 174 # Routes option and a Router option, the DHCP client MUST ignore 175 # the Router option. 176 # 177 # DHCP clients that support this option (Classless Static Routes) 178 # MUST NOT install the routes specified in the Static Routes 179 # option (option code 33) if both a Static Routes option and the 180 # Classless Static Routes option are provided. 181 182 if [ -n "$new_classless_routes" ]; then 183 fill_classless_routes "$new_classless_routes" 184 $LOGGER "New Classless Static Routes ($interface): $classless_routes" 185 set $classless_routes 186 while [ $# -gt 1 ]; do 187 if [ "0.0.0.0" = "$2" ]; then 188 route add "$1" -iface "$interface" 189 else 190 route add "$1" "$2" 191 fi 192 shift; shift 193 done 194 return 195 fi 196 197 for router in $new_routers; do 198 if is_default_interface; then 199 200 if [ "$new_ip_address" = "$router" ]; then 201 route add default -iface $router >/dev/null 2>&1 202 else 203 if [ "$new_subnet_mask" = "255.255.255.255" ]; then 204 route add "$router" -iface "$interface" >/dev/null 2>&1 205 fi 206 207 route add default $router >/dev/null 2>&1 208 fi 209 fi 210 # 2nd and subsequent default routers error out, so explicitly 211 # stop processing the list after the first one. 212 break 213 done 214 215 if [ -n "$new_static_routes" ]; then 216 $LOGGER "New Static Routes ($interface): $new_static_routes" 217 set $new_static_routes 218 while [ $# -gt 1 ]; do 219 route add $1 $2 220 shift; shift 221 done 222 fi 223} 224 225add_new_resolv_conf() { 226 # XXX Old code did not create/update resolv.conf unless both 227 # $new_domain_name and $new_domain_name_servers were provided. PR 228 # #3135 reported some ISP's only provide $new_domain_name_servers and 229 # thus broke the script. This code creates the resolv.conf if either 230 # are provided. 231 232 local tmpres=/var/run/resolv.conf.${interface} 233 rm -f $tmpres 234 235 if [ -n "$new_domain_search" ]; then 236 echo "search $new_domain_search" >>$tmpres 237 elif [ -n "$new_domain_name" ]; then 238 echo "search $new_domain_name" >>$tmpres 239 fi 240 241 if [ -n "$new_domain_name_servers" ]; then 242 for nameserver in $new_domain_name_servers; do 243 echo "nameserver $nameserver" >>$tmpres 244 done 245 fi 246 247 if [ -f $tmpres ]; then 248 if [ -f /etc/resolv.conf.tail ]; then 249 cat /etc/resolv.conf.tail >>$tmpres 250 fi 251 252 case $resolvconf_enable in 253 # "no", "false", "off", or "0" 254 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) 255 # When resolv.conf is not changed actually, we don't 256 # need to update it. 257 # If /usr is not mounted yet, we cannot use cmp, then 258 # the following test fails. In such case, we simply 259 # ignore an error and do update resolv.conf. 260 if cmp -s $tmpres /etc/resolv.conf; then 261 rm -f $tmpres 262 return 0 263 fi 2>/dev/null 264 265 # In case (e.g. during OpenBSD installs) 266 # /etc/resolv.conf is a symbolic link, take 267 # care to preserve the link and write the new 268 # data in the correct location. 269 270 if [ -f /etc/resolv.conf ]; then 271 cat /etc/resolv.conf > /etc/resolv.conf.save 272 fi 273 cat $tmpres > /etc/resolv.conf 274 275 # Try to ensure correct ownership and permissions. 276 chown -RL root:wheel /etc/resolv.conf 277 chmod -RL 644 /etc/resolv.conf 278 ;; 279 280 *) 281 /sbin/resolvconf -a ${interface} < $tmpres 282 ;; 283 esac 284 285 rm -f $tmpres 286 287 return 0 288 fi 289 290 return 1 291} 292 293# Must be used on exit. Invokes the local dhcp client exit hooks, if any. 294exit_with_hooks() { 295 exit_status=$1 296 if [ -f /etc/dhclient-exit-hooks ]; then 297 . /etc/dhclient-exit-hooks 298 fi 299 # probably should do something with exit status of the local script 300 exit $exit_status 301} 302 303# Get the interface with the current ipv4 default route on it using only 304# commands that are available prior to /usr being mounted. 305is_default_interface() 306{ 307 routeget="`route -n get -inet default`" 308 oldifs="$IFS" 309 IFS=" 310" 311 defif= 312 for line in $routeget ; do 313 case $line in 314 *interface:*) 315 defif=${line##*: } 316 ;; 317 esac 318 done 319 IFS=${oldifs} 320 321 if [ -z "$defif" -o "$defif" = "$interface" ]; then 322 return 0 323 else 324 return 1 325 fi 326} 327 328# 329# Start of active code. 330# 331 332# Invoke the local dhcp client enter hooks, if they exist. 333if [ -f /etc/dhclient-enter-hooks ]; then 334 exit_status=0 335 . /etc/dhclient-enter-hooks 336 # allow the local script to abort processing of this state 337 # local script must set exit_status variable to nonzero. 338 if [ $exit_status -ne 0 ]; then 339 exit $exit_status 340 fi 341fi 342 343: ${resolvconf_enable="YES"} 344 345case $reason in 346MEDIUM) 347 eval "$IFCONFIG $interface $medium" 348 sleep 1 349 ;; 350 351PREINIT) 352 delete_old_alias 353 eval "$IFCONFIG $interface up" 354 ;; 355 356ARPCHECK|ARPSEND) 357 ;; 358 359BOUND|RENEW|REBIND|REBOOT) 360 check_hostname 361 if [ -n "$old_ip_address" ]; then 362 if [ "$old_ip_address" != "$alias_ip_address" ]; then 363 delete_old_alias 364 fi 365 if [ "$old_ip_address" != "$new_ip_address" ]; then 366 delete_old_address 367 delete_old_routes 368 fi 369 fi 370 if [ "$reason" = BOUND ] || \ 371 [ "$reason" = REBOOT ] || \ 372 [ -z "$old_ip_address" ] || \ 373 [ "$old_ip_address" != "$new_ip_address" ]; then 374 add_new_address 375 add_new_routes 376 fi 377 if [ "$new_ip_address" != "$alias_ip_address" ]; then 378 add_new_alias 379 fi 380 add_captive_portal 381 if is_default_interface; then 382 add_new_resolv_conf 383 fi 384 ;; 385 386EXPIRE|FAIL) 387 delete_old_alias 388 delete_captive_portal 389 if [ -n "$old_ip_address" ]; then 390 delete_old_address 391 delete_old_routes 392 fi 393 if [ -x $ARP ]; then 394 $ARP -d -a -i $interface 395 fi 396 # XXX Why add alias we just deleted above? 397 add_new_alias 398 if is_default_interface; then 399 case $resolvconf_enable in 400 # "no", "false", "off", or "0" 401 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) 402 if [ -f /etc/resolv.conf.save ]; then 403 cat /etc/resolv.conf.save > /etc/resolv.conf 404 fi 405 ;; 406 *) 407 /sbin/resolvconf -d ${interface} 408 ;; 409 esac 410 fi 411 ;; 412 413TIMEOUT) 414 delete_old_alias 415 add_new_address 416 sleep 1 417 if [ -n "$new_routers" ]; then 418 $LOGGER "New Routers ($interface): $new_routers" 419 set "$new_routers" 420 if ping -q -c 1 -t 1 "$1"; then 421 if [ "$new_ip_address" != "$alias_ip_address" ]; then 422 add_new_alias 423 fi 424 add_new_routes 425 add_captive_portal 426 if ! is_default_interface; then 427 exit_with_hooks 0 428 fi 429 if add_new_resolv_conf; then 430 exit_with_hooks 0 431 fi 432 fi 433 fi 434 eval "$IFCONFIG $interface inet -alias $new_ip_address $medium" 435 delete_old_routes 436 delete_captive_portal 437 exit_with_hooks 1 438 ;; 439esac 440 441exit_with_hooks 0 442