dhclient-script 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. #!/bin/sh
  2. # Explicitly set the PATH to that of ENV_SUPATH in /etc/login.defs and unset
  3. # various other variables. We need to do this so /sbin/dhclient cannot abuse
  4. # the environment to escape AppArmor confinement via this script
  5. # (LP: #1045986). This can be removed once AppArmor supports environment
  6. # filtering (LP: #1045985)
  7. export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  8. export ENV=
  9. export BASH_ENV=
  10. export CDPATH=
  11. export GLOBIGNORE=
  12. export BASH_XTRACEFD=
  13. # dhclient-script for Linux. Dan Halbert, March, 1997.
  14. # Updated for Linux 2.[12] by Brian J. Murrell, January 1999.
  15. # Modified for Debian. Matt Zimmerman and Eloy Paris, December 2003
  16. # Modified to remove useless tests for antiquated kernel versions that
  17. # this doesn't even work with anyway, and introduces a dependency on /usr
  18. # being mounted, which causes cosmetic errors on hosts that NFS mount /usr
  19. # Andrew Pollock, February 2005
  20. # Modified to work on point-to-point links. Andrew Pollock, June 2005
  21. # Modified to support passing the parameters called with to the hooks. Andrew Pollock, November 2005
  22. # The alias handling in here probably still sucks. -mdz
  23. # log an error.
  24. error() { logger -p daemon.err "$@"; }
  25. # wait for given file to be writable
  26. wait_for_rw() {
  27. local file=$1
  28. # Find out whether we are going to mount / rw
  29. exec 9>&0 </etc/fstab
  30. rootmode=rw
  31. while read dev mnt type opts dump pass junk; do
  32. [ "$mnt" != / ] && continue
  33. case "$opts" in
  34. ro|ro,*|*,ro|*,ro,*)
  35. rootmode=ro
  36. ;;
  37. esac
  38. done
  39. exec 0>&9 9>&-
  40. # Wait for $file to become writable
  41. if [ "$rootmode" = "rw" ]; then
  42. while ! { : >> "$file"; } 2>/dev/null; do
  43. sleep 0.1
  44. done
  45. fi
  46. }
  47. # update /etc/resolv.conf based on received values
  48. make_resolv_conf() {
  49. local new_resolv_conf
  50. # DHCPv4
  51. if [ -n "$new_domain_search" ] || [ -n "$new_domain_name" ] ||
  52. [ -n "$new_domain_name_servers" ]; then
  53. resolv_conf=$(readlink -f "/etc/resolv.conf" 2>/dev/null) ||
  54. resolv_conf="/etc/resolv.conf"
  55. new_resolv_conf="${resolv_conf}.dhclient-new.$$"
  56. wait_for_rw "$new_resolv_conf"
  57. rm -f $new_resolv_conf
  58. if [ -n "$new_domain_name" ]; then
  59. echo domain ${new_domain_name%% *} >>$new_resolv_conf
  60. fi
  61. if [ -n "$new_domain_search" ]; then
  62. if [ -n "$new_domain_name" ]; then
  63. domain_in_search_list=""
  64. for domain in $new_domain_search; do
  65. if [ "$domain" = "${new_domain_name}" ] ||
  66. [ "$domain" = "${new_domain_name}." ]; then
  67. domain_in_search_list="Yes"
  68. fi
  69. done
  70. if [ -z "$domain_in_search_list" ]; then
  71. new_domain_search="$new_domain_name $new_domain_search"
  72. fi
  73. fi
  74. echo "search ${new_domain_search}" >> $new_resolv_conf
  75. elif [ -n "$new_domain_name" ]; then
  76. echo "search ${new_domain_name}" >> $new_resolv_conf
  77. fi
  78. if [ -n "$new_domain_name_servers" ]; then
  79. for nameserver in $new_domain_name_servers; do
  80. echo nameserver $nameserver >>$new_resolv_conf
  81. done
  82. else # keep 'old' nameservers
  83. sed -n /^\w*[Nn][Aa][Mm][Ee][Ss][Ee][Rr][Vv][Ee][Rr]/p $resolv_conf >>$new_resolv_conf
  84. fi
  85. if [ -f $resolv_conf ]; then
  86. chown --reference=$resolv_conf $new_resolv_conf
  87. chmod --reference=$resolv_conf $new_resolv_conf
  88. fi
  89. mv -f $new_resolv_conf $resolv_conf
  90. # DHCPv6
  91. elif [ -n "$new_dhcp6_domain_search" ] || [ -n "$new_dhcp6_name_servers" ]; then
  92. resolv_conf=$(readlink -f "/etc/resolv.conf" 2>/dev/null) ||
  93. resolv_conf="/etc/resolv.conf"
  94. new_resolv_conf="${resolv_conf}.dhclient-new.$$"
  95. wait_for_rw "$new_resolv_conf"
  96. rm -f $new_resolv_conf
  97. if [ -n "$new_dhcp6_domain_search" ]; then
  98. echo "search ${new_dhcp6_domain_search}" >> $new_resolv_conf
  99. fi
  100. if [ -n "$new_dhcp6_name_servers" ]; then
  101. for nameserver in $new_dhcp6_name_servers; do
  102. # append %interface to link-local-address nameservers
  103. if [ "${nameserver##fe80::}" != "$nameserver" ] ||
  104. [ "${nameserver##FE80::}" != "$nameserver" ]; then
  105. nameserver="${nameserver}%${interface}"
  106. fi
  107. echo nameserver $nameserver >>$new_resolv_conf
  108. done
  109. else # keep 'old' nameservers
  110. sed -n /^\w*[Nn][Aa][Mm][Ee][Ss][Ee][Rr][Vv][Ee][Rr]/p $resolv_conf >>$new_resolv_conf
  111. fi
  112. if [ -f $resolv_conf ]; then
  113. chown --reference=$resolv_conf $new_resolv_conf
  114. chmod --reference=$resolv_conf $new_resolv_conf
  115. fi
  116. mv -f $new_resolv_conf $resolv_conf
  117. fi
  118. }
  119. # set host name
  120. set_hostname() {
  121. local current_hostname
  122. if [ -n "$new_host_name" ]; then
  123. current_hostname=$(hostname)
  124. # current host name is empty, '(none)' or 'localhost' or differs from new one from DHCP
  125. if [ -z "$current_hostname" ] ||
  126. [ "$current_hostname" = '(none)' ] ||
  127. [ "$current_hostname" = 'localhost' ] ||
  128. [ "$current_hostname" = "$old_host_name" ]; then
  129. if [ "$new_host_name" != "$current_host_name" ]; then
  130. hostname "$new_host_name"
  131. fi
  132. fi
  133. fi
  134. }
  135. # set the link up and wait for ipv6 link local dad to finish
  136. ipv6_link_up_and_dad() {
  137. local dev=$1 delay=${2:-0.1} attempts=${3:-60}
  138. ip link set up dev "$dev" ||
  139. { error "$dev: failed to set link up"; return 1; }
  140. local n=0
  141. while :; do
  142. n=$((n+1))
  143. # note: busybox ip does not understand 'tentative' as input
  144. # so we cannot just use the tentative flag and check for empty
  145. out=$(ip -6 -o address show dev "$dev" scope link) || {
  146. error "$dev: checking for link-local addresses failed";
  147. return 1
  148. }
  149. case " $out " in
  150. *\ dadfailed\ *)
  151. error "$dev: ipv6 dad failed."
  152. return 1;;
  153. *\ tentative\ *) :;;
  154. *) return 0;;
  155. esac
  156. [ $n -lt $attempts ] || {
  157. error "$dev: time out waiting for permanent link-local address"
  158. return 1;
  159. }
  160. sleep $delay
  161. done
  162. }
  163. # run given script
  164. run_hook() {
  165. local script
  166. local exit_status
  167. script="$1"
  168. if [ -f $script ]; then
  169. . $script
  170. exit_status=$?
  171. fi
  172. if [ -n "$exit_status" ] && [ "$exit_status" -ne 0 ]; then
  173. logger -p daemon.err "$script returned non-zero exit status $exit_status"
  174. fi
  175. return $exit_status
  176. }
  177. # run scripts in given directory
  178. run_hookdir() {
  179. local dir
  180. local exit_status
  181. dir="$1"
  182. if [ -d "$dir" ]; then
  183. for script in $(run-parts --list $dir); do
  184. run_hook $script
  185. exit_status=$((exit_status|$?))
  186. done
  187. fi
  188. return $exit_status
  189. }
  190. # Must be used on exit. Invokes the local dhcp client exit hooks, if any.
  191. exit_with_hooks() {
  192. exit_status=$1
  193. # Source the documented exit-hook script, if it exists
  194. if ! run_hook /etc/dhcp/dhclient-exit-hooks; then
  195. exit_status=$?
  196. fi
  197. # Now run scripts in the Debian-specific directory.
  198. if ! run_hookdir /etc/dhcp/dhclient-exit-hooks.d; then
  199. exit_status=$?
  200. fi
  201. exit $exit_status
  202. }
  203. # The 576 MTU is only used for X.25 and dialup connections
  204. # where the admin wants low latency. Such a low MTU can cause
  205. # problems with UDP traffic, among other things. As such,
  206. # disallow MTUs from 576 and below by default, so that broken
  207. # MTUs are ignored, but higher stuff is allowed (1492, 1500, etc).
  208. if [ -z "$new_interface_mtu" ] || [ "$new_interface_mtu" -le 576 ]; then
  209. new_interface_mtu=''
  210. fi
  211. # The action starts here
  212. # Invoke the local dhcp client enter hooks, if they exist.
  213. run_hook /etc/dhcp/dhclient-enter-hooks
  214. run_hookdir /etc/dhcp/dhclient-enter-hooks.d
  215. # Execute the operation
  216. case "$reason" in
  217. ### DHCPv4 Handlers
  218. MEDIUM|ARPCHECK|ARPSEND)
  219. # Do nothing
  220. ;;
  221. PREINIT)
  222. # The DHCP client is requesting that an interface be
  223. # configured as required in order to send packets prior to
  224. # receiving an actual address. - dhclient-script(8)
  225. # ensure interface is up
  226. ip link set dev ${interface} up
  227. if [ -n "$alias_ip_address" ]; then
  228. # flush alias IP from interface
  229. ip -4 addr flush dev ${interface} label ${interface}:0
  230. fi
  231. ;;
  232. BOUND|RENEW|REBIND|REBOOT)
  233. set_hostname
  234. if [ -n "$old_ip_address" ] && [ -n "$alias_ip_address" ] &&
  235. [ "$alias_ip_address" != "$old_ip_address" ]; then
  236. # alias IP may have changed => flush it
  237. ip -4 addr flush dev ${interface} label ${interface}:0
  238. fi
  239. if [ -n "$old_ip_address" ] &&
  240. [ "$old_ip_address" != "$new_ip_address" ]; then
  241. # leased IP has changed => flush it
  242. ip -4 addr flush dev ${interface} label ${interface}
  243. fi
  244. if [ -z "$old_ip_address" ] ||
  245. [ "$old_ip_address" != "$new_ip_address" ] ||
  246. [ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ]; then
  247. # new IP has been leased or leased IP changed => set it
  248. ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \
  249. ${new_broadcast_address:+broadcast $new_broadcast_address} \
  250. dev ${interface} label ${interface}
  251. if [ -n "$new_interface_mtu" ]; then
  252. # set MTU
  253. ip link set dev ${interface} mtu ${new_interface_mtu}
  254. fi
  255. # if we have $new_rfc3442_classless_static_routes then we have to
  256. # ignore $new_routers entirely
  257. if [ ! "$new_rfc3442_classless_static_routes" ]; then
  258. # set if_metric if IF_METRIC is set or there's more than one router
  259. if_metric="$IF_METRIC"
  260. if [ "${new_routers%% *}" != "${new_routers}" ]; then
  261. if_metric=${if_metric:-1}
  262. fi
  263. for router in $new_routers; do
  264. if [ "$new_subnet_mask" = "255.255.255.255" ]; then
  265. # point-to-point connection => set explicit route
  266. ip -4 route add ${router} dev $interface >/dev/null 2>&1
  267. fi
  268. # set default route
  269. ip -4 route add default via ${router} dev ${interface} \
  270. ${if_metric:+metric $if_metric} >/dev/null 2>&1
  271. if [ -n "$if_metric" ]; then
  272. if_metric=$((if_metric+1))
  273. fi
  274. done
  275. fi
  276. fi
  277. if [ -n "$alias_ip_address" ] &&
  278. [ "$new_ip_address" != "$alias_ip_address" ]; then
  279. # separate alias IP given, which may have changed
  280. # => flush it, set it & add host route to it
  281. ip -4 addr flush dev ${interface} label ${interface}:0
  282. ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \
  283. dev ${interface} label ${interface}:0
  284. ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
  285. fi
  286. # update /etc/resolv.conf
  287. make_resolv_conf
  288. ;;
  289. EXPIRE|FAIL|RELEASE|STOP)
  290. if [ -n "$alias_ip_address" ]; then
  291. # flush alias IP
  292. ip -4 addr flush dev ${interface} label ${interface}:0
  293. fi
  294. if [ -n "$old_ip_address" ]; then
  295. # flush leased IP
  296. ip -4 addr flush dev ${interface} label ${interface}
  297. fi
  298. if [ -n "$alias_ip_address" ]; then
  299. # alias IP given => set it & add host route to it
  300. ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \
  301. dev ${interface} label ${interface}:0
  302. ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
  303. fi
  304. ;;
  305. TIMEOUT)
  306. if [ -n "$alias_ip_address" ]; then
  307. # flush alias IP
  308. ip -4 addr flush dev ${interface} label ${interface}:0
  309. fi
  310. # set IP from recorded lease
  311. ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \
  312. ${new_broadcast_address:+broadcast $new_broadcast_address} \
  313. dev ${interface} label ${interface}
  314. if [ -n "$new_interface_mtu" ]; then
  315. # set MTU
  316. ip link set dev ${interface} mtu ${new_interface_mtu}
  317. fi
  318. # if there is no router recorded in the lease or the 1st router answers pings
  319. if [ -z "$new_routers" ] || ping -q -c 1 "${new_routers%% *}"; then
  320. # if we have $new_rfc3442_classless_static_routes then we have to
  321. # ignore $new_routers entirely
  322. if [ ! "$new_rfc3442_classless_static_routes" ]; then
  323. if [ -n "$alias_ip_address" ] &&
  324. [ "$new_ip_address" != "$alias_ip_address" ]; then
  325. # separate alias IP given => set up the alias IP & add host route to it
  326. ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \
  327. dev ${interface} label ${interface}:0
  328. ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
  329. fi
  330. # set if_metric if IF_METRIC is set or there's more than one router
  331. if_metric="$IF_METRIC"
  332. if [ "${new_routers%% *}" != "${new_routers}" ]; then
  333. if_metric=${if_metric:-1}
  334. fi
  335. # set default route
  336. for router in $new_routers; do
  337. ip -4 route add default via ${router} dev ${interface} \
  338. ${if_metric:+metric $if_metric} >/dev/null 2>&1
  339. if [ -n "$if_metric" ]; then
  340. if_metric=$((if_metric+1))
  341. fi
  342. done
  343. fi
  344. # update /etc/resolv.conf
  345. make_resolv_conf
  346. else
  347. # flush all IPs from interface
  348. ip -4 addr flush dev ${interface}
  349. exit_with_hooks 2
  350. fi
  351. ;;
  352. ### DHCPv6 Handlers
  353. # TODO handle prefix change: ?based on ${old_ip6_prefix} and ${new_ip6_prefix}?
  354. PREINIT6)
  355. # ensure interface is up
  356. ipv6_link_up_and_dad "$interface"
  357. # flush any stale global permanent IPs from interface
  358. ip -6 addr flush dev ${interface} scope global permanent
  359. ;;
  360. BOUND6|RENEW6|REBIND6)
  361. if [ "${new_ip6_address}" ]; then
  362. # set leased IP
  363. ip -6 addr add ${new_ip6_address} \
  364. dev ${interface} scope global
  365. fi
  366. # update /etc/resolv.conf
  367. if [ "${reason}" = BOUND6 ] ||
  368. [ "${new_dhcp6_name_servers}" != "${old_dhcp6_name_servers}" ] ||
  369. [ "${new_dhcp6_domain_search}" != "${old_dhcp6_domain_search}" ]; then
  370. make_resolv_conf
  371. fi
  372. ;;
  373. DEPREF6)
  374. # set preferred lifetime of leased IP to 0
  375. ip -6 addr change ${cur_ip6_address} \
  376. dev ${interface} scope global preferred_lft 0
  377. ;;
  378. EXPIRE6|RELEASE6|STOP6)
  379. if [ -z "${old_ip6_address}" ]; then
  380. exit_with_hooks 2
  381. fi
  382. # delete leased IP
  383. ip -6 addr del ${old_ip6_address} \
  384. dev ${interface}
  385. ;;
  386. esac
  387. exit_with_hooks 0