1#!/bin/bash
2#
3# dhclient-script for Linux.
4#
5#		This program is free software; you can redistribute it and/or
6#		modify it under the terms of the GNU General Public License
7#		as published by the Free Software Foundation; either version
8#		2 of the License, or (at your option) any later version.
9#
10# Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
11#
12# Probably, I did not understand, what this funny feature as "alias"
13# means exactly. For now I suppose, that it is a static address, which
14# we should install and preserve.
15#
16
17exec >> /var/log/DHS.log 2>&1
18
19echo dhc-script $* reason=$reason
20set | grep "^\(old_\|new_\|check_\)"
21
22LOG () {
23    echo LOG $* ;
24}
25
26# convert 8bit mask to length
27# arg: $1 = mask
28#
29Mask8ToLen() {
30	local l=0;
31
32	while [ $l -le 7 ]; do
33		if [ $[ ( 1 << $l ) + $1 ] -eq 256 ]; then
34			return	$[ 8 - $l ]
35		fi
36		l=$[ $l + 1 ]
37	done
38	return 0;
39}
40
41# convert inet dotted quad mask to length
42# arg: $1 = dotquad mask
43#
44MaskToLen() {
45 local masklen=0
46 local mask8=$1
47
48 case $1 in
49 0.0.0.0)
50	return 0;
51	;;
52 255.*.0.0)
53	masklen=8
54	mask8=${mask8#255.}
55	mask8=${mask8%.0.0}
56	;;
57 255.255.*.0)
58	masklen=16
59	mask8=${mask8#255.255.}
60	mask8=${mask8%.0}
61	;;
62 255.255.255.*)
63	masklen=24
64	mask8=${mask8#255.255.255.}
65	;;
66 *)
67	return 255
68	;;
69 esac
70 Mask8ToLen $mask8
71 return $[ $? + $masklen ]
72}
73
74# calculate ABC "natural" mask
75# arg: $1 = dotquad address
76#
77ABCMask () {
78 local class;
79
80 class=${1%%.*}
81
82 if [ "$1" = "255.255.255.255" ]; then
83    echo $1
84 elif [ "$1" = "0.0.0.0" ]; then
85    echo $1
86 elif [ $class -ge 224 ]; then
87    echo 240.0.0.0
88 elif [ $class -ge 192 ]; then
89    echo 255.255.255.0
90 elif [ $class -ge 128 ]; then
91    echo 255.255.0.0
92 else
93    echo 255.0.0.0
94 fi
95}
96
97# calculate ABC "natural" mask length
98# arg: $1 = dotquad address
99#
100ABCMaskLen () {
101 local class;
102
103 class=${1%%.*}
104
105 if [ "$1" = "255.255.255.255" ]; then
106    return 32
107 elif [ "$1" = "0.0.0.0" ]; then
108    return 0
109 elif [ $class -ge 224 ]; then
110    return 4;
111 elif [ $class -ge 192 ]; then
112    return 24;
113 elif [ $class -ge 128 ]; then
114    return 16;
115 else
116    return 8;
117 fi
118}
119
120# Delete IP address
121# args: $1 = interface
122#       $2 = address
123#       $3 = mask
124#       $4 = broadcast
125#       $5 = label
126#
127DelINETAddr () {
128  local masklen=32
129  local addrid=$1
130
131  LOG DelINETAddr $*
132
133  if [ "$5" ]; then
134    addrid=$addrid:$5
135  fi
136  LOG ifconfig $addrid down
137  ifconfig $addrid down
138}
139
140# Add IP address
141# args: $1 = interface
142#       $2 = address
143#       $3 = mask
144#       $4 = broadcast
145#       $5 = label
146#
147AddINETAddr () {
148  local mask_arg
149  local brd_arg
150  local addrid=$1
151
152  LOG AddINETAddr $*
153
154  if [ "$5" ]; then
155    addrid=$addrid:$5
156  fi
157  if [ "$3" ]; then
158    mask_arg="netmask $3"
159  fi
160  if [ "$4" ]; then
161    brd_arg="broadcast $4"
162  fi
163
164  LOG ifconfig $addrid $2 $mask_arg $brd_arg up
165  ifconfig $addrid $2 $mask_arg $brd_arg up
166}
167
168# Add default routes
169# args: $1 = routers list
170#
171AddDefaultRoutes() {
172    local router
173
174    if [ "$1" ]; then
175      LOG AddDefaultRoutes $*
176      for router in $1; do
177        LOG route add default gw $router
178        route add default gw $router
179      done ;
180    fi
181}
182
183# Delete default routes
184# args: $1 = routers list
185#
186DelDefaultRoutes() {
187    local router
188
189    if [ "$1" ]; then
190      LOG DelDefaultRoutes $*
191
192      for router in $1; do
193        LOG route del default gw $router
194        route del default gw $router
195      done
196    fi
197}
198
199# ping a host
200# args: $1 = dotquad address of the host
201#
202PingNode() {
203    LOG PingNode $*
204    if ping -q -c 1 -w 2 $1 ; then
205	return 0;
206    fi
207    return 1;
208}
209
210# Check (and add route, if alive) default routers
211# args: $1 = routers list
212# returns: 0 if at least one router is alive.
213#
214CheckRouterList() {
215    local router
216    local succeed=1
217
218    LOG CheckRouterList $*
219
220    for router in $1; do
221      if PingNode $router ; then
222	succeed=0
223        route add default gw $router
224      fi
225    done
226    return $succeed
227}
228
229# Delete/create static routes.
230# args: $1 = operation (del/add)
231#       $2 = routes list in format "dst1 nexthop1 dst2 ..."
232#
233# BEWARE: this feature of DHCP is obsolete, because does not
234#         support subnetting.
235#
236X-StaticRouteList() {
237    local op=$1
238    local lst="$2"
239    local masklen
240
241    LOG X-StaticRouteList $*
242
243    if [ "$lst" ]; then
244      set $lst
245      while [ $# -gt 1 ]; do
246	route $op -net $1 netmask `ABCMask "$1"` gw $2
247	shift; shift;
248      done
249   fi
250}
251
252# Create static routes.
253# arg: $1 = routes list in format "dst1 nexthop1 dst2 ..."
254#
255AddStaticRouteList() {
256    LOG AddStaticRouteList $*
257    X-StaticRouteList add "$1"
258}
259
260# Delete static routes.
261# arg: $1 = routes list in format "dst1 nexthop1 dst2 ..."
262#
263DelStaticRouteList() {
264    LOG DelStaticRouteList $*
265    X-StaticRouteList del "$1"
266}
267
268# Broadcast unsolicited ARP to update neighbours' caches.
269# args: $1 = interface
270#       $2 = address
271#
272UnsolicitedARP() {
273    if [ -f /sbin/arping ]; then
274	/sbin/arping -A -c 1 -I "$1" "$2" &
275	(sleep 2 ; /sbin/arping -U -c 1 -I "$1" "$2" ) &
276    fi
277}
278
279# Duplicate address detection.
280# args: $1 = interface
281#       $2 = test address
282# returns: 0, if DAD succeeded.
283DAD() {
284  if [ -f /sbin/arping ]; then
285	/sbin/arping -c 2 -w 3 -D -I "$1" "$2"
286	return $?
287  fi
288  return 0
289}
290
291
292# Setup resolver.
293# args: NO
294#       domain and nameserver list are passed in global variables.
295#
296# NOTE: we try to be careful and not to break user supplied resolv.conf.
297#       The script mangles it, only if it has dhcp magic signature.
298#
299UpdateDNS() {
300    local nameserver
301    local idstring="#### Generated by DHCPCD"
302
303    LOG UpdateDNS $*
304
305    if [ "$new_domain_name" = "" -a "$new_domain_name_servers" = "" ]; then
306	return 0;
307    fi
308
309    echo $idstring > /etc/resolv.conf.dhcp
310    if [ "$new_domain_name" ]; then
311	echo search $new_domain_name >> /etc/resolv.conf.dhcp
312    fi
313    echo options ndots:1 >> /etc/resolv.conf.dhcp
314
315    if [ "$new_domain_name_servers" ]; then
316	for nameserver in $new_domain_name_servers; do
317	    echo nameserver $nameserver >> /etc/resolv.conf.dhcp
318	done
319    else
320	echo nameserver 127.0.0.1 >> /etc/resolv.conf.dhcp
321    fi
322
323    if [ -f /etc/resolv.conf ]; then
324	if [ "`head -1 /etc/resolv.conf`" != "$idstring" ]; then
325	    return 0
326	fi
327	if [ "$old_domain_name" = "$new_domain_name" -a
328	     "$new_domain_name_servers" = "$old_domain_name_servers" ]; then
329	     return 0
330	fi
331    fi
332    mv /etc/resolv.conf.dhcp /etc/resolv.conf
333}
334
335case $reason in
336NBI)
337  exit 1
338  ;;
339
340MEDIUM)
341  exit 0
342  ;;
343
344PREINIT)
345  ifconfig $interface:dhcp down
346  ifconfig $interface:dhcp1 down
347  if [ -d /proc/sys/net/ipv4/conf/$interface ]; then
348    ifconfig $interface:dhcp 10.10.10.10 netmask 255.255.255.255
349    ifconfig $interface:dhcp down
350    if [ -d /proc/sys/net/ipv4/conf/$interface ]; then
351	LOG The interface $interface already configured.
352    fi
353  fi
354  ifconfig $interface:dhcp up
355  exit 0
356  ;;
357
358ARPSEND)
359  exit 0
360  ;;
361
362ARPCHECK)
363  if DAD "$interface" "$check_ip_address" ; then
364    exit 0
365  fi
366  exit 1
367  ;;
368
369BOUND|RENEW|REBIND|REBOOT)
370  if [ "$old_ip_address" -a "$alias_ip_address" -a \
371	"$alias_ip_address" != "$old_ip_address" ]; then
372    DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
373  fi
374  if [ "$old_ip_address" -a "$old_ip_address" != "$new_ip_address" ]; then
375    DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp
376    DelDefaultRoutes "$old_routers"
377    DelStaticRouteList "$old_static_routes"
378  fi
379  if [ "$old_ip_address" = "" -o "$old_ip_address" != "$new_ip_address" -o \
380       "$reason" = "BOUND" -o "$reason" = "REBOOT" ]; then
381    AddINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp
382    AddStaticRouteList "$new_static_routes"
383    AddDefaultRoutes "$new_routers"
384    UnsolicitedARP "$interface" "$new_ip_address"
385  fi
386  if [ "$new_ip_address" != "$alias_ip_address" -a "$alias_ip_address" ]; then
387    AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
388  fi
389  UpdateDNS
390  exit 0
391  ;;
392
393EXPIRE|FAIL)
394  if [ "$alias_ip_address" ]; then
395    DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
396  fi
397  if [ "$old_ip_address" ]; then
398    DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp
399    DelDefaultRoutes "$old_routers"
400    DelStaticRouteList "$old_static_routes"
401  fi
402  if [ "$alias_ip_address" ]; then
403    AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
404  fi
405  exit 0
406  ;;
407
408TIMEOUT)
409  if [ "$alias_ip_address" ]; then
410    DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
411  fi
412# Seems, <null address> means, that no more old leases found.
413# Or does it mean bug in dhcpcd? 8) Fail for now.
414  if [ "$new_ip_address" = "<null address>" ]; then
415    if [ "$old_ip_address" ]; then
416	DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp
417    fi
418    if [ "$alias_ip_address" ]; then
419        AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
420    fi
421    exit 1
422  fi
423  if DAD "$interface" "$new_ip_address" ; then
424    AddINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp
425    UnsolicitedARP "$interface" "$new_ip_address"
426    if [ "$alias_ip_address" -a "$alias_ip_address" != "$new_ip_address" ]; then
427      AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
428      UnsolicitedARP "$interface" "$alias_ip_address"
429    fi
430    if CheckRouterList "$new_routers" ; then
431	AddStaticRouteList "$new_static_routes"
432	UpdateDNS
433	exit 0
434    fi
435  fi
436  DelINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp
437  DelDefaultRoutes "$old_routers"
438  DelStaticRouteList "$old_static_routes"
439  if [ "$alias_ip_address" ]; then
440    AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
441  fi
442  exit 1
443  ;;
444esac
445
446exit 0
447