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.

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