You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

628 lines
13 KiB

  1. #!/bin/sh /etc/rc.common
  2. START=25
  3. USE_PROCD=1
  4. PROG=/usr/sbin/dhcpd
  5. TTL=3600
  6. PREFIX="update add"
  7. lease_file=/tmp/dhcpd.leases
  8. config_file=/tmp/run/dhcpd.conf
  9. dyndir=/tmp/bind
  10. conf_local_file=$dyndir/named.conf.local
  11. session_key_name=local-ddns
  12. session_key_file=/var/run/named/session.key
  13. dyn_file=$(mktemp -u /tmp/dhcpd.XXXXXX)
  14. time2seconds() {
  15. local timestring=$1
  16. local multiplier number suffix
  17. suffix="${timestring//[0-9 ]}"
  18. number="${timestring%%$suffix}"
  19. [ "$number$suffix" != "$timestring" ] && return 1
  20. case "$suffix" in
  21. "" | s)
  22. multiplier=1
  23. ;;
  24. m)
  25. multiplier=60
  26. ;;
  27. h)
  28. multiplier=3600
  29. ;;
  30. d)
  31. multiplier=86400
  32. ;;
  33. w)
  34. multiplier=604800
  35. ;;
  36. *)
  37. return 1
  38. ;;
  39. esac
  40. echo $(( number * multiplier ))
  41. }
  42. trim()
  43. {
  44. local arg="$1"
  45. echo "$arg" | sed -e 's/^ *//' -e 's/ *$//'
  46. }
  47. # duplicated from dnsmasq init script
  48. hex_to_hostid() {
  49. local var="$1"
  50. local hex="${2#0x}" # strip optional "0x" prefix
  51. if [ -n "${hex//[0-9a-fA-F]/}" ]; then
  52. # is invalid hex literal
  53. return 1
  54. fi
  55. # convert into host id
  56. export "$var=$(
  57. printf "%0x:%0x" \
  58. $(((0x$hex >> 16) % 65536)) \
  59. $(( 0x$hex % 65536))
  60. )"
  61. return 0
  62. }
  63. typeof() {
  64. echo "$1" | awk '
  65. /^\d+\.\d+\.\d+\.\d+$/ { print "ip\n"; next; }
  66. /^(true|false)$/ { print "bool\n"; next; }
  67. /^\d+$/ { print "integer\n"; next; }
  68. /^"[^"]*"$/ { print "string\n"; next; }
  69. /^[0-9a-fA-F]{2,2}(:[0-9a-fA-F]{2,2})*$/ { print "string\n"; next; }
  70. { print "other\n"; next; }
  71. '
  72. }
  73. update() {
  74. local lhs="$1" family="$2" type="$3"
  75. shift 3
  76. [ $dynamicdns -eq 1 ] && \
  77. echo -e "$PREFIX" "$lhs $family $type $@\nsend" >> $dyn_file
  78. }
  79. explode() {
  80. local arg="$1"
  81. echo "$arg" | sed -e 's/\./, /g'
  82. }
  83. rev_str() {
  84. local str="$1" delim="$2"
  85. local frag result="" IFS="$delim"
  86. for frag in $str; do
  87. result="$frag${result:+$delim}$result"
  88. done
  89. echo "$result"
  90. }
  91. create_empty_zone() {
  92. local zone="$1"
  93. if [ ! -f $dyndir/db."$zone" ]; then
  94. cp -p /etc/bind/db.empty $dyndir/db."$zone"
  95. chmod g+w $dyndir/db."$zone"
  96. chgrp bind $dyndir/db."$zone"
  97. fi
  98. }
  99. append_routes() {
  100. local tuple tuples="$1"
  101. local string=
  102. local IFS=','
  103. for tuple in $tuples; do
  104. local network prefix router save octets compacted
  105. tuple="$(trim "$tuple")"
  106. save="${tuple% *}"
  107. router="$(trim "${tuple#${save} }")"
  108. network="$(trim "${save%/[0-9]*}")"
  109. prefix="$(trim "${save##${network}/}")"
  110. octets=$((($prefix + 7) / 8))
  111. compacted="$(echo "$network" | cut -d. -f1-$octets)"
  112. string="${string:+, }$(explode "$prefix.$compacted.$router")"
  113. done
  114. echo " option classless-ipv4-route $string;"
  115. }
  116. append_dhcp_options() {
  117. local tuple="$1"
  118. # strip redundant "option:" prefix
  119. tuple="${tuple#option:}"
  120. local tag="${tuple%%,*}"
  121. local values="${tuple#$tag,}"
  122. local formatted value
  123. local IFS=$', \n'
  124. for value in $values; do
  125. # detect type of $value and quote if necessary
  126. case $(typeof "$value") in
  127. ip|bool|integer|string)
  128. ;;
  129. other)
  130. value="\"$value\""
  131. ;;
  132. esac
  133. formatted="$formatted${formatted:+, }$value"
  134. done
  135. echo " option $tag $formatted;"
  136. }
  137. static_cname_add() {
  138. local cfg="$1"
  139. local cname target
  140. config_get cname "$cfg" "cname"
  141. [ -n "$cname" ] || return 0
  142. config_get target "$cfg" "target"
  143. [ -n "$target" ] || return 0
  144. update "$cname.$domain." IN CNAME "$target.$domain."
  145. }
  146. static_cnames() {
  147. config_foreach static_cname_add cname "$@"
  148. }
  149. static_domain_add() {
  150. local cfg="$1"
  151. local name ip ips revip
  152. config_get name "$cfg" "name"
  153. [ -n "$name" ] || return 0
  154. config_get ip "$cfg" "ip"
  155. [ -n "$ip" ] || return 0
  156. ips="$ip"
  157. for ip in $ips; do
  158. revip="$(rev_str "$ip" ".")"
  159. update "$name.$domain." IN A "$ip"
  160. update "$revip.in-addr.arpa." IN PTR "$name.$domain."
  161. done
  162. }
  163. static_domains() {
  164. config_foreach static_domain_add domain "$@"
  165. }
  166. static_mxhost_add() {
  167. local cfg="$1"
  168. local domain2 relay pref
  169. config_get domain2 "$cfg" "domain"
  170. [ -n "$domain2" ] || return 0
  171. config_get relay "$cfg" "relay"
  172. [ -n "$relay" ] || return 0
  173. config_get pref "$cfg" "pref"
  174. [ -n "$pref" ] || return 0
  175. if [ "$domain2" = "@" ]; then
  176. update "$domain." IN MX "$pref" "$relay.$domain."
  177. else
  178. update "$domain2.$domain." IN MX "$pref" "$relay.$domain."
  179. fi
  180. }
  181. static_mxhosts() {
  182. config_foreach static_mxhost_add mxhost "$@"
  183. }
  184. static_srvhost_add() {
  185. local cfg="$1"
  186. local srv target port priority weight
  187. config_get srv "$cfg" "srv"
  188. [ -n "$srv" ] || return 0
  189. config_get target "$cfg" "target"
  190. [ -n "$target" ] || return 0
  191. config_get port "$cfg" "port"
  192. [ -n "$port" ] || return 0
  193. config_get priority "$cfg" "priority"
  194. [ -n "$priority" ] || return 0
  195. config_get weight "$cfg" "weight"
  196. [ -n "$weight" ] || return 0
  197. update "$srv.$domain." IN SRV "$priority" "$weight" "$port" "$target"
  198. }
  199. static_srvhosts() {
  200. config_foreach static_srvhost_add srvhost "$@"
  201. }
  202. static_host_add() {
  203. local cfg="$1"
  204. local broadcast hostid macn macs mac name ip ips revip leasetime
  205. config_get macs "$cfg" "mac"
  206. [ -n "$macs" ] || return 0
  207. config_get name "$cfg" "name"
  208. [ -n "$name" ] || return 0
  209. config_get ip "$cfg" "ip"
  210. [ -n "$ip" ] || return 0
  211. config_get_bool broadcast "$cfg" "broadcast" 0
  212. config_get dns "$cfg" "dns"
  213. config_get gateway "$cfg" "gateway"
  214. config_get leasetime "$cfg" "leasetime"
  215. if [ -n "$leasetime" ] ; then
  216. leasetime="$(time2seconds "$leasetime")"
  217. [ "$?" -ne 0 ] && return 1
  218. fi
  219. config_get hostid "$cfg" "hostid"
  220. if [ -n "$hostid" ] ; then
  221. hex_to_hostid hostid "$hostid" || return 1
  222. fi
  223. macn=0
  224. for mac in $macs; do
  225. macn=$(( macn + 1 ))
  226. done
  227. for mac in $macs; do
  228. local secname="$name"
  229. if [ $macn -gt 1 ] ; then
  230. secname="${name}-${mac//:}"
  231. fi
  232. echo "host $secname {"
  233. echo " hardware ethernet $mac;"
  234. echo " fixed-address $ip;"
  235. echo " option host-name \"$name\";"
  236. if [ "$broadcast" -eq 1 ] ; then
  237. echo " always-broadcast true;"
  238. fi
  239. if [ -n "$leasetime" ] ; then
  240. echo " default-lease-time $leasetime;"
  241. echo " max-lease-time $leasetime;"
  242. fi
  243. if [ -n "$hostid" ] ; then
  244. echo " option dhcp-client-identifier $hostid;"
  245. fi
  246. if [ -n "$dns" ] ; then
  247. echo " option domain-name-servers $dns;"
  248. fi
  249. if [ -n "$gateway" ] ; then
  250. echo " option routers $gateway;"
  251. fi
  252. config_list_foreach "$cfg" "routes" append_routes
  253. config_list_foreach "$cfg" "dhcp_option" append_dhcp_options
  254. echo "}"
  255. done
  256. ips="$ip"
  257. for ip in $ips; do
  258. revip="$(rev_str "$ip" ".")"
  259. update "$name.$domain." IN A "$ip"
  260. update "$revip.in-addr.arpa." IN PTR "$name.$domain."
  261. done
  262. }
  263. static_hosts() {
  264. config_foreach static_host_add host "$@"
  265. }
  266. gen_dhcp_subnet() {
  267. local cfg="$1"
  268. echo "subnet $NETWORK netmask $NETMASK {"
  269. echo " range $START $END;"
  270. echo " option subnet-mask $netmask;"
  271. if [ "$BROADCAST" != "0.0.0.0" ] ; then
  272. echo " option broadcast-address $BROADCAST;"
  273. fi
  274. if [ "$dynamicdhcp" -eq 0 ] ; then
  275. if [ "$authoritative" -eq 1 ] ; then
  276. echo " deny unknown-clients;"
  277. else
  278. echo " ignore unknown-clients;"
  279. fi
  280. fi
  281. if [ -n "$leasetime" ] ; then
  282. echo " default-lease-time $leasetime;"
  283. echo " max-lease-time $leasetime;"
  284. fi
  285. echo " option routers $gateway;"
  286. echo " option domain-name-servers $DNS;"
  287. config_list_foreach "$cfg" "routes" append_routes
  288. config_list_foreach "$cfg" "dhcp_option" append_dhcp_options
  289. echo "}"
  290. }
  291. dhcpd_add() {
  292. local cfg="$1" synthesize="$2"
  293. local dhcp6range="::"
  294. local dynamicdhcp end gateway ifname ignore leasetime limit net netmask
  295. local proto networkid start subnet
  296. local IP NETMASK BROADCAST NETWORK PREFIX DNS START END
  297. config_get_bool ignore "$cfg" "ignore" 0
  298. [ "$ignore" = "0" ] || return 0
  299. config_get net "$cfg" "interface"
  300. [ -n "$net" ] || return 0
  301. config_get start "$cfg" "start"
  302. [ -n "$start" ] || return 0
  303. config_get limit "$cfg" "limit"
  304. [ -n "$limit" ] || return 0
  305. network_get_subnet subnet "$net" || return 0
  306. network_get_device ifname "$net" || return 0
  307. network_get_protocol proto "$net" || return 0
  308. [ static = "$proto" ] || return 0
  309. local pair="$(echo "${subnet%%/*}" | cut -d. -f1-2)"
  310. case "$pair" in
  311. 10.*)
  312. rfc1918_nets="$rfc1918_nets${rfc1918_nets:+ }10"
  313. ;;
  314. 172.1[6789]|172.2[0-9]|172.3[01]|192.168)
  315. rfc1918_nets="$rfc1918_nets${rfc1918_nets:+ }$pair"
  316. ;;
  317. esac
  318. [ $synthesize -eq 0 ] && return
  319. config_get_bool dynamicdhcp "$cfg" "dynamicdhcp" 1
  320. dhcp_ifs="$dhcp_ifs $ifname"
  321. eval "$(ipcalc.sh $subnet $start $limit)"
  322. config_get netmask "$cfg" "netmask" "$NETMASK"
  323. config_get leasetime "$cfg" "leasetime"
  324. if [ -n "$leasetime" ] ; then
  325. leasetime="$(time2seconds "$leasetime")"
  326. [ "$?" -ne 0 ] && return 1
  327. fi
  328. if network_get_dnsserver dnsserver "$net" ; then
  329. for dnsserv in $dnsserver ; do
  330. DNS="$DNS${DNS:+, }$dnsserv"
  331. done
  332. else
  333. DNS="$IP"
  334. fi
  335. if ! network_get_gateway gateway "$net" ; then
  336. gateway="$IP"
  337. fi
  338. gen_dhcp_subnet "$cfg"
  339. }
  340. general_config() {
  341. local always_broadcast boot_unknown_clients log_facility
  342. local default_lease_time max_lease_time
  343. config_get_bool always_broadcast "isc_dhcpd" "always_broadcast" 0
  344. config_get_bool authoritative "isc_dhcpd" "authoritative" 1
  345. config_get_bool boot_unknown_clients "isc_dhcpd" "boot_unknown_clients" 1
  346. config_get default_lease_time "isc_dhcpd" "default_lease_time" 3600
  347. config_get max_lease_time "isc_dhcpd" "max_lease_time" 86400
  348. config_get log_facility "isc_dhcpd" "log_facility"
  349. config_get domain "isc_dhcpd" "domain"
  350. config_get_bool dynamicdns "isc_dhcpd" dynamicdns 0
  351. [ $always_broadcast -eq 1 ] && echo "always-broadcast true;"
  352. [ $authoritative -eq 1 ] && echo "authoritative;"
  353. [ $boot_unknown_clients -eq 0 ] && echo "boot-unknown-clients false;"
  354. default_lease_time="$(time2seconds "$default_lease_time")"
  355. [ "$?" -ne 0 ] && return 1
  356. max_lease_time="$(time2seconds "$max_lease_time")"
  357. [ "$?" -ne 0 ] && return 1
  358. if [ $dynamicdns -eq 1 ]; then
  359. create_empty_zone "$domain"
  360. local mynet
  361. for mynet in $rfc1918_nets; do
  362. mynet="$(rev_str "$mynet" ".")"
  363. create_empty_zone "$mynet.in-addr.arpa"
  364. done
  365. cat <<EOF > $conf_local_file
  366. zone "$domain" {
  367. type master;
  368. file "$dyndir/db.$domain";
  369. allow-update { key $session_key_name; };
  370. allow-transfer { key $session_key_name; };
  371. };
  372. EOF
  373. for mynet in $rfc1918_nets; do
  374. mynet="$(rev_str "$mynet" ".")"
  375. cat <<EOF >> $conf_local_file
  376. zone "$mynet.in-addr.arpa" {
  377. type master;
  378. file "$dyndir/db.$mynet.in-addr.arpa";
  379. allow-update { key $session_key_name; };
  380. allow-transfer { key $session_key_name; };
  381. };
  382. EOF
  383. done
  384. /etc/init.d/named reload
  385. sleep 1
  386. cat <<EOF
  387. ddns-domainname "$domain.";
  388. ddns-update-style standard;
  389. ddns-updates on;
  390. ignore client-updates;
  391. update-static-leases on;
  392. use-host-decl-names on;
  393. update-conflict-detection off;
  394. update-optimization off;
  395. include "$session_key_file";
  396. zone $domain. {
  397. primary 127.0.0.1;
  398. key local-ddns;
  399. }
  400. EOF
  401. for mynet in $rfc1918_nets; do
  402. mynet="$(rev_str "$mynet" ".")"
  403. cat <<EOF
  404. zone $mynet.in-addr.arpa. {
  405. primary 127.0.0.1;
  406. key local-ddns;
  407. }
  408. EOF
  409. done
  410. fi
  411. if [ -n "$log_facility" ] ; then
  412. echo "log-facility $log_facility;"
  413. fi
  414. echo "default-lease-time $default_lease_time;"
  415. echo "max-lease-time $max_lease_time;"
  416. [ -n "$domain" ] && echo "option domain-name \"$domain\";"
  417. echo -e "\n# additional codes\noption classless-ipv4-route code 121 = array of { unsigned integer 8 };\n"
  418. rm -f /tmp/resolv.conf
  419. echo "# This file is generated by the DHCPD service" > /tmp/resolv.conf
  420. [ -n "$domain" ] && echo "domain $domain" >> /tmp/resolv.conf
  421. echo "nameserver 127.0.0.1" >> /tmp/resolv.conf
  422. }
  423. # base procd hooks
  424. boot() {
  425. DHCPD_BOOT=1
  426. start "$@"
  427. }
  428. start_service() {
  429. local domain dhcp_ifs authoritative dynamicdns
  430. if [ -n "$DHCPD_BOOT" ] ; then
  431. return 0
  432. fi
  433. if [ ! -e $lease_file ] ; then
  434. touch $lease_file
  435. fi
  436. if [ -e "/etc/dhcpd.conf" ] ; then
  437. config_file="/etc/dhcpd.conf"
  438. else
  439. . /lib/functions/network.sh
  440. config_load dhcp
  441. local rfc1918_nets=""
  442. # alas we have to make 2 passes...
  443. config_foreach dhcpd_add dhcp 0
  444. rfc1918_nets="$(echo "$rfc1918_nets" | tr ' ' $'\n' | sort | uniq | tr $'\n' ' ')"
  445. general_config > $config_file
  446. if [ $dynamicdns -eq 1 ]; then
  447. cat <<EOF > $dyn_file
  448. ; Generated by /etc/init.d/dhcpd at $(date)
  449. ttl $TTL
  450. EOF
  451. fi
  452. rfc1918_nets=
  453. config_foreach dhcpd_add dhcp 1 >> $config_file
  454. static_hosts >> $config_file
  455. static_cnames >> $config_file
  456. static_domains >> $config_file
  457. static_mxhosts >> $config_file
  458. static_srvhosts >> $config_file
  459. if [ $dynamicdns -eq 1 ]; then
  460. nsupdate -l -v $dyn_file
  461. rm -f $dyn_file
  462. fi
  463. [ -z "$dhcp_ifs" ] && return 0
  464. fi
  465. procd_open_instance
  466. procd_set_param command $PROG -q -f -cf $config_file -lf $lease_file $dhcp_ifs
  467. procd_close_instance
  468. }
  469. reload_service() {
  470. rc_procd start_service "$@"
  471. procd_send_signal dhcpd "$@"
  472. }
  473. add_interface_trigger() {
  474. local cfg=$1
  475. local trigger ignore
  476. config_get trigger "$cfg" interface
  477. config_get_bool ignore "$cfg" ignore 0
  478. if [ -n "$trigger" -a $ignore -eq 0 ] ; then
  479. procd_add_reload_interface_trigger "$trigger"
  480. fi
  481. }
  482. service_triggers() {
  483. if [ -n "$DHCPD_BOOT" ] ; then
  484. # Make the first start robust to slow interfaces; wait a while
  485. procd_add_raw_trigger "interface.*.up" 5000 /etc/init.d/dhcpd restart
  486. else
  487. # reload with normal parameters
  488. procd_add_reload_trigger "network" "dhcp"
  489. config_load dhcp
  490. config_foreach add_interface_trigger dhcp
  491. fi
  492. }