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