Files
packages/net/kea/files/dhcp4.sh
Philip Prindeville 12b8c3a543 kea: send subnet renew time if not per host
If the renewal-time isn't set for the host, but it's forced to send,
send the subnet renew time value instead.

Signed-off-by: Philip Prindeville <philipp@redfish-solutions.com>
2026-04-09 17:36:46 -06:00

1018 lines
21 KiB
Bash
Executable File

#!/bin/sh
NL=$'\n'
WS=$'[\t ]'
TTL=3600
PREFIX="update add"
prog="$(basename $0)"
dyndir=/var/run/dhcp
keadir=/var/lib/kea
session_key_name=local-ddns
getvar() {
local __dest="$1" _var="$2"
eval "export -n -- \"$__dest=\${$_var}\""
}
setvar() {
local __dest="$1" _val="$2"
eval "export -n -- \"$__dest=$_val\""
}
#### delete me -- these should all be in jshn.sh
json_add_names() {
local _name
for _name in "$@"; do
json_push_string "$_name"
done
}
#### delete me
time2seconds() {
local _var="$1" _timestring="$2"
local _multiplier _number _suffix
_suffix="${_timestring//[0-9 ]}"
_number="${_timestring%%$_suffix}"
[ "$_number$_suffix" != "$_timestring" ] && return 1
case "$_suffix" in
"" | s)
_multiplier=1
;;
m)
_multiplier=60
;;
h)
_multiplier=3600
;;
d)
_multiplier=86400
;;
w)
_multiplier=604800
;;
*)
return 1
;;
esac
setvar "$_var" "$((_number * _multiplier))"
}
explode_dotted() {
local _var="$1" _val="${2//\./ }"
setvar "$_var" "$_val"
}
is_decimal() {
local _val="$1"
[ -z "${_val//[0-9]/}" ]
}
is_hex() {
local _val="$1"
[ -z "${_val//[0-9a-f]/}" ]
}
trim() {
local _var="$1" _str="$2" _prev
while true; do
_prev="$_str"
_str="${_str%%$WS}"
[ "$_str" = "$_prev" ] && break
done
while true; do
_prev="$_str"
_str="${_str##$WS}"
[ "$_str" = "$_prev" ] && break
done
setvar "$_var" "$_str"
}
mangle() {
local _var="$1" _name="${2//[^A-Za-z0-9]/_}"
setvar "$_var" "$_name"
}
rfc1918_prefix() {
local _var="$1" _subnet="${2%/*}" _exploded
explode_dotted _exploded "$_subnet"
set -- $_exploded
case "$1.$2" in
10.*)
setvar "$_var" "$1" ;;
172.1[6789]|172.2[0-9]|172.3[01]|192.168)
setvar "$_var" "$1.$2" ;;
*)
setvar "$_var" "" ;;
esac
}
no_ipv6() {
[ -n "$(named-checkconf -px \
| sed -r -ne '1N; N; /^\tlisten-on-v6 ?\{\n\t\t"none";\n\t\};$/{ p; q; }; D')" ]
}
subnet_of() {
local _var="$1" _ip
str2ip _ip "$2" || return 1
local _ifname _pfx _start _end
for _ifname in $dhcp_ifs; do
mangle _pfx "$_ifname"
getvar _start "${_pfx}_start"
getvar _end "${_pfx}_end"
if [ $_start -le $_ip ] && [ $_ip -le $_end ]; then
setvar "$_var" "$_ifname"
return 0
fi
done
return 1
}
# duplicated from dnsmasq init script
hex_to_hostid() {
local _var="$1"
local _hex="${2#0x}" # strip optional "0x" prefix
if ! is_hex "$_hex"; then
echo "Invalid hostid: $_hex" >&2
return 1
fi
# convert into host id
setvar "$_var" "$(
printf "%0x:%0x" \
$(((0x$_hex >> 16) % 65536)) \
$(( 0x$_hex % 65536))
)"
return 0
}
update() {
local _lhs="$1" _family="$2" _type="$3"
shift 3
[ $dynamicdns -eq 1 ] && \
echo -e "$PREFIX" "$_lhs $_family $_type $@\nsend" >> "$dyn_file"
}
rev_str() {
local _var="$1" _str="$2" _delim="$3"
local _frag _result=""
for _frag in ${_str//$_delim/ }; do
prepend _result "$_frag" "$_delim"
done
setvar "$_var" "$_result"
}
write_empty_zone() {
local zpath
zpath="$1"
cat > "$zpath" <<\EOF
;
; BIND empty zone created by Kea dhcp4.sh plugin
;
$TTL 604800
@ IN SOA localhost. root.localhost. (
1 ; Serial
604800 ; Refresh
86400 ; Retry
419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS localhost.
EOF
}
create_empty_zone() {
local zone error zpath command
zone="$1"
zpath="$dyndir/db.$zone"
if [ ! -d "$dyndir" ]; then
mkdir -p "$dyndir" || return 1
chown bind:bind "$dyndir" || return 1
fi
write_empty_zone "$zpath"
chown bind:bind "$zpath" || return 1
chmod 0664 "$zpath" || return 1
# if the zone doesn't exist, or a RFC-1918 in-addr.arpa zone, then
# we need to add it, otherwise we need to modify it.
if ! rndc zonestatus $zone >/dev/null 2>&1; then
command="addzone"
else
command="modzone"
fi
case "$zone" in
10.in-addr.arpa|1[6789].172.in-addr.arpa|2[0-9].172.in-addr.arpa|3[01].172.in-addr.arpa|168.192.in-addr.arpa)
command="addzone" ;;
esac
if ! error=$(rndc $command $zone "{
type primary;
file \"$zpath\";
update-policy {
grant $session_key_name zonesub any;
};
};" 2>&1); then
case "$error" in
*"already exists"*)
;;
*)
logger -s -p info -t "$prog" "Failed to add zone $zone: $error"
return 1
;;
esac
fi
}
option_def() {
local name="$1" code="$2" type="$3"
case "$type" in
binary|boolean|empty|fqdn|ipv4-address|ipv6-address|ipv6-prefix|psid|string|tuple|uint8|uint16|uint32|int8|int16|int32)
;;
record)
echo "Not yet supported: $type" >&2
exit 1
;;
*)
echo "Unknown option type: $type" >&2
exit 1
;;
esac
if ! json_get_type type "option-def"; then
json_add_array "option-def"
else
json_select "option-def"
fi
json_add_object
json_add_fields "name:string=$name" "code:int=$code" "type:string=$type"
json_close_object
json_select ".." # option-def
}
option_data() {
local arg value type
# if the option-data array doesn't exist, create it since
# this is the first time through. otherwise, select it.
if ! json_get_type type "option-data"; then
json_add_array "option-data"
else
json_select "option-data"
fi
json_add_object
while [ $# -ge 1 ]; do
arg="$1"
shift
case "$arg" in
name:*)
value="${arg#name:}"
json_add_string "name" "$value"
;;
space:*)
value="${arg#space:}"
json_add_string "space" "$value"
;;
code:*)
value="${arg#code:}"
if is_decimal "$value"; then
json_add_int "code" $value
else
echo "Bad code '$value' in DHCP options" >&2
fi
;;
csv-format:true)
json_add_boolean "csv-format" 1
;;
csv-format:false)
json_add_boolean "csv-format" 0
;;
data:*)
value="$arg"
json_add_fields "$value"
;;
always-send:true)
json_add_boolean "always-send" 1
;;
*)
echo "Unexpected argument '$arg' to option_data" >&2
;;
esac
done
json_close_object
json_select .. # option-data
}
is_force_send() {
local forced="$1" option="$2"
list_contains forced "$option" && echo "always-send:true"
}
append_routes() {
local tuple
local network prefix router subnet
trim tuple "$1"
subnet="${tuple%%$WS*}"
network="${subnet%/[0-9]*}"
prefix="${subnet#*/}"
router="${tuple#${subnet}$WS}"
append routes "$subnet - $router" ", "
}
append_dhcp_options() {
local tuple="$1"
# strip redundant "option:" prefix
tuple="${tuple#option:}"
local tag="${tuple%%,*}"
local values="${tuple#$tag,}"
case "$tag" in
routers|time-servers|name-servers|domain-name-servers|log-servers|static-routes|ntp-servers|domain-search)
option_data "name:$tag" "data:string=$values"
;;
dhcp-renewal-time)
if ! is_decimal "$values"; then
echo "Expected a decimal integer: $tag" >&2
exit 1
fi
## option_data "name:$tag" "data:int=$values"
option_data "name:$tag" "data:string=$values"
;;
*)
echo "Unhandled option: $tag" >&2
;;
esac
}
static_cname_add() {
local cfg="$1"
local cname target
config_get cname "$cfg" "cname"
[ -n "$cname" ] || return 0
config_get target "$cfg" "target"
[ -n "$target" ] || return 0
case "$target" in
*.*)
;;
*)
target="$target.$g_domain"
;;
esac
update "$cname.$g_domain." IN CNAME "$target."
}
static_cnames() {
config_foreach static_cname_add cname "$@"
}
static_domain_add() {
local cfg="$1"
local name ip ips revip octets
config_get name "$cfg" "name"
[ -n "$name" ] || return 0
config_get ip "$cfg" "ip"
[ -n "$ip" ] || return 0
ips="$ip"
for ip in $ips; do
rev_str revip "$ip" "."
update "$name.$g_domain." IN A "$ip"
rfc1918_prefix octets "$ip"
[ -n "$octets" ] && \
update "$revip.in-addr.arpa." IN PTR "$name.$g_domain."
done
}
static_domains() {
config_foreach static_domain_add domain "$@"
}
static_mxhost_add() {
local cfg="$1"
local h_domain relay pref
config_get h_domain "$cfg" "domain"
[ -n "$h_domain" ] || return 0
config_get relay "$cfg" "relay"
[ -n "$relay" ] || return 0
config_get pref "$cfg" "pref"
[ -n "$pref" ] || return 0
case "$relay" in
*.*)
;;
*)
relay="$relay.$g_domain"
;;
esac
if [ "$h_domain" = "@" ]; then
update "$g_domain." IN MX "$pref" "$relay."
else
update "$h_domain.$g_domain." IN MX "$pref" "$relay."
fi
}
static_mxhosts() {
config_foreach static_mxhost_add mxhost "$@"
}
static_srvhost_add() {
local cfg="$1"
local srv target port priority weight
config_get srv "$cfg" "srv"
[ -n "$srv" ] || return 0
config_get target "$cfg" "target"
[ -n "$target" ] || return 0
config_get port "$cfg" "port"
[ -n "$port" ] || return 0
config_get priority "$cfg" "priority"
[ -n "$priority" ] || return 0
config_get weight "$cfg" "weight"
[ -n "$weight" ] || return 0
case "$target" in
*.*)
;;
*)
target="$target.$g_domain"
;;
esac
update "$srv.$g_domain." IN SRV "$priority" "$weight" "$port" "$target."
}
static_srvhosts() {
config_foreach static_srvhost_add srvhost "$@"
}
static_host_add() {
local cfg="$1"
local broadcast hostid id macn macs mac name net ip ips revip leasetime
local h_domain s_domain defaultroute renewal_time s_renewal_time
local h_gateway s_gateway
local force_send always index
config_get macs "$cfg" "mac"
[ -n "$macs" ] || return 0
config_get name "$cfg" "name"
[ -n "$name" ] || return 0
config_get ip "$cfg" "ip"
[ -n "$ip" ] || return 0
# needs to match a provisioned subnet
local ifname pfx
if ! subnet_of ifname "$ip"; then
echo "$name's address $ip doesn't match any subnet" >&2
return 1
fi
mangle pfx "$ifname"
getvar net "${pfx}_ifname"
getvar index "${net}_subnet4_index"
local h_gateway s_gateway
getvar s_gateway "${pfx}_gateway"
config_get_bool broadcast "$cfg" "broadcast" 0
config_get dns "$cfg" "dns"
config_get h_gateway "$cfg" "gateway" "$s_gateway"
config_get leasetime "$cfg" "leasetime"
if [ -n "$leasetime" ]; then
time2seconds leasetime "$leasetime" || return 1
fi
config_get hostid "$cfg" "hostid"
if [ -n "$hostid" ]; then
hex_to_hostid hostid "$hostid" || return 1
fi
local s_defaultroute
getvar s_defaultroute "${pfx}_defaultroute"
# if provisioned, otherwise default to subnet value
config_get_bool defaultroute "$cfg" "default_route" $s_defaultroute
config_get force_send "$cfg" "force_send"
force_send="${force_send//,/ }"
local s_domain
getvar s_domain "${pfx}_domain"
config_get h_domain "$cfg" "domain" "$s_domain"
getvar s_renewal_time "${pfx}_renewal_time"
config_get renewal_time "$cfg" "renewal_time"
json_select "$index" # why "$index" and not "$pfx"?
json_select "reservations"
# rebinding-time)
macn=0
for mac in $macs; do
macn=$(( macn + 1 ))
done
for mac in $macs; do
local secname="$name"
if [ $macn -gt 1 ]; then
secname="${name}-${mac//:}"
fi
json_add_object "$mac"
json_add_fields "hostname:string=$name" "hw-address:string=$mac" "ip-address:string=$ip"
[ -n "$hostid" ] && json_add_fields "client-id:string=$hostid"
### redundant...
always="$(is_force_send "$force_send" "hostname")"
option_data "name:host-name" "data:string=$name" $always
local routes=
config_list_foreach "$cfg" "routes" append_routes
always="$(is_force_send "$force_send" "routes")"
if [ -n "$routes" -o -n "$always" ]; then
option_data "name:classless-static-route" "code:121" "data:string=$routes" $always
fi
always="$(is_force_send "$force_send" "domain-name")"
if [ "$h_domain" != "$s_domain" -o -n "$always" ]; then
option_data "name:domain-name" "data:string=$h_domain" $always
fi
always="$(is_force_send "$force_send" "fqdn")"
[ -n "$always" ] && option_data "name:host-name" "data:string=$name.$h_domain" $always
if [ -n "$dns" ]; then
always="$(is_force_send "$force_send" "domain-name-servers")"
option_data "name:domain-name-servers" "data:string=$dns" $always
fi
if [ "$h_gateway" != "$s_gateway" -a $defaultroute -eq 1 ]; then
always="$(is_force_send "$force_send" "routers")"
option_data "name:routers" "data:string=$h_gateway" $always
fi
always="$(is_force_send "$force_send" "renewal-time")"
if [ -n "$renewal_time" ]; then
option_data "name:dhcp-renewal-time" "data:string=$renewal_time" $always
elif [ -n "$always" ]; then
option_data "name:dhcp-renewal-time" "data:string=$s_renewal_time" $always
fi
### need special handling for list dhcp_option 'option:xxx,yyy'
config_list_foreach "$cfg" "dhcp_option" append_dhcp_options
# other options here
### always-broadcast
### default-lease-time
### max-lease-time
json_close_object # $mac
done
json_select .. # reservations
json_select .. # $index
ips="$ip"
for ip in $ips; do
rev_str revip "$ip" "."
update "$name.$h_domain." IN A "$ip"
update "$revip.in-addr.arpa." IN PTR "$name.$h_domain."
done
}
static_hosts() {
config_foreach static_host_add host "$@"
}
gen_dhcp_subnet() {
local cfg="$1" index
json_add_object "$cfg"
json_get_index index
subnet4_id=$((subnet4_id + 1))
json_add_int "id" $subnet4_id
setvar "${cfg}_subnet4_id" "$subnet4_id"
setvar "${cfg}_subnet4_index" "$index"
json_add_fields "subnet:string=$NETWORK/$PREFIX"
if [ -n "$START" ] && [ -n "$END" ]; then
json_add_array "pools"
json_add_object
json_add_fields "pool:string=$START - $END"
json_close_object
json_close_array # pools
fi
if [ -n "$leasetime" ]; then
json_add_fields "valid-lifetime:int=$leasetime" "max-valid-lifetime:int=$leasetime"
fi
option_data "name:subnet-mask" "data:string=$NETMASK"
if [ -n "$BROADCAST" ] && [ "$BROADCAST" != "0.0.0.0" ]; then
option_data "name:broadcast-address" "data:string=$BROADCAST"
fi
if [ $defaultroute -eq 1 ]; then
option_data "name:routers" "data:string=$gateway"
fi
if [ -n "$DNS" ]; then
option_data "name:domain-name-servers" "data:string=$DNS"
fi
if [ "$s_domain" != "$g_domain" ]; then
option_data "name:domain-name" "data:string=$s_domain"
fi
[ -n "$ntp_servers" ] && option_data "name:ntp-servers" "data:string=$ntp_servers"
[ -n "$routes" ] && option_data "name:classless-ipv4-route" "code:121" "csv-format:false" "data:string=$routes"
if [ $dynamicdhcp -eq 0 ]; then
if [ $authoritative -eq 1 ]; then
# see:
# https://gitlab.isc.org/isc-projects/kea/-/issues/4110
# echo " deny unknown-clients;"
:
else
# echo " ignore unknown-clients;"
json_add_array "client-classes"
json_add_object
json_add_fields "name:string=DROP" "test:string=not(member('KNOWN'))"
json_close_object
json_close_array # client-classes
fi
fi
config_list_foreach "$cfg" "dhcp_option" append_dhcp_options
json_add_array "reservations"
json_close_array # reservations
json_close_object # $cfg
}
dhcpd_add() {
local cfg="$1"
local dhcp6range="::"
local dynamicdhcp defaultroute dnsserv dnsserver end
local gateway ifname ignore ntp_servers
local leasetime
local limit net netmask networkid octets pfx proto
local routes start subnet s_domain s_renewal_time
local IP NETMASK BROADCAST NETWORK PREFIX DNS START END
config_get_bool ignore "$cfg" "ignore" 0
[ $ignore -eq 1 ] && return 0
config_get net "$cfg" "interface"
[ -n "$net" ] || return 0
config_get start "$cfg" "start"
config_get limit "$cfg" "limit"
case "$start:$limit" in
:)
;;
:*|*:)
echo "In pool $cfg start/limit must be used together" >&2
return 0
;;
*:*)
# In Kea, this is done implicitly by not having a pool
# for unknown clients defined.
if [ $boot_unknown_clients -eq 1 ]; then
echo "To not boot unknown clients, remove the pool start and limit for $cfg" >&2
fi
;;
esac
network_get_subnet subnet "$net" || return 0
network_get_device ifname "$net" || return 0
network_get_protocol proto "$net" || return 0
mangle pfx "$ifname"
setvar "${pfx}_ifname" "$net"
# only operate on statically provisioned interfaces
[ "$proto" != "static" ] && return 0
append dhcp_ifs "$ifname"
rfc1918_prefix octets "$subnet"
[ -n "$octets" ] && append rfc1918_nets "$octets"
config_get_bool dynamicdhcp "$cfg" "dynamicdhcp" 1
config_get_bool defaultroute "$cfg" "default_route" 1
setvar "${pfx}_defaultroute" $defaultroute
ipcalc -d $subnet $start $limit
setvar "${pfx}_start" "$NETWORK"
setvar "${pfx}_end" "$BROADCAST"
ip2str IP "$IP"
ip2str NETMASK "$NETMASK"
ip2str NETWORK "$NETWORK"
ip2str BROADCAST "$BROADCAST"
[ -n "${START:+x}" ] && ip2str START "$START"
[ -n "${END:+x}" ] && ip2str END "$END"
config_get netmask "$cfg" "netmask" "$NETMASK"
NETMASK="$netmask"
config_get s_domain "$cfg" "domain" "$g_domain"
setvar "${pfx}_domain" "$s_domain"
config_get ntp_servers "$cfg" "ntp_servers" ""
config_get s_renewal_time "$cfg" "renewal_time"
if [ -n "$s_renewal_time" ]; then
time2seconds s_renewal_time "$s_renewal_time" || exit 1
else
s_renewal_time="$g_renewal_time"
fi
setvar "${pfx}_renewal_time" "$s_renewal_time"
config_get leasetime "$cfg" "leasetime"
if [ -n "$leasetime" ]; then
time2seconds leasetime "$leasetime" || return 1
setvar "${pfx}_leasetime" "$leasetime"
fi
if network_get_dnsserver dnsserver "$net" ; then
for dnsserv in $dnsserver; do
append DNS "$dnsserv" ","
done
else
DNS="$IP"
fi
if ! network_get_gateway gateway "$net" ; then
gateway="$IP"
fi
setvar "${pfx}_gateway" $gateway
routes=
config_list_foreach "$cfg" "routes" append_routes
gen_dhcp_subnet "$cfg"
}
general_config() {
local always_broadcast log_facility
local default_lease_time max_lease_time intf
config_get_bool always_broadcast "isc_dhcpd" "always_broadcast" 0
config_get_bool authoritative "isc_dhcpd" "authoritative" 1
config_get_bool boot_unknown_clients "isc_dhcpd" "boot_unknown_clients" 1
config_get default_lease_time "isc_dhcpd" "default_lease_time" 3600
config_get max_lease_time "isc_dhcpd" "max_lease_time" 86400
config_get g_renewal_time "isc_dhcpd" "renewal_time"
config_get log_facility "isc_dhcpd" "log_facility"
config_get g_domain "isc_dhcpd" "domain"
config_get_bool dynamicdns "isc_dhcpd" dynamicdns 0
time2seconds default_lease_time "$default_lease_time" || return 1
time2seconds max_lease_time "$max_lease_time" || return 1
if [ -n "$g_renewal_time" ]; then
time2seconds g_renewal_time "$g_renewal_time" || return 1
else
g_renewal_time=$((default_lease_time / 2))
fi
setvar g_max_lease_time "$max_lease_time"
setvar g_lease_time "$default_lease_time"
setvar g_renewal_time "$g_renewal_time"
json_add_object "lease-database"
json_add_string "type" "memfile"
json_add_boolean "persist" 1
json_add_string "name" "$keadir/kea-leases4.csv"
json_add_int "lfc-interval" 900
json_add_int "max-row-errors" 1
json_close_object
json_add_object "interfaces-config"
json_add_array "interfaces"
# will populate later
json_close_array # interfaces
json_add_boolean "re-detect" 0
json_add_string "dhcp-socket-type" "raw"
json_add_string "outbound-interface" "same-as-inbound"
json_close_object # interfaces-config
## option_def "renew-timer" 58 "uint32"
[ $authoritative -eq 1 ] && json_add_boolean "authoritative" "1"
json_add_boolean "ip-reservations-unique" "0"
if [ $dynamicdns -eq 1 ]; then
json_add_fields "ddns-qualifying-suffix:string=$g_domain." "ddns-send-updates:boolean=1"
fi
json_add_fields "valid-lifetime:int=$default_lease_time" "max-valid-lifetime:int=$g_max_lease_time" "renew-timer:int=$g_renewal_time"
option_data "name:domain-name" "data:string=$g_domain"
### see:
### https://gitlab.isc.org/isc-projects/kea/-/issues/241
if [ $always_broadcast -eq 1 ]; then
echo "This option is deprecated and being ignored: always-broadcast" >&2
fi
}
write_zones() {
if [ $dynamicdns -eq 1 ]; then
rndc freeze
create_empty_zone "$g_domain"
local mynet
for mynet in $rfc1918_nets; do
rev_str mynet "$mynet" "."
create_empty_zone "$mynet.in-addr.arpa"
done
rndc thaw
fi
rm -f /tmp/resolv.conf
echo "# This file is generated by the DHCPD service" > /tmp/resolv.conf
[ -n "$g_domain" ] && echo "domain $g_domain" >> /tmp/resolv.conf
echo "nameserver 127.0.0.1" >> /tmp/resolv.conf
}
main() {
# values parsed by general_config that we need to persist
# for subsequent subnet and host configurations
local dhcp_ifs= dynamicdns authoritative boot_unknown_clients
local g_domain g_renewal_time g_max_lease_time g_lease_time
local rfc1918_nets=""
local config_file="$1"
if [ ! -f /etc/config/dhcp ]; then
return 0
fi
local dyn_file="$(mktemp -u /tmp/dhcpd.XXXXXX)"
. /lib/functions.sh
. /lib/functions/ipv4.sh
. /lib/functions/network.sh
. /usr/share/libubox/jshn.sh
mkdir -p "$keadir"
config_load dhcp
json_init
json_add_object "Dhcp4"
general_config
if [ $dynamicdns -eq 1 ]; then
cat <<EOF > "$dyn_file"
; Generated by $prog at $(date)
ttl $TTL
EOF
fi
local subnet4_id=0
json_add_array "subnet4"
config_foreach dhcpd_add dhcp
static_hosts
json_close_array # subnet4
json_add_array "host-reservation-identifiers"
json_add_names "hw-address" "client-id"
json_close_array # host-reservation-identifiers
# json_add_string "reservation-mode" "global"
json_add_boolean "reservations-in-subnet" 1
# plug the interfaces back in
json_select "interfaces-config"
json_select "interfaces"
json_add_names $dhcp_ifs
json_select ..
json_select ..
json_close_object # Dhcp4
# the rest just generate DNS records
static_cnames
static_domains
static_mxhosts
static_srvhosts
write_zones
# not running on any interfaces
[ -z "$dhcp_ifs" ] && return 1
rfc1918_nets="${rfc1918_nets// /$NL}"
rfc1918_nets="$(echo "$rfc1918_nets" | sort -V | uniq)"
rfc1918_nets="${rfc1918_nets//$NL/ }"
if [ $dynamicdns -eq 1 ]; then
local args=
no_ipv6 && args="-4"
nsupdate -l -v $args "$dyn_file"
fi
rm -f "$dyn_file"
json_pretty
json_dump | sed 's/\t/ /g' > "$config_file"
return 0
}
main "$@"