309d3ec87abfa37c4c3b2526ff8cd8602d64591c
[feed/packages.git] / net / isc-dhcp / files / dhcpd.init
1 #!/bin/sh /etc/rc.common
2
3 START=25
4 USE_PROCD=1
5 PROG=/usr/sbin/dhcpd
6
7 TTL=3600
8 PREFIX="update add"
9
10 lease_file=/tmp/dhcpd.leases
11 config_file=/tmp/run/dhcpd.conf
12
13 dyndir=/tmp/bind
14 conf_local_file=$dyndir/named.conf.local
15
16 session_key_name=local-ddns
17 session_key_file=/var/run/named/session.key
18
19 time2seconds() {
20 local timestring=$1
21 local multiplier number suffix
22
23 suffix="${timestring//[0-9 ]}"
24 number="${timestring%%$suffix}"
25 [ "$number$suffix" != "$timestring" ] && return 1
26 case "$suffix" in
27 "" | s)
28 multiplier=1
29 ;;
30 m)
31 multiplier=60
32 ;;
33 h)
34 multiplier=3600
35 ;;
36 d)
37 multiplier=86400
38 ;;
39 w)
40 multiplier=604800
41 ;;
42 *)
43 return 1
44 ;;
45 esac
46 echo $(( number * multiplier ))
47 }
48
49 trim() {
50 local arg="$1"
51
52 echo "$arg" | sed -e 's/^ *//' -e 's/ *$//'
53 }
54
55 rfc1918_prefix() {
56 local octets="$(echo "${1%%/*}" | cut -d. -f1)"
57
58 [ "$octets" = "10" ] && { echo "$octets"; return; }
59
60 octets="$(echo "${1%%/*}" | cut -d. -f1-2)"
61
62 case "$octets" in
63 172.1[6789]|172.2[0-9]|172.3[01]|192.168)
64 echo "$octets"
65 ;;
66 esac
67 }
68
69 no_ipv6() {
70 [ -n "$(named-checkconf -px \
71 | sed -r -ne '1N; N; /^\tlisten-on-v6 ?\{\n\t\t"none";\n\t\};$/{ p; q; }; D')" ]
72 }
73
74 # duplicated from dnsmasq init script
75 hex_to_hostid() {
76 local var="$1"
77 local hex="${2#0x}" # strip optional "0x" prefix
78
79 if [ -n "${hex//[0-9a-fA-F]/}" ]; then
80 # is invalid hex literal
81 return 1
82 fi
83
84 # convert into host id
85 export "$var=$(
86 printf "%0x:%0x" \
87 $(((0x$hex >> 16) % 65536)) \
88 $(( 0x$hex % 65536))
89 )"
90
91 return 0
92 }
93
94 typeof() {
95 echo "$1" | awk '
96 /^\d+\.\d+\.\d+\.\d+$/ { print "ip\n"; next; }
97 /^(true|false)$/ { print "bool\n"; next; }
98 /^\d+$/ { print "integer\n"; next; }
99 /^"[^"]*"$/ { print "string\n"; next; }
100 /^[0-9a-fA-F]{2,2}(:[0-9a-fA-F]{2,2})*$/ { print "string\n"; next; }
101 { print "other\n"; next; }
102 '
103 }
104
105 update() {
106 local lhs="$1" family="$2" type="$3"
107 shift 3
108
109 [ $dynamicdns -eq 1 ] && \
110 echo -e "$PREFIX" "$lhs $family $type $@\nsend" >> $dyn_file
111 }
112
113 explode() {
114 local arg="$1"
115
116 echo "$arg" | sed -e 's/\./, /g'
117 }
118
119 rev_str() {
120 local str="$1" delim="$2"
121 local frag result="" IFS="$delim"
122
123 for frag in $str; do
124 result="$frag${result:+$delim}$result"
125 done
126
127 echo "$result"
128 }
129
130 create_empty_zone() {
131 local zone="$1"
132
133 if [ ! -f $dyndir/db."$zone" ]; then
134 cp -p /etc/bind/db.empty $dyndir/db."$zone"
135 chmod g+w $dyndir/db."$zone"
136 chgrp bind $dyndir/db."$zone"
137 fi
138 }
139
140 append_routes() {
141 local tuple tuple="$(trim "$1")"
142 local network prefix router save octets compacted
143
144 save="${tuple% *}"
145 router="$(trim "${tuple#${save} }")"
146
147 network="$(trim "${save%/[0-9]*}")"
148
149 prefix="$(trim "${save##${network}/}")"
150
151 octets=$((($prefix + 7) / 8))
152 compacted="$(echo "$network" | cut -d. -f1-$octets)"
153
154 routes="$routes${routes:+, }$(explode "$prefix${compacted:+.$compacted}.$router")"
155 }
156
157 append_dhcp_options() {
158 local tuple="$1"
159
160 # strip redundant "option:" prefix
161 tuple="${tuple#option:}"
162
163 local tag="${tuple%%,*}"
164 local values="${tuple#$tag,}"
165
166 local formatted value
167 local IFS=$', \n'
168 for value in $values; do
169 # detect type of $value and quote if necessary
170 case $(typeof "$value") in
171 ip|bool|integer|string)
172 ;;
173 other)
174 value="\"$value\""
175 ;;
176 esac
177 formatted="$formatted${formatted:+, }$value"
178 done
179 echo " option $tag $formatted;"
180 }
181
182 static_cname_add() {
183 local cfg="$1"
184 local cname target
185
186 config_get cname "$cfg" "cname"
187 [ -n "$cname" ] || return 0
188 config_get target "$cfg" "target"
189 [ -n "$target" ] || return 0
190
191 update "$cname.$domain." IN CNAME "$target.$domain."
192 }
193
194 static_cnames() {
195 config_foreach static_cname_add cname "$@"
196 }
197
198 static_domain_add() {
199 local cfg="$1"
200 local name ip ips revip
201
202 config_get name "$cfg" "name"
203 [ -n "$name" ] || return 0
204 config_get ip "$cfg" "ip"
205 [ -n "$ip" ] || return 0
206
207 ips="$ip"
208 for ip in $ips; do
209 revip="$(rev_str "$ip" ".")"
210
211 update "$name.$domain." IN A "$ip"
212 [ -n "$(rfc1918_prefix "$ip")" ] && \
213 update "$revip.in-addr.arpa." IN PTR "$name.$domain."
214 done
215 }
216
217 static_domains() {
218 config_foreach static_domain_add domain "$@"
219 }
220
221 static_mxhost_add() {
222 local cfg="$1"
223 local domain2 relay pref
224
225 config_get domain2 "$cfg" "domain"
226 [ -n "$domain2" ] || return 0
227 config_get relay "$cfg" "relay"
228 [ -n "$relay" ] || return 0
229 config_get pref "$cfg" "pref"
230 [ -n "$pref" ] || return 0
231
232 if [ "$domain2" = "@" ]; then
233 update "$domain." IN MX "$pref" "$relay.$domain."
234 else
235 update "$domain2.$domain." IN MX "$pref" "$relay.$domain."
236 fi
237 }
238
239 static_mxhosts() {
240 config_foreach static_mxhost_add mxhost "$@"
241 }
242
243 static_srvhost_add() {
244 local cfg="$1"
245 local srv target port priority weight
246
247 config_get srv "$cfg" "srv"
248 [ -n "$srv" ] || return 0
249 config_get target "$cfg" "target"
250 [ -n "$target" ] || return 0
251 config_get port "$cfg" "port"
252 [ -n "$port" ] || return 0
253 config_get priority "$cfg" "priority"
254 [ -n "$priority" ] || return 0
255 config_get weight "$cfg" "weight"
256 [ -n "$weight" ] || return 0
257
258 update "$srv.$domain." IN SRV "$priority" "$weight" "$port" "$target.$domain"
259 }
260
261 static_srvhosts() {
262 config_foreach static_srvhost_add srvhost "$@"
263 }
264
265 static_host_add() {
266 local cfg="$1"
267 local broadcast hostid macn macs mac name ip ips revip leasetime
268 local force_send extra_options option
269
270 config_get macs "$cfg" "mac"
271 [ -n "$macs" ] || return 0
272 config_get name "$cfg" "name"
273 [ -n "$name" ] || return 0
274 config_get ip "$cfg" "ip"
275 [ -n "$ip" ] || return 0
276
277 config_get_bool broadcast "$cfg" "broadcast" 0
278 config_get dns "$cfg" "dns"
279 config_get gateway "$cfg" "gateway"
280 config_get leasetime "$cfg" "leasetime"
281 if [ -n "$leasetime" ] ; then
282 leasetime="$(time2seconds "$leasetime")"
283 [ "$?" -ne 0 ] && return 1
284 fi
285
286 config_get hostid "$cfg" "hostid"
287 if [ -n "$hostid" ] ; then
288 hex_to_hostid hostid "$hostid" || return 1
289 fi
290
291 config_get force_send "$cfg" "force_send"
292 extra_options=
293 local _IFS="$IFS" IFS=','
294 for option in $force_send; do
295 case "$option" in
296 hostname)
297 extra_options="$extra_options${extra_options:+ }0c" ;;
298 domain-name)
299 extra_options="$extra_options${extra_options:+ }0f" ;;
300 fqdn)
301 extra_options="$extra_options${extra_options:+ }51" ;;
302 routes)
303 extra_options="$extra_options${extra_options:+ }79" ;;
304 *)
305 echo "unknown option: $option" >&2 ;;
306 esac
307 done
308 IFS="$_IFS"
309
310 macn=0
311 for mac in $macs; do
312 macn=$(( macn + 1 ))
313 done
314
315 for mac in $macs; do
316 local secname="$name"
317 if [ $macn -gt 1 ] ; then
318 secname="${name}-${mac//:}"
319 fi
320 echo "host $secname {"
321 echo " hardware ethernet $mac;"
322 echo " fixed-address $ip;"
323 echo " option host-name \"$name\";"
324 if [ "$broadcast" -eq 1 ] ; then
325 echo " always-broadcast true;"
326 fi
327 if [ -n "$leasetime" ] ; then
328 echo " default-lease-time $leasetime;"
329 echo " max-lease-time $leasetime;"
330 fi
331 if [ -n "$hostid" ] ; then
332 echo " option dhcp-client-identifier $hostid;"
333 fi
334 if [ -n "$dns" ] ; then
335 echo " option domain-name-servers $dns;"
336 fi
337 if [ -n "$gateway" ] ; then
338 echo " option routers $gateway;"
339 fi
340
341 local routes=
342 config_list_foreach "$cfg" "routes" append_routes
343 [ -n "$routes" ] && echo " option classless-ipv4-route $routes;"
344
345 config_list_foreach "$cfg" "dhcp_option" append_dhcp_options
346 if [ -n "$extra_options" ]; then
347 echo -e " if exists dhcp-parameter-request-list {\n option dhcp-parameter-request-list = concat(option dhcp-parameter-request-list, $extra_options);\n }"
348 fi
349 echo "}"
350 done
351
352 ips="$ip"
353 for ip in $ips; do
354 revip="$(rev_str "$ip" ".")"
355
356 update "$name.$domain." IN A "$ip"
357 update "$revip.in-addr.arpa." IN PTR "$name.$domain."
358 done
359 }
360
361 static_hosts() {
362 config_foreach static_host_add host "$@"
363 }
364
365 gen_dhcp_subnet() {
366 local cfg="$1"
367
368 echo "subnet $NETWORK netmask $NETMASK {"
369 if [ -n "$START" ] && [ -n "$END" ]; then
370 echo " range $START $END;"
371 fi
372 echo " option subnet-mask $netmask;"
373 if [ "$BROADCAST" != "0.0.0.0" ] ; then
374 echo " option broadcast-address $BROADCAST;"
375 fi
376 if [ "$dynamicdhcp" -eq 0 ] ; then
377 if [ "$authoritative" -eq 1 ] ; then
378 echo " deny unknown-clients;"
379 else
380 echo " ignore unknown-clients;"
381 fi
382 fi
383 if [ -n "$leasetime" ] ; then
384 echo " default-lease-time $leasetime;"
385 echo " max-lease-time $leasetime;"
386 fi
387 if [ "$defaultroute" -eq 1 ] ; then
388 echo " option routers $gateway;"
389 fi
390 echo " option domain-name-servers $DNS;"
391
392 local routes=
393 config_list_foreach "$cfg" "routes" append_routes
394 [ -n "$routes" ] && echo " option classless-ipv4-route $routes;"
395
396 config_list_foreach "$cfg" "dhcp_option" append_dhcp_options
397 echo "}"
398 }
399
400 dhcpd_add() {
401 local cfg="$1" synthesize="$2"
402 local dhcp6range="::"
403 local dynamicdhcp defaultroute end gateway ifname ignore leasetime limit net netmask
404 local proto networkid start subnet
405 local IP NETMASK BROADCAST NETWORK PREFIX DNS START END
406
407 config_get_bool ignore "$cfg" "ignore" 0
408 [ "$ignore" = "0" ] || return 0
409
410 config_get net "$cfg" "interface"
411 [ -n "$net" ] || return 0
412
413 config_get start "$cfg" "start"
414 config_get limit "$cfg" "limit"
415
416 case "$start:$limit" in
417 ":*"|"*:")
418 echo "dhcpd: start/limit must be used together in $cfg" >&2
419 return 0
420 esac
421
422 network_get_subnet subnet "$net" || return 0
423 network_get_device ifname "$net" || return 0
424 network_get_protocol proto "$net" || return 0
425
426 [ static = "$proto" ] || return 0
427
428 local octets="$(rfc1918_prefix "$subnet")"
429
430 [ -n "$octets" ] && rfc1918_nets="$rfc1918_nets${rfc1918_nets:+ }$octets"
431
432 [ $synthesize -eq 0 ] && return
433
434 config_get_bool dynamicdhcp "$cfg" "dynamicdhcp" 1
435
436 config_get_bool defaultroute "$cfg" "default_route" 1
437
438 dhcp_ifs="$dhcp_ifs $ifname"
439
440 eval "$(ipcalc.sh $subnet $start $limit)"
441
442 config_get netmask "$cfg" "netmask" "$NETMASK"
443 config_get leasetime "$cfg" "leasetime"
444 if [ -n "$leasetime" ] ; then
445 leasetime="$(time2seconds "$leasetime")"
446 [ "$?" -ne 0 ] && return 1
447 fi
448
449 if network_get_dnsserver dnsserver "$net" ; then
450 for dnsserv in $dnsserver ; do
451 DNS="$DNS${DNS:+, }$dnsserv"
452 done
453 else
454 DNS="$IP"
455 fi
456
457 if ! network_get_gateway gateway "$net" ; then
458 gateway="$IP"
459 fi
460
461 gen_dhcp_subnet "$cfg"
462 }
463
464 general_config() {
465 local always_broadcast boot_unknown_clients log_facility
466 local default_lease_time max_lease_time
467
468 config_get_bool always_broadcast "isc_dhcpd" "always_broadcast" 0
469 config_get_bool authoritative "isc_dhcpd" "authoritative" 1
470 config_get_bool boot_unknown_clients "isc_dhcpd" "boot_unknown_clients" 1
471 config_get default_lease_time "isc_dhcpd" "default_lease_time" 3600
472 config_get max_lease_time "isc_dhcpd" "max_lease_time" 86400
473 config_get log_facility "isc_dhcpd" "log_facility"
474
475 config_get domain "isc_dhcpd" "domain"
476 config_get_bool dynamicdns "isc_dhcpd" dynamicdns 0
477
478 [ $always_broadcast -eq 1 ] && echo "always-broadcast true;"
479 [ $authoritative -eq 1 ] && echo "authoritative;"
480 [ $boot_unknown_clients -eq 0 ] && echo "boot-unknown-clients false;"
481
482 default_lease_time="$(time2seconds "$default_lease_time")"
483 [ "$?" -ne 0 ] && return 1
484 max_lease_time="$(time2seconds "$max_lease_time")"
485 [ "$?" -ne 0 ] && return 1
486
487 if [ $dynamicdns -eq 1 ]; then
488 create_empty_zone "$domain"
489
490 local mynet
491
492 for mynet in $rfc1918_nets; do
493 mynet="$(rev_str "$mynet" ".")"
494 create_empty_zone "$mynet.in-addr.arpa"
495 done
496
497 local need_reload=
498
499 cp -p $conf_local_file ${conf_local_file}_
500
501 cat <<EOF > $conf_local_file
502 zone "$domain" {
503 type master;
504 file "$dyndir/db.$domain";
505 update-policy {
506 grant $session_key_name zonesub any;
507 };
508 };
509
510 EOF
511
512 for mynet in $rfc1918_nets; do
513 mynet="$(rev_str "$mynet" ".")"
514 cat <<EOF >> $conf_local_file
515 zone "$mynet.in-addr.arpa" {
516 type master;
517 file "$dyndir/db.$mynet.in-addr.arpa";
518 update-policy {
519 grant $session_key_name zonesub any;
520 };
521 };
522
523 EOF
524 done
525
526 cmp -s $conf_local_file ${conf_local_file}_ || need_reload=1
527 rm -f ${conf_local_file}_
528
529 [ -n "$need_reload" ] && /etc/init.d/named reload
530 sleep 1
531
532 cat <<EOF
533 ddns-domainname "$domain.";
534 ddns-update-style standard;
535 ddns-updates on;
536 ignore client-updates;
537
538 update-static-leases on;
539 use-host-decl-names on;
540 update-conflict-detection off;
541 update-optimization off;
542
543 include "$session_key_file";
544
545 zone $domain. {
546 primary 127.0.0.1;
547 key $session_key_name;
548 }
549
550 EOF
551
552 for mynet in $rfc1918_nets; do
553 mynet="$(rev_str "$mynet" ".")"
554 cat <<EOF
555 zone $mynet.in-addr.arpa. {
556 primary 127.0.0.1;
557 key $session_key_name;
558 }
559
560 EOF
561 done
562 fi
563
564 if [ -n "$log_facility" ] ; then
565 echo "log-facility $log_facility;"
566 fi
567 echo "default-lease-time $default_lease_time;"
568 echo "max-lease-time $max_lease_time;"
569
570 [ -n "$domain" ] && echo "option domain-name \"$domain\";"
571
572 echo -e "\n# additional codes\noption classless-ipv4-route code 121 = array of { unsigned integer 8 };\n"
573
574 rm -f /tmp/resolv.conf
575 echo "# This file is generated by the DHCPD service" > /tmp/resolv.conf
576 [ -n "$domain" ] && echo "domain $domain" >> /tmp/resolv.conf
577 echo "nameserver 127.0.0.1" >> /tmp/resolv.conf
578 }
579
580 # base procd hooks
581
582 boot() {
583 DHCPD_BOOT=1
584 start "$@"
585 }
586
587 start_service() {
588 local domain dhcp_ifs authoritative dynamicdns
589
590 if [ -n "$DHCPD_BOOT" ] ; then
591 return 0
592 fi
593
594 if [ ! -e $lease_file ] ; then
595 touch $lease_file
596 fi
597
598 if [ -e "/etc/dhcpd.conf" ] ; then
599 config_file="/etc/dhcpd.conf"
600 else
601 . /lib/functions/network.sh
602
603 local dyn_file=$(mktemp -u /tmp/dhcpd.XXXXXX)
604
605 config_load dhcp
606
607 local rfc1918_nets=""
608
609 # alas we have to make 2 passes...
610 config_foreach dhcpd_add dhcp 0
611
612 rfc1918_nets="$(echo "$rfc1918_nets" | tr ' ' $'\n' | sort | uniq | tr $'\n' ' ')"
613
614 general_config > $config_file
615
616 if [ $dynamicdns -eq 1 ]; then
617 cat <<EOF > $dyn_file
618 ; Generated by /etc/init.d/dhcpd at $(date)
619
620 ttl $TTL
621
622 EOF
623 fi
624
625 rfc1918_nets=
626
627 config_foreach dhcpd_add dhcp 1 >> $config_file
628
629 static_hosts >> $config_file
630
631 static_cnames >> $config_file
632
633 static_domains >> $config_file
634
635 static_mxhosts >> $config_file
636
637 static_srvhosts >> $config_file
638
639 if [ $dynamicdns -eq 1 ]; then
640 local args=
641
642 no_ipv6 && args="-4"
643
644 nsupdate -l -v $args $dyn_file
645
646 fi
647
648 rm -f $dyn_file
649
650 [ -z "$dhcp_ifs" ] && return 0
651 fi
652
653 procd_open_instance
654 procd_set_param command $PROG -q -f -cf $config_file -lf $lease_file $dhcp_ifs
655 procd_close_instance
656 }
657
658 reload_service() {
659 rc_procd start_service "$@"
660 procd_send_signal dhcpd "$@"
661 }
662
663 add_interface_trigger() {
664 local cfg=$1
665 local trigger ignore
666
667 config_get trigger "$cfg" interface
668 config_get_bool ignore "$cfg" ignore 0
669
670 if [ -n "$trigger" -a $ignore -eq 0 ] ; then
671 procd_add_reload_interface_trigger "$trigger"
672 fi
673 }
674
675 service_triggers() {
676 if [ -n "$DHCPD_BOOT" ] ; then
677 # Make the first start robust to slow interfaces; wait a while
678 procd_add_raw_trigger "interface.*.up" 5000 /etc/init.d/dhcpd restart
679
680 else
681 # reload with normal parameters
682 procd_add_reload_trigger "network" "dhcp"
683 config_load dhcp
684 config_foreach add_interface_trigger dhcp
685 fi
686 }
687