]> git.ipfire.org Git - people/stevee/network.git/blob - src/functions/functions.dhcpd
bash-completion: Add some basic completion for DHCP servers
[people/stevee/network.git] / src / functions / functions.dhcpd
1 #!/bin/bash
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2012 IPFire Network Development Team #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 DHCPV6D_CONFIG_FILE="/etc/dhcp/dhcpd6.conf"
23 DHCPV4D_CONFIG_FILE="/etc/dhcp/dhcpd.conf"
24
25 DHCPV6D_CONFIG_DIR="${NETWORK_CONFIG_DIR}/dhcpd/ipv6"
26 DHCPV4D_CONFIG_DIR="${NETWORK_CONFIG_DIR}/dhcpd/ipv4"
27
28 DHCPV6D_OPTIONS_FILE="${DHCPV6D_CONFIG_DIR}/options"
29 DHCPV4D_OPTIONS_FILE="${DHCPV4D_CONFIG_DIR}/options"
30
31 DHCPV6D_SETTINGS_FILE="${DHCPV6D_CONFIG_DIR}/settings"
32 DHCPV4D_SETTINGS_FILE="${DHCPV4D_CONFIG_DIR}/settings"
33
34 DHCPD_SETTINGS="\
35 AUTHORITATIVE
36 "
37 DHCPV6D_SETTINGS="\
38 ${DHCPD_SETTINGS} \
39 PREFERRED_LIFETIME
40 VALID_LIFETIME
41 "
42 DHCPV4D_SETTINGS="\
43 ${DHCPD_SETTINGS} \
44 DEFAULT_LEASE_TIME \
45 MAX_LEASE_TIME \
46 MIN_LEASE_TIME \
47 "
48
49 DHCPD_SUBNET_PREFIX="subnet-"
50 #DHCPD_SUBNET_POOL_PREFIX="pool-"
51 DHCPD_SUBNET_RANGE_PREFIX="range-"
52
53 DHCPD_SUBNET_SETTINGS="ADDRESS PREFIX ROUTERS"
54 DHCPV6D_SUBNET_SETTINGS="${DHCPD_SUBNET_SETTINGS}"
55 DHCPV4D_SUBNET_SETTINGS="${DHCPD_SUBNET_SETTINGS}"
56
57 DHCPD_SUBNET_RANGE_SETTINGS="START END"
58 DHCPV6D_SUBNET_RANGE_SETTINGS="${DHCPD_SUBNET_RANGE_SETTINGS}"
59 DHCPV4D_SUBNET_RANGE_SETTINGS="${DHCPD_SUBNET_RANGE_SETTINGS}"
60
61 DHCPV6D_OPTIONS="\
62 domain-search \
63 name-servers \
64 "
65 DHCPV4D_OPTIONS="\
66 all-subnets-local \
67 arp-cache-timeout \
68 bootfile-name \
69 broadcast-address \
70 default-ip-ttl \
71 default-tcp-ttl \
72 dhcp-client-identifier \
73 dhcp-lease-time \
74 dhcp-max-message-size \
75 dhcp-rebinding-time \
76 dhcp-renewal-time \
77 domain-name \
78 domain-name-servers \
79 domain-search \
80 interface-mtu \
81 ntp-servers \
82 root-path \
83 routers \
84 tftp-server-name \
85 "
86
87 DHCPD_SUBNET_OPTIONS="${DHCPD_OPTIONS}"
88 DHCPV6D_SUBNET_OPTIONS="${DHCPD_SUBNET_OPTIONS}"
89 DHCPV4D_SUBNET_OPTIONS="${DHCPD_SUBNET_OPTIONS}"
90
91 # Defaults for DHCPv6.
92 DHCPV6D_PREFERRED_LIFETIME=""
93 DHCPV6D_VALID_LIFETIME="43200" # 12h
94
95 # Defaults for DHCPv4.
96 DHCPV4D_AUTHORITATIVE="true"
97 DHCPV4D_DEFAULT_LEASE_TIME="43200" # 12h
98 DHCPV4D_MAX_LEASE_TIME="86400" # 24h
99 DHCPV4D_MIN_LEASE_TIME=""
100
101 function dhcpd_service() {
102 case "${1}" in
103 ipv6)
104 print "dhcpd6.service"
105 ;;
106 ipv4)
107 print "dhcpd.service"
108 ;;
109 "")
110 print "dhcpd6.service dhcp.service"
111 ;;
112 esac
113
114 return ${EXIT_OK}
115 }
116
117 function dhcpd_start() {
118 local services=$(dhcpd_service $@)
119
120 local service
121 for service in ${services}; do
122 service_start ${service}
123 done
124 }
125
126 function dhcpd_stop() {
127 local services=$(dhcpd_service $@)
128
129 local service
130 for service in ${services}; do
131 service_stop ${service}
132 done
133 }
134
135 function dhcpd_restart() {
136 # DHCP does not support a reload, so
137 # we retsart it.
138 local services=$(dhcpd_service $@)
139
140 local service
141 for service in ${services}; do
142 service_restart ${service}
143 done
144 }
145
146 function dhcpd_reload() {
147 dhcpd_restart $@
148 }
149
150 function dhcpd_edit() {
151 local proto=${1}
152 assert isset proto
153 shift
154
155 local settings=$(dhcpd_settings ${proto})
156 assert isset settings
157
158 local ${settings}
159 dhcpd_global_settings_read ${proto}
160
161 case "${proto}" in
162 ipv6)
163 _dhcpd_edit_ipv6 $@ || return $?
164 ;;
165 ipv4)
166 _dhcpd_edit_ipv4 $@ || return $?
167 ;;
168 esac
169
170 dhcpd_global_settings_write ${proto}
171 }
172
173 function _dhcpd_edit_ipv4() {
174 local val
175
176 while [ $# -gt 0 ]; do
177 case "${1}" in
178 --authoritative=*)
179 val=$(cli_get_val ${1})
180
181 if enabled val; then
182 AUTHORITATIVE="true"
183 else
184 AUTHORITATIVE="false"
185 fi
186 ;;
187 --default-lease-time=*)
188 DEFAULT_LEASE_TIME=$(cli_get_val ${1})
189
190 if ! isinteger DEFAULT_LEASE_TIME; then
191 error "Invalid value for --default-lease-time."
192 return ${EXIT_ERROR}
193 fi
194 ;;
195 --max-lease-time=*)
196 MAX_LEASE_TIME=$(cli_get_val ${1})
197
198 if ! isinteger MAX_LEASE_TIME; then
199 error "Invalid value for --max-lease-time."
200 return ${EXIT_ERROR}
201 fi
202 ;;
203 --min-lease-time=*)
204 MIN_LEASE_TIME=$(cli_get_val ${1})
205
206 if isset MIN_LEASE_TIME; then
207 if ! isinteger MIN_LEASE_TIME; then
208 error "Invalid value for --min-lease-time."
209 return ${EXIT_ERROR}
210 fi
211 fi
212 ;;
213 *)
214 error "Unrecognized argument: ${1}"
215 return ${EXIT_ERROR}
216 ;;
217 esac
218 shift
219 done
220
221 if [ ${MAX_LEASE_TIME} -le ${DEFAULT_LEASE_TIME} ]; then
222 error "The max. lease time must be higher than the default lease time."
223 return ${EXIT_ERROR}
224 fi
225 }
226
227 function _dhcpd_edit_ipv6() {
228 while [ $# -gt 0 ]; do
229 case "${1}" in
230 --preferred-lifetime=*)
231 PREFERRED_LIFETIME=$(cli_get_val ${1})
232
233 if ! isinteger PREFERRED_LIFETIME; then
234 error "Invalid value for --preferred-lifetime."
235 return ${EXIT_ERROR}
236 fi
237 ;;
238 --valid-lifetime=*)
239 VALID_LIFETIME=$(cli_get_val ${1})
240
241 if ! isinteger VALID_LIFETIME; then
242 error "Invalid value for --valid-lifetime."
243 return ${EXIT_ERROR}
244 fi
245 ;;
246 *)
247 error "Unrecognized argument: ${1}"
248 return ${EXIT_ERROR}
249 ;;
250 esac
251 shift
252 done
253 }
254
255 function dhcpd_settings_file() {
256 local proto=${1}
257 assert isset proto
258
259 case "${proto}" in
260 ipv6)
261 print "${DHCPV6D_SETTINGS_FILE}"
262 ;;
263 ipv4)
264 print "${DHCPV4D_SETTINGS_FILE}"
265 ;;
266 esac
267
268 return ${EXIT_OK}
269 }
270
271 function dhcpd_settings() {
272 local proto=${1}
273 assert isset proto
274
275 case "${proto}" in
276 ipv6)
277 print "${DHCPV6D_SETTINGS}"
278 ;;
279 ipv4)
280 print "${DHCPV4D_SETTINGS}"
281 ;;
282 esac
283
284 return ${EXIT_OK}
285 }
286
287 function dhcpd_options_file() {
288 local proto=${1}
289 assert isset proto
290
291 case "${proto}" in
292 ipv6)
293 print "${DHCPV6D_OPTIONS_FILE}"
294 ;;
295 ipv4)
296 print "${DHCPV4D_OPTIONS_FILE}"
297 ;;
298 esac
299
300 return ${EXIT_OK}
301 }
302
303 function dhcpd_options_list() {
304 local proto=${1}
305 assert isset proto
306
307 case "${proto}" in
308 ipv6)
309 print "DHCPV6D_OPTIONS"
310 ;;
311 ipv4)
312 print "DHCPV4D_OPTIONS"
313 ;;
314 esac
315
316 return ${EXIT_OK}
317 }
318
319 function dhcpd_options() {
320 local proto=${1}
321 assert isset proto
322
323 case "${proto}" in
324 ipv6)
325 print "${DHCPV6D_OPTIONS}"
326 ;;
327 ipv4)
328 print "${DHCPV4D_OPTIONS}"
329 ;;
330 esac
331
332 return ${EXIT_OK}
333 }
334
335 function dhcpd_global_settings_list() {
336 local proto="${1}"
337 assert isset proto
338
339 dhcpd_settings "${proto}"
340 }
341
342 function dhcpd_global_settings_defaults() {
343 local proto=${1}
344 assert isset proto
345
346 local settings=$(dhcpd_settings ${proto})
347 assert isset settings
348
349 local prefix="DHCPV${proto/ipv/}D_"
350
351 local setting setting_default
352 for setting in ${settings}; do
353 setting_default="${prefix}${setting}"
354 printf -v ${setting} "%s" "${!setting_default}"
355 done
356 }
357
358 function dhcpd_global_settings_read() {
359 local proto=${1}
360 assert isset proto
361
362 local file=$(dhcpd_settings_file ${proto})
363 assert isset file
364
365 local settings=$(dhcpd_settings ${proto})
366 assert isset settings
367
368 dhcpd_global_settings_defaults ${proto}
369 settings_read ${file} ${settings}
370 }
371
372 function dhcpd_global_settings_write() {
373 local proto=${1}
374 assert isset proto
375
376 local file=$(dhcpd_settings_file ${proto})
377 assert isset file
378
379 local settings=$(dhcpd_settings ${proto})
380 assert isset settings
381
382 settings_write ${file} ${settings}
383 }
384
385 function dhcpd_global_options_read() {
386 local proto=${1}
387 assert isset proto
388
389 local options_file=$(dhcpd_options_file ${proto})
390 local options_list=$(dhcpd_options_list ${proto})
391
392 settings_read_array ${options_file} options ${!options_list}
393
394 # Check if domain-name is set.
395 if [ -z "${options["domain-name"]}" ]; then
396 options["domain-name"]=$(config_domainname)
397 fi
398 }
399
400 function dhcpd_subnet_path() {
401 local proto=${1}
402 assert isset proto
403
404 local subnet_id=${2}
405 assert isset subnet_id
406
407 local path
408 case "${proto}" in
409 ipv6)
410 path=${DHCPV6D_CONFIG_DIR}
411 ;;
412 ipv4)
413 path=${DHCPV4D_CONFIG_DIR}
414 ;;
415 esac
416 assert isset path
417
418 print "${path}/${DHCPD_SUBNET_PREFIX}${subnet_id}"
419 return ${EXIT_OK}
420 }
421
422 function dhcpd_subnet_exists() {
423 local proto=${1}
424 assert isset proto
425
426 local subnet_id=${2}
427 assert isset subnet_id
428
429 local path=$(dhcpd_subnet_path ${proto} ${subnet_id})
430 assert isset path
431
432 [ -d "${path}" ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
433 }
434
435 function dhcpd_subnet_match() {
436 local proto=${1}
437 assert isset proto
438
439 local subnet=${2}
440 assert isset subnet
441
442 local settings=$(dhcpd_subnet_settings ${proto})
443 assert isset settings
444
445 local subnet_id ${settings}
446 for subnet_id in $(dhcpd_subnet_list ${proto}); do
447 dhcpd_subnet_read ${proto} ${subnet_id}
448
449 ${proto}_addr_eq "${ADDRESS}/${PREFIX}" "${subnet}" \
450 && return ${EXIT_TRUE}
451 done
452
453 return ${EXIT_FALSE}
454 }
455
456 function dhcpd_new_subnet_id() {
457 local proto=${1}
458 assert isset proto
459
460 local id=1
461 while :; do
462 if ! dhcpd_subnet_exists ${proto} ${id}; then
463 print "${id}"
464 return ${EXIT_OK}
465 fi
466
467 id=$(( ${id} + 1 ))
468 done
469
470 return ${EXIT_ERROR}
471 }
472
473 function dhcpd_subnet_new() {
474 local proto=${1}
475 assert isset proto
476 shift
477
478 # Allocate a new subnet id.
479 local subnet_id=$(dhcpd_new_subnet_id ${proto})
480 assert isinteger subnet_id
481
482 # Create directory structure.
483 local path=$(dhcpd_subnet_path ${proto} ${subnet_id})
484 assert isset path
485
486 mkdir -p ${path}
487 touch ${path}/settings
488
489 dhcpd_subnet_edit ${proto} ${subnet_id} $@
490 local ret=$?
491
492 # Remove the new subnet, when the edit method returned
493 # an error.
494 if [ ${ret} -ne ${EXIT_OK} ]; then
495 dhcpd_subnet_remove ${proto} ${subnet_id}
496 fi
497 }
498
499 function dhcpd_subnet_edit() {
500 local proto=${1}
501 assert isset proto
502 shift
503
504 local id=${1}
505 assert isset id
506 shift
507
508 local settings
509 case "${proto}" in
510 ipv6)
511 settings=${DHCPV6D_SUBNET_SETTINGS}
512 ;;
513 ipv4)
514 settings=${DHCPV4D_SUBNET_SETTINGS}
515 ;;
516 esac
517 assert isset settings
518 local ${settings}
519
520 # Read current settings.
521 dhcpd_subnet_read ${proto} ${id} || :
522
523 while [ $# -gt 0 ]; do
524 case "${1}" in
525 --address=*)
526 ADDRESS=$(cli_get_val ${1})
527
528 local prefix=$(ip_get_prefix ${ADDRESS})
529 if isset prefix; then
530 PREFIX=${prefix}
531 ADDRESS=$(ip_split_prefix ${ADDRESS})
532 fi
533 ;;
534 --prefix=*)
535 PREFIX=$(cli_get_val ${1})
536 ;;
537 --routers=*)
538 ROUTERS=$(cli_get_val ${1})
539 ;;
540 *)
541 error "Unknown argument: ${1}"
542 return ${EXIT_ERROR}
543 ;;
544 esac
545 shift
546 done
547
548 case "${proto}" in
549 ipv6)
550 if ! ipv6_is_valid ${ADDRESS}; then
551 error "'${ADDRESS}' is not a valid IPv6 address."
552 return ${EXIT_ERROR}
553 fi
554
555 if ! ipv6_prefix_is_valid ${PREFIX}; then
556 error "'${PREFIX}' is not a valid IPv6 prefix."
557 return ${EXIT_ERROR}
558 fi
559 ;;
560 ipv4)
561 if ! ipv4_is_valid ${ADDRESS}; then
562 error "'${ADDRESS}' is not a valid IPv4 address."
563 return ${EXIT_ERROR}
564 fi
565
566 if ! ipv4_prefix_is_valid ${PREFIX}; then
567 error "'${PREFIX}' is not a valid IPv4 prefix."
568 return ${EXIT_ERROR}
569 fi
570 ;;
571 esac
572
573 # XXX Check for subnet collisions!
574
575 local file="$(dhcpd_subnet_path ${proto} ${id})/settings"
576 settings_write ${file} ${settings}
577 }
578
579 function dhcpd_subnet_remove() {
580 local proto=${1}
581 assert isset proto
582
583 local id=${2}
584 assert isset id
585
586 local path=$(dhcpd_subnet_path ${proto} ${id})
587 assert isset path
588
589 # Remove everything of this subnet.
590 rm -rf ${path}
591 }
592
593 function dhcpd_subnet_list() {
594 local proto=${1}
595 assert isset proto
596
597 local path=$(dhcpd_subnet_path ${proto} 0)
598 path=$(dirname ${path})
599
600 # Return an error of the directory does not exist.
601 [ -d "${path}" ] || return ${EXIT_ERROR}
602
603 local p
604 for p in ${path}/${DHCPD_SUBNET_PREFIX}*; do
605 [ -d "${p}" ] || continue
606
607 p=$(basename ${p})
608 print "${p:${#DHCPD_SUBNET_PREFIX}}"
609 done
610 }
611
612 function dhcpd_subnet_read() {
613 local proto=${1}
614 assert isset proto
615
616 local id=${2}
617 assert isset id
618
619 local file="$(dhcpd_subnet_path ${proto} ${id})/settings"
620 settings_read ${file}
621 }
622
623 function dhcpd_subnet_range_path() {
624 local proto=${1}
625 assert isset proto
626
627 local subnet_id=${2}
628 assert isinteger subnet_id
629
630 local range_id=${3}
631 assert isinteger range_id
632
633 print "$(dhcpd_subnet_path ${proto} ${subnet_id})/${DHCPD_SUBNET_RANGE_PREFIX}${range_id}"
634 return ${EXIT_OK}
635 }
636
637 function dhcpd_subnet_range_settings() {
638 local proto=${1}
639
640 case "${proto}" in
641 ipv6)
642 print "${DHCPV6D_SUBNET_RANGE_SETTINGS}"
643 ;;
644 ipv4)
645 print "${DHCPV4D_SUBNET_RANGE_SETTINGS}"
646 ;;
647 esac
648
649 return ${EXIT_OK}
650 }
651
652 function dhcpd_subnet_new_range_id() {
653 local proto=${1}
654 assert isset proto
655
656 local subnet_id=${2}
657 assert isset subnet_id
658
659 local id=1 path
660 while :; do
661 path=$(dhcpd_subnet_range_path ${proto} ${subnet_id} ${id})
662 if [ ! -f "${path}" ]; then
663 print "${id}"
664 return ${EXIT_OK}
665 fi
666
667 id=$(( ${id} + 1 ))
668 done
669
670 return ${EXIT_ERROR}
671 }
672
673 function dhcpd_subnet_range_new() {
674 local proto=${1}
675 assert isset proto
676 shift
677
678 local subnet_id=${1}
679 assert isset subnet_id
680 shift
681
682 # Allocate a new range id.
683 local range_id=$(dhcpd_subnet_new_range_id ${proto} ${subnet_id})
684 assert isinteger range_id
685
686 local path=$(dhcpd_subnet_range_path ${proto} ${subnet_id} ${range_id})
687 assert isset path
688
689 # Create file (as a placeholder).
690 touch ${path}
691
692 dhcpd_subnet_range_edit ${proto} ${subnet_id} ${range_id} $@
693 local ret=$?
694
695 if [ ${ret} -ne ${EXIT_OK} ]; then
696 dhcpd_subnet_range_remove ${proto} ${subnet_id} ${range_id}
697 return ${EXIT_ERROR}
698 fi
699
700 return ${EXIT_OK}
701 }
702
703 function dhcpd_subnet_range_edit() {
704 local proto=${1}
705 assert isset proto
706 shift
707
708 local subnet_id=${1}
709 assert isset subnet_id
710 shift
711
712 local range_id=${1}
713 assert isset range_id
714 shift
715
716 local ip_encode ip_is_valid
717 local settings
718 case "${proto}" in
719 ipv6)
720 ip_encode="ipv6_encode"
721 ip_is_valid="ipv6_is_valid"
722 settings=${DHCPV6D_SUBNET_RANGE_SETTINGS}
723 ;;
724 ipv4)
725 ip_encode="ipv4_encode"
726 ip_is_valid="ipv4_is_valid"
727 settings=${DHCPV4D_SUBNET_RANGE_SETTINGS}
728 ;;
729 esac
730 assert isset settings
731 local ${settings}
732
733 while [ $# -gt 0 ]; do
734 case "${1}" in
735 --start=*)
736 START=$(cli_get_val ${1})
737 ;;
738 --end=*)
739 END=$(cli_get_val ${1})
740 ;;
741 *)
742 error "Unknown argument: ${1}"
743 return ${EXIT_ERROR}
744 ;;
745 esac
746 shift
747 done
748
749 if ! isset START; then
750 error "You need to set the start of the IP range with --start=..."
751 return ${EXIT_ERROR}
752 fi
753
754 if ! isset END; then
755 error "You need to set the end of the IP range with --end=..."
756 return ${EXIT_ERROR}
757 fi
758
759 local var
760 for var in START END; do
761 if ! ${ip_is_valid} ${!var}; then
762 error "'${!var}' is not a valid IP address."
763 return ${EXIT_ERROR}
764 fi
765 done
766
767 # XXX currently, this check can only be performed for IPv4
768 if [ "${proto}" = "ipv4" ]; then
769 # Check if the end address is greater than the start address.
770 local start_encoded=$(${ip_encode} ${START})
771 local end_encoded=$(${ip_encode} ${END})
772
773 if [ ${start_encoded} -ge ${end_encoded} ]; then
774 error "The start address of the range must be greater than the end address."
775 return ${EXIT_ERROR}
776 fi
777 fi
778
779 # Write the configuration to file.
780 local file=$(dhcpd_subnet_range_path ${proto} ${subnet_id} ${range_id})
781 assert isset file
782
783 settings_write ${file} ${settings}
784 }
785
786 function dhcpd_subnet_range_remove() {
787 local path=$(dhcpd_subnet_range_path $@)
788 assert isset path
789
790 rm -f ${path}
791 }
792
793 function dhcpd_subnet_range_list() {
794 local proto=${1}
795 assert isset proto
796
797 local subnet_id=${2}
798 assert isset subnet_id
799
800 local path=$(dhcpd_subnet_range_path ${proto} ${subnet_id} 0)
801 path=$(dirname ${path})
802
803 local p
804 for p in ${path}/${DHCPD_SUBNET_RANGE_PREFIX}*; do
805 [ -r "${p}" ] || continue
806
807 p=$(basename ${p})
808 print "${p:${#DHCPD_SUBNET_RANGE_PREFIX}}"
809 done
810
811 return ${EXIT_OK}
812 }
813
814 function dhcpd_subnet_range_read() {
815 local proto=${1}
816 assert isset proto
817
818 local subnet_id=${2}
819 assert isset subnet_id
820
821 local range_id=${3}
822 assert isset range_id
823
824 local file=$(dhcpd_subnet_range_path ${proto} ${subnet_id} ${range_id})
825 settings_read ${file}
826 }
827
828 function dhcpd_subnet_settings() {
829 local proto=${1}
830
831 case "${proto}" in
832 ipv6)
833 print "${DHCPV6D_SUBNET_SETTINGS}"
834 ;;
835 ipv4)
836 print "${DHCPV4D_SUBNET_SETTINGS}"
837 ;;
838 esac
839
840 return ${EXIT_OK}
841 }
842
843 function dhcpd_subnet_options_file() {
844 local path=$(dhcpd_subnet_path $@)
845 assert isset path
846
847 print "${path}/options"
848 }
849
850 function dhcpd_subnet_options_list() {
851 local proto=${1}
852 assert isset proto
853
854 case "${proto}" in
855 ipv6)
856 print "${DHCPV6D_SUBNET_OPTIONS}"
857 ;;
858 ipv4)
859 print "${DHCPV4D_SUBNET_OPTIONS}"
860 ;;
861 esac
862
863 return ${EXIT_OK}
864 }
865
866 function dhcpd_subnet_options_read() {
867 local proto=${1}
868 assert isset proto
869
870 local subnet_id=${2}
871 assert isset subnet_id
872
873 local options_file=$(dhcpd_subnet_options_file ${proto} ${subnet_id})
874 local options_list=$(dhcpd_subnet_options_list ${proto})
875
876 _dhcpd_read_options ${options_file} ${options_list}
877 }
878
879 # Helper functions to create a DHCP configuration file.
880 function _dhcpd_write_options() {
881 local proto=${1}
882 assert isset proto
883
884 local file=${2}
885 assert isset file
886
887 local options_list=${3}
888 assert isset options_list
889
890 local ident=${4}
891
892 # Dump options array.
893 local key val fmt
894 for key in ${!options_list}; do
895 val=${options[${key}]}
896
897 # Prepend dhcp6 on IPv6 options.
898 if [ "${proto}" = "ipv6" ]; then
899 key="dhcp6.${key}"
900 fi
901
902 if isset val; then
903 if isinteger val; then
904 fmt="option %s %d;"
905 elif isipaddress val; then
906 fmt="option %s %s;"
907 else
908 fmt="option %s \"%s\";"
909 fi
910 print "${ident}${fmt}" "${key}" "${val}"
911 fi
912 done >> ${file}
913
914 # Append an empty line when options have been written.
915 if [ -n "${!options[@]}" ]; then
916 print >> ${file}
917 fi
918 }
919
920 function _dhcpd_read_options() {
921 local file=${1}
922 assert isset file
923
924 local options_list=${2}
925 assert isset options_list
926
927 settings_read_array ${file} options ${!options_list}
928 }
929
930 function _dhcpd_write_subnet() {
931 local proto=${1}
932 assert isset proto
933
934 local subnet_id=${2}
935 assert isset subnet_id
936
937 local file=${3}
938 assert isset file
939
940 # Check which settings we do expect.
941 local settings
942 case "${proto}" in
943 ipv6)
944 settings=${DHCPV6D_SUBNET_SETTINGS}
945 ;;
946 ipv4)
947 settings=${DHCPV4D_SUBNET_SETTINGS}
948 ;;
949 esac
950 assert isset settings
951 local ${settings}
952
953 # Read configuration settings.
954 dhcpd_subnet_read ${proto} ${subnet_id}
955
956 print "# Subnet declaration for subnet id ${subnet_id}." >> ${file}
957 case "${proto}" in
958 ipv6)
959 print "subnet6 ${ADDRESS}/${PREFIX} {" >> ${file}
960 ;;
961 ipv4)
962 local netmask=$(ipv4_get_netmask ${ADDRESS}/${PREFIX})
963 print "subnet ${ADDRESS} netmask ${netmask} {" >> ${file}
964 ;;
965 esac
966
967 # Add options.
968 _dhcpd_write_subnet_options ${proto} ${subnet_id} ${file}
969
970 # Add the ranges.
971 local range_id
972 for range_id in $(dhcpd_subnet_range_list ${proto} ${subnet_id} ${range_id}); do
973 _dhcpd_write_subnet_range ${proto} ${subnet_id} ${range_id} ${file}
974 done
975
976 # End this subnet block.
977 print "}\n" >> ${file}
978
979 return ${EXIT_OK}
980 }
981
982 function _dhcpd_write_subnet_options() {
983 local proto=${1}
984 assert isset proto
985
986 local subnet_id=${2}
987 assert isset subnet_id
988
989 local file=${3}
990 assert isset file
991
992 local settings
993 local options_file="$(dhcpd_subnet_path ${proto} ${subnet_id})/options"
994 local options_list
995 case "${proto}" in
996 ipv6)
997 settings=${DHCPV6D_SUBNET_SETTINGS}
998 options_list="DHCPV6D_OPTIONS"
999 ;;
1000 ipv4)
1001 settings=${DHCPV4D_SUBNET_SETTINGS}
1002 options_list="DHCPV4D_OPTIONS"
1003 ;;
1004 esac
1005 assert isset settings
1006 assert isset options_list
1007
1008 local ${settings}
1009 dhcpd_subnet_read ${proto} ${subnet_id}
1010
1011 local -A options
1012 _dhcpd_read_options ${options_file} ${options_list}
1013
1014 # Fill in router, if not already set.
1015 if [ -z "${options["routers"]}" ]; then
1016 options["routers"]=$(_dhcpd_search_routers ${proto} "${ADDRESS}/${PREFIX}")
1017 fi
1018
1019 _dhcpd_write_options ${proto} ${file} ${options_list} "\t"
1020 }
1021
1022 function _dhcpd_search_routers() {
1023 local proto=${1}
1024 assert isset proto
1025
1026 # Do nothing for IPv6 (yet?).
1027 [ "${proto}" = "ipv6" ] && return ${EXIT_OK}
1028
1029 local subnet=${2}
1030 assert isset subnet
1031
1032 local routers
1033
1034 local zone addr
1035 for zone in $(zones_get_all); do
1036 addr=$(routing_db_get ${zone} ${proto} local-ip-address)
1037 isset addr || continue
1038
1039 if ipv4_in_subnet ${addr} ${subnet}; then
1040 list_append routers $(ip_split_prefix ${addr})
1041 fi
1042 done
1043
1044 list_join routers ", "
1045 }
1046
1047 function _dhcpd_write_subnet_range() {
1048 local proto=${1}
1049 assert isset proto
1050
1051 local subnet_id=${2}
1052 assert isset subnet_id
1053
1054 local range_id=${3}
1055 assert isset range_id
1056
1057 local file=${4}
1058 assert isset file
1059
1060 local settings=$(dhcpd_subnet_range_settings ${proto})
1061 assert isset settings
1062
1063 # Read the configuration settings.
1064 local ${settings}
1065 dhcpd_subnet_range_read ${proto} ${subnet_id} ${range_id}
1066
1067 # Print the range line.
1068 print " # Range id ${range_id}." >> ${file}
1069
1070 case "${proto}" in
1071 ipv6)
1072 print " range6 ${START} ${END};" >> ${file}
1073 ;;
1074 ipv4)
1075 print " range ${START} ${END};" >> ${file}
1076 ;;
1077 esac
1078 print >> ${file}
1079
1080 return ${EXIT_OK}
1081 }
1082
1083 function dhcpd_write_config() {
1084 local proto=${1}
1085 assert isset proto
1086
1087 local file options_list
1088 case "${proto}" in
1089 ipv6)
1090 file=${DHCPV6D_CONFIG_FILE}
1091 options_list="DHCPV6D_OPTIONS"
1092 ;;
1093 ipv4)
1094 file=${DHCPV4D_CONFIG_FILE}
1095 options_list="DHCPV4D_OPTIONS"
1096 ;;
1097 esac
1098 assert isset file
1099 assert isset options_list
1100
1101 # Writing header.
1102 config_header "DHCP ${proto} daemon configuration file" > ${file}
1103
1104 # Authoritative.
1105 if enabled AUTHORITATIVE; then
1106 (
1107 print "# This is an authoritative DHCP server for this network."
1108 print "authoritative;\n"
1109 ) >> ${file}
1110 else
1111 (
1112 print "# This is NOT an authoritative DHCP server for this network."
1113 print "not authoritative;\n"
1114 ) >> ${file}
1115 fi
1116
1117 case "${proto}" in
1118 ipv6)
1119 # Lease times.
1120 if ininteger VALID_LIFETIME; then
1121 print "default-lease-time %d;" "${VALID_LIFETIME}" >> ${file}
1122 fi
1123
1124 if isinteger PREFERRED_LIFETIME; then
1125 print "preferred-lifetime %d;" "${PREFERRED_LIFETIME}" >> ${file}
1126 fi
1127 ;;
1128 ipv4)
1129 # Lease times.
1130 if isinteger DEFAULT_LEASE_TIME; then
1131 print "default-lease-time %d;" "${DEFAULT_LEASE_TIME}" >> ${file}
1132 fi
1133
1134 if isinteger MAX_LEASE_TIME; then
1135 print "max-lease-time %d;" "${MAX_LEASE_TIME}" >> ${file}
1136 fi
1137
1138 if isinteger MIN_LEASE_TIME; then
1139 print "min-lease-time %d;" "${MIN_LEASE_TIME}" >> ${file}
1140 fi
1141 ;;
1142 esac
1143
1144 # Write the options to file.
1145 local -A options
1146 dhcpd_global_options_read ${proto}
1147 _dhcpd_write_options ${proto} ${file} ${options_list}
1148
1149 # Add all subnet declarations.
1150 local subnet_id
1151 for subnet_id in $(dhcpd_subnet_list ${proto}); do
1152 _dhcpd_write_subnet ${proto} ${subnet_id} ${file}
1153 done
1154
1155 return ${EXIT_OK}
1156 }