]> git.ipfire.org Git - people/stevee/network.git/blob - functions.dhcpd
hostapd: Enable WMM by default.
[people/stevee/network.git] / 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_defaults() {
336 local proto=${1}
337 assert isset proto
338
339 local settings=$(dhcpd_settings ${proto})
340 assert isset settings
341
342 local prefix="DHCPV${proto/ipv/}D_"
343
344 local setting setting_default
345 for setting in ${settings}; do
346 setting_default="${prefix}${setting}"
347 printf -v ${setting} "%s" "${!setting_default}"
348 done
349 }
350
351 function dhcpd_global_settings_read() {
352 local proto=${1}
353 assert isset proto
354
355 local file=$(dhcpd_settings_file ${proto})
356 assert isset file
357
358 local settings=$(dhcpd_settings ${proto})
359 assert isset settings
360
361 dhcpd_global_settings_defaults ${proto}
362 config_read ${file} ${settings}
363 }
364
365 function dhcpd_global_settings_write() {
366 local proto=${1}
367 assert isset proto
368
369 local file=$(dhcpd_settings_file ${proto})
370 assert isset file
371
372 local settings=$(dhcpd_settings ${proto})
373 assert isset settings
374
375 config_write ${file} ${settings}
376 }
377
378 function dhcpd_global_options_read() {
379 local proto=${1}
380 assert isset proto
381
382 local options_file=$(dhcpd_options_file ${proto})
383 local options_list=$(dhcpd_options_list ${proto})
384
385 config_read_array ${options_file} options ${!options_list}
386
387 # Check if domain-name is set.
388 if [ -z "${options["domain-name"]}" ]; then
389 options["domain-name"]=$(config_domainname)
390 fi
391 }
392
393 function dhcpd_subnet_path() {
394 local proto=${1}
395 assert isset proto
396
397 local subnet_id=${2}
398 assert isset subnet_id
399
400 local path
401 case "${proto}" in
402 ipv6)
403 path=${DHCPV6D_CONFIG_DIR}
404 ;;
405 ipv4)
406 path=${DHCPV4D_CONFIG_DIR}
407 ;;
408 esac
409 assert isset path
410
411 print "${path}/${DHCPD_SUBNET_PREFIX}${subnet_id}"
412 return ${EXIT_OK}
413 }
414
415 function dhcpd_subnet_exists() {
416 local proto=${1}
417 assert isset proto
418
419 local subnet_id=${2}
420 assert isset subnet_id
421
422 local path=$(dhcpd_subnet_path ${proto} ${subnet_id})
423 assert isset path
424
425 [ -d "${path}" ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
426 }
427
428 function dhcpd_subnet_match() {
429 local proto=${1}
430 assert isset proto
431
432 local subnet=${2}
433 assert isset subnet
434
435 local settings=$(dhcpd_subnet_settings ${proto})
436 assert isset settings
437
438 local subnet_id ${settings}
439 for subnet_id in $(dhcpd_subnet_list ${proto}); do
440 dhcpd_subnet_read ${proto} ${subnet_id}
441
442 ${proto}_addr_eq "${ADDRESS}/${PREFIX}" "${subnet}" \
443 && return ${EXIT_TRUE}
444 done
445
446 return ${EXIT_FALSE}
447 }
448
449 function dhcpd_new_subnet_id() {
450 local proto=${1}
451 assert isset proto
452
453 local id=1
454 while :; do
455 if ! dhcpd_subnet_exists ${proto} ${id}; then
456 print "${id}"
457 return ${EXIT_OK}
458 fi
459
460 id=$(( ${id} + 1 ))
461 done
462
463 return ${EXIT_ERROR}
464 }
465
466 function dhcpd_subnet_new() {
467 local proto=${1}
468 assert isset proto
469 shift
470
471 # Allocate a new subnet id.
472 local subnet_id=$(dhcpd_new_subnet_id ${proto})
473 assert isinteger subnet_id
474
475 # Create directory structure.
476 local path=$(dhcpd_subnet_path ${proto} ${subnet_id})
477 assert isset path
478
479 mkdir -p ${path}
480 touch ${path}/settings
481
482 dhcpd_subnet_edit ${proto} ${subnet_id} $@
483 local ret=$?
484
485 # Remove the new subnet, when the edit method returned
486 # an error.
487 if [ ${ret} -ne ${EXIT_OK} ]; then
488 dhcpd_subnet_remove ${proto} ${subnet_id}
489 fi
490 }
491
492 function dhcpd_subnet_edit() {
493 local proto=${1}
494 assert isset proto
495 shift
496
497 local id=${1}
498 assert isset id
499 shift
500
501 local settings
502 case "${proto}" in
503 ipv6)
504 settings=${DHCPV6D_SUBNET_SETTINGS}
505 ;;
506 ipv4)
507 settings=${DHCPV4D_SUBNET_SETTINGS}
508 ;;
509 esac
510 assert isset settings
511 local ${settings}
512
513 # Read current settings.
514 dhcpd_subnet_read ${proto} ${id} || :
515
516 while [ $# -gt 0 ]; do
517 case "${1}" in
518 --address=*)
519 ADDRESS=$(cli_get_val ${1})
520
521 local prefix=$(ip_get_prefix ${ADDRESS})
522 if isset prefix; then
523 PREFIX=${prefix}
524 ADDRESS=$(ip_split_prefix ${ADDRESS})
525 fi
526 ;;
527 --prefix=*)
528 PREFIX=$(cli_get_val ${1})
529 ;;
530 --routers=*)
531 ROUTERS=$(cli_get_val ${1})
532 ;;
533 *)
534 error "Unknown argument: ${1}"
535 return ${EXIT_ERROR}
536 ;;
537 esac
538 shift
539 done
540
541 case "${proto}" in
542 ipv6)
543 if ! ipv6_is_valid ${ADDRESS}; then
544 error "'${ADDRESS}' is not a valid IPv6 address."
545 return ${EXIT_ERROR}
546 fi
547
548 if ! ipv6_prefix_is_valid ${PREFIX}; then
549 error "'${PREFIX}' is not a valid IPv6 prefix."
550 return ${EXIT_ERROR}
551 fi
552 ;;
553 ipv4)
554 if ! ipv4_is_valid ${ADDRESS}; then
555 error "'${ADDRESS}' is not a valid IPv4 address."
556 return ${EXIT_ERROR}
557 fi
558
559 if ! ipv4_prefix_is_valid ${PREFIX}; then
560 error "'${PREFIX}' is not a valid IPv4 prefix."
561 return ${EXIT_ERROR}
562 fi
563 ;;
564 esac
565
566 # XXX Check for subnet collisions!
567
568 local file="$(dhcpd_subnet_path ${proto} ${id})/settings"
569 config_write ${file} ${settings}
570 }
571
572 function dhcpd_subnet_remove() {
573 local proto=${1}
574 assert isset proto
575
576 local id=${2}
577 assert isset id
578
579 local path=$(dhcpd_subnet_path ${proto} ${id})
580 assert isset path
581
582 # Remove everything of this subnet.
583 rm -rf ${path}
584 }
585
586 function dhcpd_subnet_list() {
587 local proto=${1}
588 assert isset proto
589
590 local path=$(dhcpd_subnet_path ${proto} 0)
591 path=$(dirname ${path})
592
593 # Return an error of the directory does not exist.
594 [ -d "${path}" ] || return ${EXIT_ERROR}
595
596 local p
597 for p in ${path}/${DHCPD_SUBNET_PREFIX}*; do
598 [ -d "${p}" ] || continue
599
600 p=$(basename ${p})
601 print "${p:${#DHCPD_SUBNET_PREFIX}}"
602 done
603 }
604
605 function dhcpd_subnet_read() {
606 local proto=${1}
607 assert isset proto
608
609 local id=${2}
610 assert isset id
611
612 local file="$(dhcpd_subnet_path ${proto} ${id})/settings"
613 config_read ${file}
614 }
615
616 function dhcpd_subnet_range_path() {
617 local proto=${1}
618 assert isset proto
619
620 local subnet_id=${2}
621 assert isinteger subnet_id
622
623 local range_id=${3}
624 assert isinteger range_id
625
626 print "$(dhcpd_subnet_path ${proto} ${subnet_id})/${DHCPD_SUBNET_RANGE_PREFIX}${range_id}"
627 return ${EXIT_OK}
628 }
629
630 function dhcpd_subnet_range_settings() {
631 local proto=${1}
632
633 case "${proto}" in
634 ipv6)
635 print "${DHCPV6D_SUBNET_RANGE_SETTINGS}"
636 ;;
637 ipv4)
638 print "${DHCPV4D_SUBNET_RANGE_SETTINGS}"
639 ;;
640 esac
641
642 return ${EXIT_OK}
643 }
644
645 function dhcpd_subnet_new_range_id() {
646 local proto=${1}
647 assert isset proto
648
649 local subnet_id=${2}
650 assert isset subnet_id
651
652 local id=1 path
653 while :; do
654 path=$(dhcpd_subnet_range_path ${proto} ${subnet_id} ${id})
655 if [ ! -f "${path}" ]; then
656 print "${id}"
657 return ${EXIT_OK}
658 fi
659
660 id=$(( ${id} + 1 ))
661 done
662
663 return ${EXIT_ERROR}
664 }
665
666 function dhcpd_subnet_range_new() {
667 local proto=${1}
668 assert isset proto
669 shift
670
671 local subnet_id=${1}
672 assert isset subnet_id
673 shift
674
675 # Allocate a new range id.
676 local range_id=$(dhcpd_subnet_new_range_id ${proto} ${subnet_id})
677 assert isinteger range_id
678
679 local path=$(dhcpd_subnet_range_path ${proto} ${subnet_id} ${range_id})
680 assert isset path
681
682 # Create file (as a placeholder).
683 touch ${path}
684
685 dhcpd_subnet_range_edit ${proto} ${subnet_id} ${range_id} $@
686 local ret=$?
687
688 if [ ${ret} -ne ${EXIT_OK} ]; then
689 dhcpd_subnet_range_remove ${proto} ${subnet_id} ${range_id}
690 return ${EXIT_ERROR}
691 fi
692
693 return ${EXIT_OK}
694 }
695
696 function dhcpd_subnet_range_edit() {
697 local proto=${1}
698 assert isset proto
699 shift
700
701 local subnet_id=${1}
702 assert isset subnet_id
703 shift
704
705 local range_id=${1}
706 assert isset range_id
707 shift
708
709 local ip_encode ip_is_valid
710 local settings
711 case "${proto}" in
712 ipv6)
713 ip_encode="ipv6_encode"
714 ip_is_valid="ipv6_is_valid"
715 settings=${DHCPV6D_SUBNET_RANGE_SETTINGS}
716 ;;
717 ipv4)
718 ip_encode="ipv4_encode"
719 ip_is_valid="ipv4_is_valid"
720 settings=${DHCPV4D_SUBNET_RANGE_SETTINGS}
721 ;;
722 esac
723 assert isset settings
724 local ${settings}
725
726 while [ $# -gt 0 ]; do
727 case "${1}" in
728 --start=*)
729 START=$(cli_get_val ${1})
730 ;;
731 --end=*)
732 END=$(cli_get_val ${1})
733 ;;
734 *)
735 error "Unknown argument: ${1}"
736 return ${EXIT_ERROR}
737 ;;
738 esac
739 shift
740 done
741
742 if ! isset START; then
743 error "You need to set the start of the IP range with --start=..."
744 return ${EXIT_ERROR}
745 fi
746
747 if ! isset END; then
748 error "You need to set the end of the IP range with --end=..."
749 return ${EXIT_ERROR}
750 fi
751
752 local var
753 for var in START END; do
754 if ! ${ip_is_valid} ${!var}; then
755 error "'${!var}' is not a valid IP address."
756 return ${EXIT_ERROR}
757 fi
758 done
759
760 # XXX currently, this check can only be performed for IPv4
761 if [ "${proto}" = "ipv4" ]; then
762 # Check if the end address is greater than the start address.
763 local start_encoded=$(${ip_encode} ${START})
764 local end_encoded=$(${ip_encode} ${END})
765
766 if [ ${start_encoded} -ge ${end_encoded} ]; then
767 error "The start address of the range must be greater than the end address."
768 return ${EXIT_ERROR}
769 fi
770 fi
771
772 # Write the configuration to file.
773 local file=$(dhcpd_subnet_range_path ${proto} ${subnet_id} ${range_id})
774 assert isset file
775
776 config_write ${file} ${settings}
777 }
778
779 function dhcpd_subnet_range_remove() {
780 local path=$(dhcpd_subnet_range_path $@)
781 assert isset path
782
783 rm -f ${path}
784 }
785
786 function dhcpd_subnet_range_list() {
787 local proto=${1}
788 assert isset proto
789
790 local subnet_id=${2}
791 assert isset subnet_id
792
793 local path=$(dhcpd_subnet_range_path ${proto} ${subnet_id} 0)
794 path=$(dirname ${path})
795
796 local p
797 for p in ${path}/${DHCPD_SUBNET_RANGE_PREFIX}*; do
798 [ -r "${p}" ] || continue
799
800 p=$(basename ${p})
801 print "${p:${#DHCPD_SUBNET_RANGE_PREFIX}}"
802 done
803
804 return ${EXIT_OK}
805 }
806
807 function dhcpd_subnet_range_read() {
808 local proto=${1}
809 assert isset proto
810
811 local subnet_id=${2}
812 assert isset subnet_id
813
814 local range_id=${3}
815 assert isset range_id
816
817 local file=$(dhcpd_subnet_range_path ${proto} ${subnet_id} ${range_id})
818 config_read ${file}
819 }
820
821 function dhcpd_subnet_settings() {
822 local proto=${1}
823
824 case "${proto}" in
825 ipv6)
826 print "${DHCPV6D_SUBNET_SETTINGS}"
827 ;;
828 ipv4)
829 print "${DHCPV4D_SUBNET_SETTINGS}"
830 ;;
831 esac
832
833 return ${EXIT_OK}
834 }
835
836 function dhcpd_subnet_options_file() {
837 local path=$(dhcpd_subnet_path $@)
838 assert isset path
839
840 print "${path}/options"
841 }
842
843 function dhcpd_subnet_options_list() {
844 local proto=${1}
845
846 case "${proto}" in
847 ipv6)
848 print "DHCPV6D_SUBNET_OPTIONS"
849 ;;
850 ipv4)
851 print "DHCPV4D_SUBNET_OPTIONS"
852 ;;
853 esac
854
855 return ${EXIT_OK}
856 }
857
858 function dhcpd_subnet_options() {
859 local proto=${1}
860
861 case "${proto}" in
862 ipv6)
863 print "${DHCPV6D_SUBNET_OPTIONS}"
864 ;;
865 ipv4)
866 print "${DHCPV4D_SUBNET_OPTIONS}"
867 ;;
868 esac
869
870 return ${EXIT_OK}
871 }
872
873 function dhcpd_subnet_options_read() {
874 local proto=${1}
875 assert isset proto
876
877 local subnet_id=${2}
878 assert isset subnet_id
879
880 local options_file=$(dhcpd_subnet_options_file ${proto} ${subnet_id})
881 local options_list=$(dhcpd_subnet_options_list ${proto})
882
883 _dhcpd_read_options ${options_file} ${options_list}
884 }
885
886 # Helper functions to create a DHCP configuration file.
887 function _dhcpd_write_options() {
888 local proto=${1}
889 assert isset proto
890
891 local file=${2}
892 assert isset file
893
894 local options_list=${3}
895 assert isset options_list
896
897 local ident=${4}
898
899 # Dump options array.
900 local key val fmt
901 for key in ${!options_list}; do
902 val=${options[${key}]}
903
904 # Prepend dhcp6 on IPv6 options.
905 if [ "${proto}" = "ipv6" ]; then
906 key="dhcp6.${key}"
907 fi
908
909 if isset val; then
910 if isinteger val; then
911 fmt="option %s %d;"
912 elif isipaddress val; then
913 fmt="option %s %s;"
914 else
915 fmt="option %s \"%s\";"
916 fi
917 print "${ident}${fmt}" "${key}" "${val}"
918 fi
919 done >> ${file}
920
921 # Append an empty line when options have been written.
922 if [ -n "${!options[@]}" ]; then
923 print >> ${file}
924 fi
925 }
926
927 function _dhcpd_read_options() {
928 local file=${1}
929 assert isset file
930
931 local options_list=${2}
932 assert isset options_list
933
934 config_read_array ${file} options ${!options_list}
935 }
936
937 function _dhcpd_write_subnet() {
938 local proto=${1}
939 assert isset proto
940
941 local subnet_id=${2}
942 assert isset subnet_id
943
944 local file=${3}
945 assert isset file
946
947 # Check which settings we do expect.
948 local settings
949 case "${proto}" in
950 ipv6)
951 settings=${DHCPV6D_SUBNET_SETTINGS}
952 ;;
953 ipv4)
954 settings=${DHCPV4D_SUBNET_SETTINGS}
955 ;;
956 esac
957 assert isset settings
958 local ${settings}
959
960 # Read configuration settings.
961 dhcpd_subnet_read ${proto} ${subnet_id}
962
963 print "# Subnet declaration for subnet id ${subnet_id}." >> ${file}
964 case "${proto}" in
965 ipv6)
966 print "subnet6 ${ADDRESS}/${PREFIX} {" >> ${file}
967 ;;
968 ipv4)
969 local netmask=$(ipv4_get_netmask ${ADDRESS}/${PREFIX})
970 print "subnet ${ADDRESS} netmask ${netmask} {" >> ${file}
971 ;;
972 esac
973
974 # Add options.
975 _dhcpd_write_subnet_options ${proto} ${subnet_id} ${file}
976
977 # Add the ranges.
978 local range_id
979 for range_id in $(dhcpd_subnet_range_list ${proto} ${subnet_id} ${range_id}); do
980 _dhcpd_write_subnet_range ${proto} ${subnet_id} ${range_id} ${file}
981 done
982
983 # End this subnet block.
984 print "}\n" >> ${file}
985
986 return ${EXIT_OK}
987 }
988
989 function _dhcpd_write_subnet_options() {
990 local proto=${1}
991 assert isset proto
992
993 local subnet_id=${2}
994 assert isset subnet_id
995
996 local file=${3}
997 assert isset file
998
999 local settings
1000 local options_file="$(dhcpd_subnet_path ${proto} ${subnet_id})/options"
1001 local options_list
1002 case "${proto}" in
1003 ipv6)
1004 settings=${DHCPV6D_SUBNET_SETTINGS}
1005 options_list="DHCPV6D_OPTIONS"
1006 ;;
1007 ipv4)
1008 settings=${DHCPV4D_SUBNET_SETTINGS}
1009 options_list="DHCPV4D_OPTIONS"
1010 ;;
1011 esac
1012 assert isset settings
1013 assert isset options_list
1014
1015 local ${settings}
1016 dhcpd_subnet_read ${proto} ${subnet_id}
1017
1018 local -A options
1019 _dhcpd_read_options ${options_file} ${options_list}
1020
1021 # Fill in router, if not already set.
1022 if [ -z "${options["routers"]}" ]; then
1023 options["routers"]=$(_dhcpd_search_routers ${proto} "${ADDRESS}/${PREFIX}")
1024 fi
1025
1026 _dhcpd_write_options ${proto} ${file} ${options_list} "\t"
1027 }
1028
1029 function _dhcpd_search_routers() {
1030 local proto=${1}
1031 assert isset proto
1032
1033 # Do nothing for IPv6 (yet?).
1034 [ "${proto}" = "ipv6" ] && return ${EXIT_OK}
1035
1036 local subnet=${2}
1037 assert isset subnet
1038
1039 local routers
1040
1041 local zone addr
1042 for zone in $(zones_get_all); do
1043 addr=$(routing_db_get ${zone} ${proto} local-ip-address)
1044 isset addr || continue
1045
1046 if ipv4_in_subnet ${addr} ${subnet}; then
1047 list_append routers $(ip_split_prefix ${addr})
1048 fi
1049 done
1050
1051 list_join routers ", "
1052 }
1053
1054 function _dhcpd_write_subnet_range() {
1055 local proto=${1}
1056 assert isset proto
1057
1058 local subnet_id=${2}
1059 assert isset subnet_id
1060
1061 local range_id=${3}
1062 assert isset range_id
1063
1064 local file=${4}
1065 assert isset file
1066
1067 local settings=$(dhcpd_subnet_range_settings ${proto})
1068 assert isset settings
1069
1070 # Read the configuration settings.
1071 local ${settings}
1072 dhcpd_subnet_range_read ${proto} ${subnet_id} ${range_id}
1073
1074 # Print the range line.
1075 print " # Range id ${range_id}." >> ${file}
1076
1077 case "${proto}" in
1078 ipv6)
1079 print " range6 ${START} ${END};" >> ${file}
1080 ;;
1081 ipv4)
1082 print " range ${START} ${END};" >> ${file}
1083 ;;
1084 esac
1085 print >> ${file}
1086
1087 return ${EXIT_OK}
1088 }
1089
1090 function dhcpd_write_config() {
1091 local proto=${1}
1092 assert isset proto
1093
1094 local file options_list
1095 case "${proto}" in
1096 ipv6)
1097 file=${DHCPV6D_CONFIG_FILE}
1098 options_list="DHCPV6D_OPTIONS"
1099 ;;
1100 ipv4)
1101 file=${DHCPV4D_CONFIG_FILE}
1102 options_list="DHCPV4D_OPTIONS"
1103 ;;
1104 esac
1105 assert isset file
1106 assert isset options_list
1107
1108 # Writing header.
1109 config_header "DHCP ${proto} daemon configuration file" > ${file}
1110
1111 # Authoritative.
1112 if enabled AUTHORITATIVE; then
1113 (
1114 print "# This is an authoritative DHCP server for this network."
1115 print "authoritative;\n"
1116 ) >> ${file}
1117 else
1118 (
1119 print "# This is NOT an authoritative DHCP server for this network."
1120 print "not authoritative;\n"
1121 ) >> ${file}
1122 fi
1123
1124 case "${proto}" in
1125 ipv6)
1126 # Lease times.
1127 if ininteger VALID_LIFETIME; then
1128 print "default-lease-time %d;" "${VALID_LIFETIME}" >> ${file}
1129 fi
1130
1131 if isinteger PREFERRED_LIFETIME; then
1132 print "preferred-lifetime %d;" "${PREFERRED_LIFETIME}" >> ${file}
1133 fi
1134 ;;
1135 ipv4)
1136 # Lease times.
1137 if isinteger DEFAULT_LEASE_TIME; then
1138 print "default-lease-time %d;" "${DEFAULT_LEASE_TIME}" >> ${file}
1139 fi
1140
1141 if isinteger MAX_LEASE_TIME; then
1142 print "max-lease-time %d;" "${MAX_LEASE_TIME}" >> ${file}
1143 fi
1144
1145 if isinteger MIN_LEASE_TIME; then
1146 print "min-lease-time %d;" "${MIN_LEASE_TIME}" >> ${file}
1147 fi
1148 ;;
1149 esac
1150
1151 # Write the options to file.
1152 local -A options
1153 dhcpd_global_options_read ${proto}
1154 _dhcpd_write_options ${proto} ${file} ${options_list}
1155
1156 # Add all subnet declarations.
1157 local subnet_id
1158 for subnet_id in $(dhcpd_subnet_list ${proto}); do
1159 _dhcpd_write_subnet ${proto} ${subnet_id} ${file}
1160 done
1161
1162 return ${EXIT_OK}
1163 }