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