]>
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 | ||
cf0d2484 MT |
87 | DHCPV6D_SUBNET_OPTIONS="${DHCPV6D_OPTIONS}" |
88 | DHCPV4D_SUBNET_OPTIONS="${DHCPV4D_OPTIONS}" | |
6c07160e MT |
89 | |
90 | # Defaults for DHCPv6. | |
91 | DHCPV6D_PREFERRED_LIFETIME="" | |
92 | DHCPV6D_VALID_LIFETIME="43200" # 12h | |
93 | ||
94 | # Defaults for DHCPv4. | |
95 | DHCPV4D_AUTHORITATIVE="true" | |
96 | DHCPV4D_DEFAULT_LEASE_TIME="43200" # 12h | |
97 | DHCPV4D_MAX_LEASE_TIME="86400" # 24h | |
98 | DHCPV4D_MIN_LEASE_TIME="" | |
99 | ||
1c6a4e30 | 100 | dhcpd_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 | 116 | dhcpd_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 | 125 | dhcpd_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 | 134 | dhcpd_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 | 145 | dhcpd_reload() { |
6c07160e MT |
146 | dhcpd_restart $@ |
147 | } | |
148 | ||
1c6a4e30 | 149 | dhcpd_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 | 254 | dhcpd_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 | 270 | dhcpd_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 | 286 | dhcpd_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 | 302 | dhcpd_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 | 318 | dhcpd_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 | 334 | dhcpd_global_settings_list() { |
cc02f6be MT |
335 | local proto="${1}" |
336 | assert isset proto | |
337 | ||
338 | dhcpd_settings "${proto}" | |
339 | } | |
340 | ||
1c6a4e30 | 341 | dhcpd_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 | 357 | dhcpd_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 | 371 | dhcpd_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 | 384 | dhcpd_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 | 399 | dhcpd_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 | 421 | dhcpd_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 | 434 | dhcpd_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 | 455 | dhcpd_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 | 472 | dhcpd_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 | 498 | dhcpd_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 | 578 | dhcpd_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 | 592 | dhcpd_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 | 611 | dhcpd_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 | 622 | dhcpd_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 | 636 | dhcpd_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 | 651 | dhcpd_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 | 672 | dhcpd_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 | 702 | dhcpd_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 | 785 | dhcpd_subnet_range_remove() { |
6c07160e MT |
786 | local path=$(dhcpd_subnet_range_path $@) |
787 | assert isset path | |
788 | ||
789 | rm -f ${path} | |
790 | } | |
791 | ||
1c6a4e30 | 792 | dhcpd_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 | 813 | dhcpd_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 | 827 | dhcpd_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 | 842 | dhcpd_subnet_options_file() { |
6c07160e MT |
843 | local path=$(dhcpd_subnet_path $@) |
844 | assert isset path | |
845 | ||
846 | print "${path}/options" | |
847 | } | |
848 | ||
1c6a4e30 | 849 | dhcpd_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 | 865 | dhcpd_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 | 1082 | dhcpd_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 | } |