dhclient-script 14 KB

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