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