1201062Sluigi#!/bin/sh
2201062Sluigi#
3201062Sluigi# $OpenBSD: dhclient-script,v 1.6 2004/05/06 18:22:41 claudio Exp $
4201062Sluigi# $FreeBSD$
5201062Sluigi#
6201062Sluigi# Copyright (c) 2003 Kenneth R Westerback <krw@openbsd.org>
7201062Sluigi#
8201062Sluigi# Permission to use, copy, modify, and distribute this software for any
9201062Sluigi# purpose with or without fee is hereby granted, provided that the above
10201062Sluigi# copyright notice and this permission notice appear in all copies.
11201062Sluigi#
12201062Sluigi# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13201062Sluigi# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14201062Sluigi# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15201062Sluigi# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16201062Sluigi# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17201062Sluigi# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18201062Sluigi# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19201062Sluigi#
20201062Sluigi#
21201062Sluigi
22201062SluigiARP=/usr/sbin/arp
23201062SluigiHOSTNAME=/bin/hostname
24201062SluigiIFCONFIG='/sbin/ifconfig -n'
25201062Sluigi
26201062SluigiLOCALHOST=127.0.0.1
27201062Sluigi
28201062Sluigiif [ -x /usr/bin/logger ]; then
29201062Sluigi	LOGGER="/usr/bin/logger -s -p user.notice -t dhclient"
30201062Sluigielse
31201062Sluigi	LOGGER=echo
32201062Sluigifi
33201062Sluigi
34201062Sluigi#
35201062Sluigi# Helper functions that implement common actions.
36201062Sluigi#
37201062Sluigi
38201062Sluigicheck_hostname() {
39201062Sluigi	current_hostname=`$HOSTNAME`
40201062Sluigi	if [ -z "$current_hostname" ]; then
41201062Sluigi		$LOGGER "New Hostname ($interface): $new_host_name"
42201062Sluigi		$HOSTNAME $new_host_name
43201062Sluigi	elif [ "$current_hostname" = "$old_host_name" -a \
44201062Sluigi	       "$new_host_name" != "$old_host_name" ]; then
45201062Sluigi		$LOGGER "New Hostname ($interface): $new_host_name"
46201062Sluigi		$HOSTNAME $new_host_name
47201062Sluigi	fi
48201062Sluigi}
49201062Sluigi
50201062Sluigiarp_flush() {
51201062Sluigi	arp -an -i $interface | \
52201062Sluigi		sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' | \
53201062Sluigi		sh >/dev/null 2>&1
54201062Sluigi}
55201062Sluigi
56201062Sluigidelete_old_address() {
57201062Sluigi	eval "$IFCONFIG $interface inet -alias $old_ip_address $medium"
58201062Sluigi}
59201062Sluigi
60201062Sluigiadd_new_address() {
61201062Sluigi	eval "$IFCONFIG $interface \
62201062Sluigi		inet $new_ip_address \
63201062Sluigi		netmask $new_subnet_mask \
64201062Sluigi		broadcast $new_broadcast_address \
65201062Sluigi		$medium"
66201062Sluigi
67201062Sluigi	$LOGGER "New IP Address ($interface): $new_ip_address"
68201062Sluigi	$LOGGER "New Subnet Mask ($interface): $new_subnet_mask"
69201062Sluigi	$LOGGER "New Broadcast Address ($interface): $new_broadcast_address"
70201062Sluigi	$LOGGER "New Routers ($interface): $new_routers"
71201062Sluigi}
72201062Sluigi
73201062Sluigidelete_old_alias() {
74201062Sluigi	if [ -n "$alias_ip_address" ]; then
75201062Sluigi		$IFCONFIG $interface inet -alias $alias_ip_address > /dev/null 2>&1
76201062Sluigi		#route delete $alias_ip_address $LOCALHOST > /dev/null 2>&1
77201062Sluigi	fi
78201062Sluigi}
79201062Sluigi
80201062Sluigiadd_new_alias() {
81201062Sluigi	if [ -n "$alias_ip_address" ]; then
82201062Sluigi		$IFCONFIG $interface inet alias $alias_ip_address netmask \
83201062Sluigi		    $alias_subnet_mask
84201062Sluigi		#route add $alias_ip_address $LOCALHOST
85201062Sluigi	fi
86201062Sluigi}
87201062Sluigi
88201062Sluigifill_classless_routes() {
89201062Sluigi	set $1
90201062Sluigi	while [ $# -ge 5 ]; do
91201062Sluigi		if [ $1 -eq 0 ]; then
92201062Sluigi			route="default"
93201062Sluigi		elif [ $1 -le 8 ]; then
94201062Sluigi			route="$2.0.0.0/$1"
95201062Sluigi			shift
96201062Sluigi		elif [ $1 -le 16 ]; then
97201062Sluigi			route="$2.$3.0.0/$1"
98201062Sluigi			shift; shift
99201062Sluigi		elif [ $1 -le 24 ]; then
100201062Sluigi			route="$2.$3.$4.0/$1"
101201062Sluigi			shift; shift; shift
102201062Sluigi		else
103201062Sluigi			route="$2.$3.$4.$5/$1"
104201062Sluigi			shift; shift; shift; shift
105201062Sluigi		fi
106201062Sluigi		shift
107201062Sluigi		router="$1.$2.$3.$4"
108201062Sluigi		classless_routes="$classless_routes $route $router"
109201062Sluigi		shift; shift; shift; shift
110201062Sluigi	done
111201062Sluigi}
112201062Sluigi
113201062Sluigidelete_old_routes() {
114201062Sluigi	#route delete "$old_ip_address" $LOCALHOST >/dev/null 2>&1
115201062Sluigi	if [ -n "$old_classless_routes" ]; then
116201062Sluigi		fill_classless_routes "$old_classless_routes"
117201062Sluigi		set $classless_routes
118201062Sluigi		while [ $# -gt 1 ]; do
119201062Sluigi			route delete "$1" "$2"
120201062Sluigi			shift; shift
121201062Sluigi		done
122201062Sluigi		return 0;
123201062Sluigi	fi
124201062Sluigi
125201062Sluigi	# If we supported multiple default routes, we'd be removing each
126201062Sluigi	# one here.  We don't so just delete the default route if it's
127201062Sluigi	# through our interface.
128201062Sluigi	if is_default_interface; then
129201062Sluigi		route delete default >/dev/null 2>&1
130201062Sluigi	fi
131201062Sluigi
132201062Sluigi	if [ -n "$old_static_routes" ]; then
133201062Sluigi		set $old_static_routes
134201062Sluigi		while [ $# -gt 1 ]; do
135201062Sluigi			route delete "$1" "$2"
136201062Sluigi			shift; shift
137201062Sluigi		done
138201062Sluigi	fi
139201062Sluigi
140201062Sluigi	arp_flush
141201062Sluigi}
142201062Sluigi
143201062Sluigiadd_new_routes() {
144201062Sluigi	#route add $new_ip_address $LOCALHOST >/dev/null 2>&1
145201062Sluigi
146201062Sluigi	# RFC 3442: If the DHCP server returns both a Classless Static
147201062Sluigi	# Routes option and a Router option, the DHCP client MUST ignore
148201062Sluigi	# the Router option.
149201062Sluigi	#
150201062Sluigi	# DHCP clients that support this option (Classless Static Routes)
151201062Sluigi	# MUST NOT install the routes specified in the Static Routes
152201062Sluigi	# option (option code 33) if both a Static Routes option and the
153201062Sluigi	# Classless Static Routes option are provided.
154201062Sluigi
155201062Sluigi	if [ -n "$new_classless_routes" ]; then
156201062Sluigi		fill_classless_routes "$new_classless_routes"
157201062Sluigi		$LOGGER "New Classless Static Routes ($interface): $classless_routes"
158201062Sluigi		set $classless_routes
159201062Sluigi		while [ $# -gt 1 ]; do
160201062Sluigi			if [ "0.0.0.0" = "$2" ]; then
161201062Sluigi				route add "$1" -iface "$interface"
162201062Sluigi			else
163201062Sluigi				route add "$1" "$2"
164201062Sluigi			fi
165201062Sluigi			shift; shift
166201062Sluigi		done
167201062Sluigi		return
168201062Sluigi	fi
169201062Sluigi
170201062Sluigi	for router in $new_routers; do
171201062Sluigi		if is_default_interface; then
172201062Sluigi
173201062Sluigi			if [ "$new_ip_address" = "$router" ]; then
174201062Sluigi				route add default -iface $router >/dev/null 2>&1
175201062Sluigi			else
176201062Sluigi				route add default $router >/dev/null 2>&1
177201062Sluigi			fi
178201062Sluigi		fi
179201062Sluigi		# 2nd and subsequent default routers error out, so explicitly
180201062Sluigi		# stop processing the list after the first one.
181201062Sluigi		break
182201062Sluigi	done
183201062Sluigi
184201062Sluigi	if [ -n "$new_static_routes" ]; then
185201062Sluigi		$LOGGER "New Static Routes ($interface): $new_static_routes"
186201062Sluigi		set $new_static_routes
187201062Sluigi		while [ $# -gt 1 ]; do
188201062Sluigi			route add $1 $2
189201062Sluigi			shift; shift
190201062Sluigi		done
191201062Sluigi	fi
192201062Sluigi}
193201062Sluigi
194201062Sluigiadd_new_resolv_conf() {
195201062Sluigi	# XXX Old code did not create/update resolv.conf unless both
196201062Sluigi	# $new_domain_name and $new_domain_name_servers were provided.  PR
197201062Sluigi	# #3135 reported some ISP's only provide $new_domain_name_servers and
198201062Sluigi	# thus broke the script. This code creates the resolv.conf if either
199201062Sluigi	# are provided.
200201062Sluigi
201201062Sluigi	local tmpres=/var/run/resolv.conf.${interface}
202201062Sluigi	rm -f $tmpres
203201062Sluigi
204201062Sluigi	if [ -n "$new_domain_name" ]; then
205201062Sluigi		echo "search $new_domain_name" >>$tmpres
206201062Sluigi	fi
207201062Sluigi
208201062Sluigi	if [ -n "$new_domain_name_servers" ]; then
209201062Sluigi		for nameserver in $new_domain_name_servers; do
210201062Sluigi			echo "nameserver $nameserver" >>$tmpres
211201062Sluigi		done
212201062Sluigi	fi
213201062Sluigi
214201062Sluigi	if [ -f $tmpres ]; then
215201062Sluigi		if [ -f /etc/resolv.conf.tail ]; then
216201062Sluigi			cat /etc/resolv.conf.tail >>$tmpres
217201062Sluigi		fi
218201062Sluigi
219201062Sluigi		# When resolv.conf is not changed actually, we don't
220201062Sluigi		# need to update it.
221201062Sluigi		# If /usr is not mounted yet, we cannot use cmp, then
222201062Sluigi		# the following test fails.  In such case, we simply
223201062Sluigi		# ignore an error and do update resolv.conf.
224201062Sluigi		if cmp -s $tmpres /etc/resolv.conf; then
225201062Sluigi			rm -f $tmpres
226201062Sluigi			return 0
227201062Sluigi		fi 2>/dev/null
228201062Sluigi
229201062Sluigi		# In case (e.g. during OpenBSD installs) /etc/resolv.conf
230201062Sluigi		# is a symbolic link, take care to preserve the link and write
231201062Sluigi		# the new data in the correct location.
232201062Sluigi
233201062Sluigi		if [ -f /etc/resolv.conf ]; then
234201062Sluigi			cat /etc/resolv.conf > /etc/resolv.conf.save
235201062Sluigi		fi
236201062Sluigi		cat $tmpres > /etc/resolv.conf
237201062Sluigi		rm -f $tmpres
238201062Sluigi
239201062Sluigi		# Try to ensure correct ownership and permissions.
240201062Sluigi		chown -RL root:wheel /etc/resolv.conf
241201062Sluigi		chmod -RL 644 /etc/resolv.conf
242201062Sluigi
243201062Sluigi		return 0
244201062Sluigi	fi
245201062Sluigi
246201062Sluigi	return 1
247201062Sluigi}
248201062Sluigi
249201062Sluigi# Must be used on exit.   Invokes the local dhcp client exit hooks, if any.
250201062Sluigiexit_with_hooks() {
251201062Sluigi	exit_status=$1
252201062Sluigi	if [ -f /etc/dhclient-exit-hooks ]; then
253201062Sluigi		. /etc/dhclient-exit-hooks
254201062Sluigi	fi
255201062Sluigi	# probably should do something with exit status of the local script
256201062Sluigi	exit $exit_status
257201062Sluigi}
258201062Sluigi
259201062Sluigi# Get the interface with the current ipv4 default route on it using only
260201062Sluigi# commands that are available prior to /usr being mounted.
261201062Sluigiis_default_interface()
262201062Sluigi{
263201062Sluigi	routeget="`route -n get -inet default`"
264201062Sluigi	oldifs="$IFS"
265201062Sluigi	IFS="
266201062Sluigi"
267201062Sluigi	defif=
268201062Sluigi	for line in $routeget ; do
269201062Sluigi		case $line in
270201062Sluigi		*interface:*)
271201062Sluigi			defif=${line##*: }
272201062Sluigi			;;
273201062Sluigi		esac
274201062Sluigi	done
275201062Sluigi	IFS=${oldifs}
276201062Sluigi
277201062Sluigi	if [ -z "$defif" -o "$defif" = "$interface" ]; then
278201062Sluigi		return 0
279201062Sluigi	else
280201062Sluigi		return 1
281201062Sluigi	fi
282201062Sluigi}
283201062Sluigi
284201062Sluigi#
285201062Sluigi# Start of active code.
286201062Sluigi#
287201062Sluigi
288201062Sluigi# Invoke the local dhcp client enter hooks, if they exist.
289201062Sluigiif [ -f /etc/dhclient-enter-hooks ]; then
290201062Sluigi	exit_status=0
291201062Sluigi	. /etc/dhclient-enter-hooks
292201062Sluigi	# allow the local script to abort processing of this state
293201062Sluigi	# local script must set exit_status variable to nonzero.
294201062Sluigi	if [ $exit_status -ne 0 ]; then
295201062Sluigi		exit $exit_status
296201062Sluigi	fi
297201062Sluigifi
298201062Sluigi
299201062Sluigicase $reason in
300201062SluigiMEDIUM)
301201062Sluigi	eval "$IFCONFIG $interface $medium"
302201062Sluigi	eval "$IFCONFIG $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1
303201062Sluigi	sleep 1
304201062Sluigi	;;
305201062Sluigi
306201062SluigiPREINIT)
307201062Sluigi	delete_old_alias
308201062Sluigi	$IFCONFIG $interface inet alias 0.0.0.0 netmask 0.0.0.0 broadcast 255.255.255.255 up
309201062Sluigi	;;
310201062Sluigi
311201062SluigiARPCHECK|ARPSEND)
312201062Sluigi	;;
313201062Sluigi
314201062SluigiBOUND|RENEW|REBIND|REBOOT)
315201062Sluigi	check_hostname
316201062Sluigi	if [ -n "$old_ip_address" ]; then
317201062Sluigi		if [ "$old_ip_address" != "$alias_ip_address" ]; then
318201062Sluigi			delete_old_alias
319201062Sluigi		fi
320201062Sluigi		if [ "$old_ip_address" != "$new_ip_address" ]; then
321201062Sluigi			delete_old_address
322201062Sluigi			delete_old_routes
323201062Sluigi		fi
324201062Sluigi	fi
325201062Sluigi	if [ "$reason" = BOUND ] || \
326201062Sluigi	   [ "$reason" = REBOOT ] || \
327201062Sluigi	   [ -z "$old_ip_address" ] || \
328201062Sluigi	   [ "$old_ip_address" != "$new_ip_address" ]; then
329201062Sluigi		add_new_address
330201062Sluigi		add_new_routes
331201062Sluigi	fi
332201062Sluigi	if [ "$new_ip_address" != "$alias_ip_address" ]; then
333201062Sluigi		add_new_alias
334201062Sluigi	fi
335201062Sluigi	if is_default_interface; then
336201062Sluigi		add_new_resolv_conf
337201062Sluigi	fi
338201062Sluigi	;;
339201062Sluigi
340201062SluigiEXPIRE|FAIL)
341201062Sluigi	delete_old_alias
342201062Sluigi	if [ -n "$old_ip_address" ]; then
343201062Sluigi		delete_old_address
344201062Sluigi		delete_old_routes
345201062Sluigi	fi
346201062Sluigi	if [ -x $ARP ]; then
347201062Sluigi		$ARP -d -a -i $interface
348201062Sluigi	fi
349201062Sluigi	# XXX Why add alias we just deleted above?
350201062Sluigi	add_new_alias
351201062Sluigi	if is_default_interface; then
352201062Sluigi		if [ -f /etc/resolv.conf.save ]; then
353201062Sluigi			cat /etc/resolv.conf.save > /etc/resolv.conf
354201062Sluigi		fi
355201062Sluigi	fi
356201062Sluigi	;;
357201062Sluigi
358201062SluigiTIMEOUT)
359201062Sluigi	delete_old_alias
360201062Sluigi	add_new_address
361201062Sluigi	sleep 1
362201062Sluigi	if [ -n "$new_routers" ]; then
363201062Sluigi		$LOGGER "New Routers ($interface): $new_routers"
364201062Sluigi		set "$new_routers"
365201062Sluigi		if ping -q -c 1 -t 1 "$1"; then
366201062Sluigi			if [ "$new_ip_address" != "$alias_ip_address" ]; then
367201062Sluigi				add_new_alias
368201062Sluigi			fi
369201062Sluigi			add_new_routes
370201062Sluigi			if ! is_default_interface; then
371201062Sluigi				exit_with_hooks 0
372201062Sluigi			fi
373201062Sluigi			if add_new_resolv_conf; then
374201062Sluigi				exit_with_hooks 0
375201062Sluigi			fi
376201062Sluigi		fi
377201062Sluigi	fi
378201062Sluigi	eval "$IFCONFIG $interface inet -alias $new_ip_address $medium"
379201062Sluigi	delete_old_routes
380201062Sluigi	exit_with_hooks 1
381201062Sluigi	;;
382201062Sluigiesac
383201062Sluigi
384201062Sluigiexit_with_hooks 0
385