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