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.

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