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