9525743c07
dnsmasq has sufficient services to meet the needs of DHCP and RA with IP6 for single router router users. This is the most common use for consumer routers. Its reenforced as most ISP tend to only DHCP-PD /64. dnsmasq has year over year demonstrated great flexibility in its option set, and support for off-standard DHCP clients. odhcpd has enhanced capabilities focused on IP6 such as DHCP/RA relay and NDP proxy. However, it is not as flexible in its option set. odhcpd is not as forgiving with off-standard DHCP clients. Some points may represent a long term TODO list, but it is the state currently. These changes make any such combination possible. Already odhcpd can be set as the main dhcp server. Now odhcpd can be removed or disabled and dnsmasq will take over if DHCPv6 compiled in. The existing DHCPv6 and RA UCI are translated into dnsmasq.conf. The changes focus on '--dhcp-range', '--dhcp-host', and '--dhcp-options'. DHCP host ID is least 16 bits [::1000-::FFFF], but leaves low range for typical infrastructure assignments. dnsmasq accepts DHCPv6 options in the tranditional '--dhcp-option' put they must be prefixed 'option6:'. dnsmasq will also discover SLAAC DNS entries from DHCPv4 clients MAC, and confirm with a ping at least renew. Long term TODO include improving use of dnsmasq relay options for DHCPv4 and DHCPv6 in parallel. It would also be possible to preconfigure DHCP-PD in host-with-options records for fixed infrastructure. Signed-off-by: Eric Luehrsen <ericluehrsen@hotmail.com> [Jo-Philipp Wich: emit proper IPv6 hostid format in dhcp-host directive] Signed-off-by: Jo-Philipp Wich <jo@mein.io>
1022 lines
25 KiB
Bash
1022 lines
25 KiB
Bash
#!/bin/sh /etc/rc.common
|
|
# Copyright (C) 2007-2012 OpenWrt.org
|
|
|
|
START=19
|
|
|
|
USE_PROCD=1
|
|
PROG=/usr/sbin/dnsmasq
|
|
|
|
ADD_LOCAL_DOMAIN=1
|
|
ADD_LOCAL_HOSTNAME=1
|
|
ADD_WAN_FQDN=0
|
|
ADD_LOCAL_FQDN=""
|
|
|
|
BASECONFIGFILE="/var/etc/dnsmasq.conf"
|
|
BASEHOSTFILE="/tmp/hosts/dhcp"
|
|
BASETIMESTAMPFILE="/etc/dnsmasq.time"
|
|
TRUSTANCHORSFILE="/usr/share/dnsmasq/trust-anchors.conf"
|
|
TIMEVALIDFILE="/var/state/dnsmasqsec"
|
|
BASEDHCPSTAMPFILE="/var/run/dnsmasq"
|
|
|
|
DNSMASQ_DHCP_VER=4
|
|
|
|
xappend() {
|
|
local value="$1"
|
|
|
|
echo "${value#--}" >> $CONFIGFILE_TMP
|
|
}
|
|
|
|
hex_to_hostid() {
|
|
local var="$1"
|
|
local hex="${2#0x}" # strip optional "0x" prefix
|
|
|
|
if [ -n "${hex//[0-9a-fA-F]/}" ]; then
|
|
# is invalid hex literal
|
|
return 1
|
|
fi
|
|
|
|
# convert into host id
|
|
export "$var=$(
|
|
printf "%0x:%0x" \
|
|
$(((0x$hex >> 16) % 65536)) \
|
|
$(( 0x$hex % 65536))
|
|
)"
|
|
|
|
return 0
|
|
}
|
|
|
|
dhcp_calc() {
|
|
local ip="$1"
|
|
local res=0
|
|
|
|
while [ -n "$ip" ]; do
|
|
part="${ip%%.*}"
|
|
res="$(($res * 256))"
|
|
res="$(($res + $part))"
|
|
[ "${ip%.*}" != "$ip" ] && ip="${ip#*.}" || ip=
|
|
done
|
|
echo "$res"
|
|
}
|
|
|
|
dhcp_check() {
|
|
local ifname="$1"
|
|
local stamp="${BASEDHCPSTAMPFILE_CFG}.${ifname}.dhcp"
|
|
local rv=0
|
|
|
|
[ -s "$stamp" ] && return $(cat "$stamp")
|
|
|
|
# If there's no carrier yet, skip this interface.
|
|
# The init script will be called again once the link is up
|
|
case "$(devstatus "$ifname" | jsonfilter -e @.carrier)" in
|
|
false) return 1;;
|
|
esac
|
|
|
|
udhcpc -n -q -s /bin/true -t 1 -i "$ifname" >&- && rv=1 || rv=0
|
|
|
|
[ $rv -eq 1 ] && \
|
|
logger -t dnsmasq \
|
|
"found already running DHCP-server on interface '$ifname'" \
|
|
"refusing to start, use 'option force 1' to override"
|
|
|
|
echo $rv > "$stamp"
|
|
return $rv
|
|
}
|
|
|
|
log_once() {
|
|
pidof dnsmasq >/dev/null || \
|
|
logger -t dnsmasq "$@"
|
|
}
|
|
|
|
append_bool() {
|
|
local section="$1"
|
|
local option="$2"
|
|
local value="$3"
|
|
local _loctmp
|
|
config_get_bool _loctmp "$section" "$option" 0
|
|
[ $_loctmp -gt 0 ] && xappend "$value"
|
|
}
|
|
|
|
append_parm() {
|
|
local section="$1"
|
|
local option="$2"
|
|
local switch="$3"
|
|
local default="$4"
|
|
local _loctmp
|
|
config_get _loctmp "$section" "$option" "$default"
|
|
[ -z "$_loctmp" ] && return 0
|
|
xappend "$switch=$_loctmp"
|
|
}
|
|
|
|
append_server() {
|
|
xappend "--server=$1"
|
|
}
|
|
|
|
append_address() {
|
|
xappend "--address=$1"
|
|
}
|
|
|
|
append_ipset() {
|
|
xappend "--ipset=$1"
|
|
}
|
|
|
|
append_interface() {
|
|
network_get_device ifname "$1" || return
|
|
xappend "--interface=$ifname"
|
|
}
|
|
|
|
append_notinterface() {
|
|
network_get_device ifname "$1" || return
|
|
xappend "--except-interface=$ifname"
|
|
}
|
|
|
|
append_addnhosts() {
|
|
xappend "--addn-hosts=$1"
|
|
}
|
|
|
|
append_bogusnxdomain() {
|
|
xappend "--bogus-nxdomain=$1"
|
|
}
|
|
|
|
append_pxe_service() {
|
|
xappend "--pxe-service=$1"
|
|
}
|
|
|
|
filter_dnsmasq() {
|
|
local cfg="$1" func="$2" match_cfg="$3" found_cfg
|
|
|
|
# use entry when no instance entry set, or if it matches
|
|
config_get found_cfg "$cfg" "instance"
|
|
if [ -z "$found_cfg" -o "$found_cfg" = "$match_cfg" ]; then
|
|
$func $cfg
|
|
fi
|
|
}
|
|
|
|
dhcp_subscrid_add() {
|
|
local cfg="$1"
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] || return 0
|
|
|
|
config_get subscriberid "$cfg" subscriberid
|
|
[ -n "$subscriberid" ] || return 0
|
|
|
|
xappend "--dhcp-subscrid=$networkid,$subscriberid"
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
|
|
dhcp_option_add "$cfg" "$networkid" "$force"
|
|
}
|
|
|
|
dhcp_remoteid_add() {
|
|
local cfg="$1"
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] || return 0
|
|
|
|
config_get remoteid "$cfg" remoteid
|
|
[ -n "$remoteid" ] || return 0
|
|
|
|
xappend "--dhcp-remoteid=$networkid,$remoteid"
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
|
|
dhcp_option_add "$cfg" "$networkid" "$force"
|
|
}
|
|
|
|
dhcp_circuitid_add() {
|
|
# TODO: DHCPV6 does not have circuitid; catch "option6:"
|
|
local cfg="$1"
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] || return 0
|
|
|
|
config_get circuitid "$cfg" circuitid
|
|
[ -n "$circuitid" ] || return 0
|
|
|
|
xappend "--dhcp-circuitid=$networkid,$circuitid"
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
|
|
dhcp_option_add "$cfg" "$networkid" "$force"
|
|
}
|
|
|
|
dhcp_userclass_add() {
|
|
local cfg="$1"
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] || return 0
|
|
|
|
config_get userclass "$cfg" userclass
|
|
[ -n "$userclass" ] || return 0
|
|
|
|
xappend "--dhcp-userclass=$networkid,$userclass"
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
|
|
dhcp_option_add "$cfg" "$networkid" "$force"
|
|
}
|
|
|
|
dhcp_vendorclass_add() {
|
|
# TODO: DHCPV6 vendor class has stricter definitions; catch? fixup?
|
|
local cfg="$1"
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] || return 0
|
|
|
|
config_get vendorclass "$cfg" vendorclass
|
|
[ -n "$vendorclass" ] || return 0
|
|
|
|
xappend "--dhcp-vendorclass=$networkid,$vendorclass"
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
|
|
dhcp_option_add "$cfg" "$networkid" "$force"
|
|
}
|
|
|
|
dhcp_match_add() {
|
|
local cfg="$1"
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] || return 0
|
|
|
|
config_get match "$cfg" match
|
|
[ -n "$match" ] || return 0
|
|
|
|
xappend "--dhcp-match=$networkid,$match"
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
|
|
dhcp_option_add "$cfg" "$networkid" "$force"
|
|
}
|
|
|
|
dhcp_host_add() {
|
|
local cfg="$1"
|
|
local hosttag nametime addrs
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] && dhcp_option_add "$cfg" "$networkid" "$force"
|
|
|
|
config_get_bool enable "$cfg" enable 1
|
|
[ "$enable" = "0" ] && return 0
|
|
|
|
config_get name "$cfg" name
|
|
config_get ip "$cfg" ip
|
|
config_get hostid "$cfg" hostid
|
|
|
|
[ -n "$ip" -o -n "$name" -o -n "$hostid" ] || return 0
|
|
|
|
config_get_bool dns "$cfg" dns 0
|
|
[ "$dns" = "1" -a -n "$ip" -a -n "$name" ] && {
|
|
echo "$ip $name${DOMAIN:+.$DOMAIN}" >> $HOSTFILE
|
|
}
|
|
|
|
config_get mac "$cfg" mac
|
|
config_get duid "$cfg" duid
|
|
config_get tag "$cfg" tag
|
|
|
|
if [ -n "$mac" ]; then
|
|
# --dhcp-host=00:20:e0:3b:13:af,192.168.0.199,lap
|
|
# many MAC are possible to track a laptop ON/OFF dock
|
|
macs=""
|
|
for m in $mac; do append macs "$m" ","; done
|
|
fi
|
|
|
|
if [ $DNSMASQ_DHCP_VER -eq 6 -a -n "$duid" ]; then
|
|
# --dhcp-host=id:00:03:00:01:12:00:00:01:02:03,[::beef],lap
|
|
# one (virtual) machine gets one DUID per RFC3315
|
|
duids="id:${duid// */}"
|
|
fi
|
|
|
|
if [ -z "$macs" -a -z "$duids" ]; then
|
|
# --dhcp-host=lap,192.168.0.199,[::beef]
|
|
[ -n "$name" ] || return 0
|
|
macs="$name"
|
|
name=""
|
|
fi
|
|
|
|
if [ -n "$hostid" ]; then
|
|
hex_to_hostid hostid "$hostid"
|
|
fi
|
|
|
|
config_get_bool broadcast "$cfg" broadcast 0
|
|
config_get leasetime "$cfg" leasetime
|
|
|
|
[ "$broadcast" = "0" ] && broadcast= || broadcast=",set:needs-broadcast"
|
|
|
|
hosttag="${networkid:+,set:${networkid}}${tag:+,set:${tag}}$broadcast"
|
|
nametime="${name:+,$name}${leasetime:+,$leasetime}"
|
|
|
|
if [ $DNSMASQ_DHCP_VER -eq 6 ]; then
|
|
addrs="${ip:+,$ip}${hostid:+,[::$hostid]}"
|
|
xappend "--dhcp-host=$macs${duids:+,$duids}$hosttag$addrs$nametime"
|
|
else
|
|
xappend "--dhcp-host=$macs$hosttag${ip:+,$ip}$nametime"
|
|
fi
|
|
}
|
|
|
|
dhcp_this_host_add() {
|
|
local net="$1"
|
|
local ifname="$2"
|
|
local mode="$3"
|
|
local routerstub routername ifdashname
|
|
local lanaddr lanaddr6 lanaddrs6 ulaprefix
|
|
|
|
if [ "$mode" -gt 0 ] ; then
|
|
ifdashname="${ifname//./-}"
|
|
routerstub="$( md5sum /etc/os-release )"
|
|
routerstub="router-${routerstub// */}"
|
|
routername="$( uci_get system @system[0] hostname $routerstub )"
|
|
|
|
if [ "$mode" -gt 1 ] ; then
|
|
if [ "$mode" -gt 2 ] ; then
|
|
if [ "$mode" -gt 3 ] ; then
|
|
xappend "--interface-name=$ifdashname.$routername.$DOMAIN,$ifname"
|
|
fi
|
|
|
|
xappend "--interface-name=$routername.$DOMAIN,$ifname"
|
|
fi
|
|
|
|
# All IP addresses discovered by dnsmasq will be labeled (except fe80::)
|
|
xappend "--interface-name=$routername,$ifname"
|
|
|
|
else
|
|
# This uses a static host file entry for only limited addresses.
|
|
# Use dnsmasq option "--expandhosts" to enable FQDN on host files.
|
|
ulaprefix="$(uci_get network @globals[0] ula_prefix)"
|
|
network_get_ipaddr lanaddr "$net"
|
|
network_get_ipaddrs6 lanaddrs6 "$net"
|
|
|
|
if [ -n "$lanaddr" ] ; then
|
|
dhcp_domain_add "" "$routername" "$lanaddr"
|
|
fi
|
|
|
|
if [ -n "$ulaprefix" -a -n "$lanaddrs6" ] ; then
|
|
for lanaddr6 in $lanaddrs6 ; do
|
|
case "$lanaddr6" in
|
|
"${ulaprefix%%:/*}"*)
|
|
dhcp_domain_add "" "$routername" "$lanaddr6"
|
|
;;
|
|
esac
|
|
done
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
dhcp_tag_add() {
|
|
# NOTE: dnsmasq has explicit "option6:" prefix for DHCPv6 so no collisions
|
|
local cfg="$1"
|
|
|
|
tag="$cfg"
|
|
|
|
[ -n "$tag" ] || return 0
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
[ "$force" = "0" ] && force=
|
|
|
|
config_get option "$cfg" dhcp_option
|
|
for o in $option; do
|
|
xappend "--dhcp-option${force:+-force}=tag:$tag,$o"
|
|
done
|
|
}
|
|
|
|
dhcp_mac_add() {
|
|
local cfg="$1"
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] || return 0
|
|
|
|
config_get mac "$cfg" mac
|
|
[ -n "$mac" ] || return 0
|
|
|
|
xappend "--dhcp-mac=$networkid,$mac"
|
|
|
|
dhcp_option_add "$cfg" "$networkid"
|
|
}
|
|
|
|
dhcp_boot_add() {
|
|
# TODO: BOOTURL is different between DHCPv4 and DHCPv6
|
|
local cfg="$1"
|
|
|
|
config_get networkid "$cfg" networkid
|
|
|
|
config_get filename "$cfg" filename
|
|
[ -n "$filename" ] || return 0
|
|
|
|
config_get servername "$cfg" servername
|
|
config_get serveraddress "$cfg" serveraddress
|
|
|
|
[ -n "$serveraddress" -a ! -n "$servername" ] && return 0
|
|
|
|
xappend "--dhcp-boot=${networkid:+net:$networkid,}${filename}${servername:+,$servername}${serveraddress:+,$serveraddress}"
|
|
|
|
config_get_bool force "$cfg" force 0
|
|
|
|
dhcp_option_add "$cfg" "$networkid" "$force"
|
|
}
|
|
|
|
|
|
dhcp_add() {
|
|
local cfg="$1"
|
|
local dhcp6range="::"
|
|
local nettag
|
|
|
|
config_get net "$cfg" interface
|
|
[ -n "$net" ] || return 0
|
|
|
|
config_get networkid "$cfg" networkid
|
|
[ -n "$networkid" ] || networkid="$net"
|
|
|
|
network_get_subnet subnet "$net" || return 0
|
|
network_get_device ifname "$net" || return 0
|
|
network_get_protocol proto "$net" || return 0
|
|
|
|
[ "$cachelocal" = "0" ] && network_get_dnsserver dnsserver "$net" && {
|
|
DNS_SERVERS="$DNS_SERVERS $dnsserver"
|
|
}
|
|
|
|
append_bool "$cfg" ignore "--no-dhcp-interface=$ifname" && {
|
|
# Many ISP do not have useful names for DHCP customers (your WAN).
|
|
dhcp_this_host_add "$net" "$ifname" "$ADD_WAN_FQDN"
|
|
return 0
|
|
}
|
|
|
|
# Do not support non-static interfaces for now
|
|
[ static = "$proto" ] || return 0
|
|
|
|
# Override interface netmask with dhcp config if applicable
|
|
config_get netmask "$cfg" netmask "${subnet##*/}"
|
|
|
|
#check for an already active dhcp server on the interface, unless 'force' is set
|
|
config_get_bool force "$cfg" force 0
|
|
[ $force -gt 0 ] || dhcp_check "$ifname" || return 0
|
|
|
|
config_get start "$cfg" start 100
|
|
config_get limit "$cfg" limit 150
|
|
config_get leasetime "$cfg" leasetime 12h
|
|
config_get options "$cfg" options
|
|
config_get_bool dynamicdhcp "$cfg" dynamicdhcp 1
|
|
|
|
config_get dhcpv4 "$cfg" dhcpv4
|
|
config_get dhcpv6 "$cfg" dhcpv6
|
|
|
|
config_get ra "$cfg" ra
|
|
config_get ra_management "$cfg" ra_management
|
|
config_get ra_preference "$cfg" ra_preference
|
|
|
|
# Put the router host name on this DHCP served interface address(es)
|
|
dhcp_this_host_add "$net" "$ifname" "$ADD_LOCAL_FQDN"
|
|
|
|
start="$( dhcp_calc "$start" )"
|
|
nettag="${networkid:+set:${networkid},}"
|
|
|
|
if [ "$limit" -gt 0 ] ; then
|
|
limit=$((limit-1))
|
|
fi
|
|
|
|
eval "$(ipcalc.sh "${subnet%%/*}" $netmask $start $limit)"
|
|
|
|
if [ "$dynamicdhcp" = "0" ] ; then
|
|
END="static"
|
|
dhcp6range="::,static"
|
|
else
|
|
dhcp6range="::1000,::ffff"
|
|
fi
|
|
|
|
|
|
if [ "$dhcpv4" != "disabled" ] ; then
|
|
xappend "--dhcp-range=$nettag$START,$END,$NETMASK,$leasetime${options:+ $options}"
|
|
fi
|
|
|
|
|
|
if [ $DNSMASQ_DHCP_VER -eq 6 -a "$ra" = "server" ] ; then
|
|
# Note: dnsmasq cannot just be a DHCPv6 server (all-in-1)
|
|
# and let some other machine(s) send RA pointing to it.
|
|
|
|
case $ra_preference in
|
|
*high*)
|
|
xappend "--ra-param=$ifname,high,0,7200"
|
|
;;
|
|
*low*)
|
|
xappend "--ra-param=$ifname,low,0,7200"
|
|
;;
|
|
*)
|
|
# Send UNSOLICITED RA at default interval and live for 2 hours.
|
|
# TODO: convert flexible lease time into route life time (only seconds).
|
|
xappend "--ra-param=$ifname,0,7200"
|
|
;;
|
|
esac
|
|
|
|
if [ "$dhcpv6" = "disabled" ] ; then
|
|
ra_management="3"
|
|
fi
|
|
|
|
|
|
case $ra_management in
|
|
0)
|
|
# SLACC with DCHP for extended options
|
|
xappend "--dhcp-range=$nettag::,constructor:$ifname,ra-stateless,ra-names"
|
|
;;
|
|
2)
|
|
# DHCP address and RA only for management redirection
|
|
xappend "--dhcp-range=$nettag$dhcp6range,constructor:$ifname,$leasetime"
|
|
;;
|
|
3)
|
|
# SLAAC only but dnsmasq attempts to link HOSTNAME, DHCPv4 MAC, and SLAAC
|
|
xappend "--dhcp-range=$nettag::,constructor:$ifname,ra-only,ra-names"
|
|
;;
|
|
*)
|
|
# SLAAC and full DHCP
|
|
xappend "--dhcp-range=$nettag$dhcp6range,constructor:$ifname,slaac,ra-names,$leasetime"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
dhcp_option_add "$cfg" "$networkid"
|
|
}
|
|
|
|
dhcp_option_add() {
|
|
# NOTE: dnsmasq has explicit "option6:" prefix for DHCPv6 so no collisions
|
|
local cfg="$1"
|
|
local networkid="$2"
|
|
local force="$3"
|
|
|
|
[ "$force" = "0" ] && force=
|
|
|
|
config_get dhcp_option "$cfg" dhcp_option
|
|
for o in $dhcp_option; do
|
|
xappend "--dhcp-option${force:+-force}=${networkid:+$networkid,}$o"
|
|
done
|
|
|
|
}
|
|
|
|
dhcp_domain_add() {
|
|
local cfg="$1"
|
|
local ip name names record
|
|
|
|
config_get names "$cfg" name "$2"
|
|
[ -n "$names" ] || return 0
|
|
|
|
config_get ip "$cfg" ip "$3"
|
|
[ -n "$ip" ] || return 0
|
|
|
|
for name in $names; do
|
|
record="${record:+$record }$name"
|
|
done
|
|
|
|
echo "$ip $record" >> $HOSTFILE
|
|
}
|
|
|
|
dhcp_srv_add() {
|
|
local cfg="$1"
|
|
|
|
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 class "$cfg" class
|
|
config_get weight "$cfg" weight
|
|
|
|
local service="$srv,$target,$port${class:+,$class${weight:+,$weight}}"
|
|
|
|
xappend "--srv-host=$service"
|
|
}
|
|
|
|
dhcp_mx_add() {
|
|
local cfg="$1"
|
|
local domain relay pref
|
|
|
|
config_get domain "$cfg" domain
|
|
[ -n "$domain" ] || return 0
|
|
|
|
config_get relay "$cfg" relay
|
|
[ -n "$relay" ] || return 0
|
|
|
|
config_get pref "$cfg" pref 0
|
|
|
|
local service="$domain,$relay,$pref"
|
|
|
|
xappend "--mx-host=$service"
|
|
}
|
|
|
|
dhcp_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
|
|
|
|
xappend "--cname=${cname},${target}"
|
|
}
|
|
|
|
dhcp_hostrecord_add() {
|
|
local cfg="$1"
|
|
local names addresses record val
|
|
|
|
config_get names "$cfg" name "$2"
|
|
if [ -z "$names" ]; then
|
|
return 0
|
|
fi
|
|
|
|
config_get addresses "$cfg" ip "$3"
|
|
if [ -z "$addresses" ]; then
|
|
return 0
|
|
fi
|
|
|
|
for val in $names $addresses; do
|
|
record="${record:+$record,}$val"
|
|
done
|
|
|
|
xappend "--host-record=$record"
|
|
}
|
|
|
|
dhcp_relay_add() {
|
|
local cfg="$1"
|
|
local local_addr server_addr interface
|
|
|
|
config_get local_addr "$cfg" local_addr
|
|
[ -n "$local_addr" ] || return 0
|
|
|
|
config_get server_addr "$cfg" server_addr
|
|
[ -n "$server_addr" ] || return 0
|
|
|
|
config_get interface "$cfg" interface
|
|
if [ -z "$interface" ]; then
|
|
xappend "--dhcp-relay=$local_addr,$server_addr"
|
|
else
|
|
xappend "--dhcp-relay=$local_addr,$server_addr,$interface"
|
|
fi
|
|
}
|
|
|
|
dnsmasq_start()
|
|
{
|
|
local cfg="$1" disabled
|
|
|
|
config_get_bool disabled "$cfg" disabled 0
|
|
[ "$disabled" -gt 0 ] && return 0
|
|
|
|
# reset list of DOMAINS and DNS servers (for each dnsmasq instance)
|
|
DNS_SERVERS=""
|
|
DOMAIN=""
|
|
CONFIGFILE="${BASECONFIGFILE}.${cfg}"
|
|
CONFIGFILE_TMP="${CONFIGFILE}.$$"
|
|
HOSTFILE="${BASEHOSTFILE}.${cfg}"
|
|
TIMESTAMPFILE="${BASETIMESTAMPFILE}.${cfg}"
|
|
BASEDHCPSTAMPFILE_CFG="${BASEDHCPSTAMPFILE}.${cfg}"
|
|
|
|
# before we can call xappend
|
|
mkdir -p /var/run/dnsmasq/
|
|
mkdir -p $(dirname $CONFIGFILE)
|
|
mkdir -p $(dirname $HOSTFILE)
|
|
mkdir -p /var/lib/misc
|
|
chown dnsmasq:dnsmasq /var/run/dnsmasq
|
|
|
|
[ -f "$TIMESTAMPFILE" ] && rm -f "$TIMESTAMPFILE"
|
|
|
|
echo "# auto-generated config file from /etc/config/dhcp" > $CONFIGFILE_TMP
|
|
echo "# auto-generated config file from /etc/config/dhcp" > $HOSTFILE
|
|
|
|
# if we did this last, we could override auto-generated config
|
|
[ -f /etc/dnsmasq.conf ] && {
|
|
xappend "--conf-file=/etc/dnsmasq.conf"
|
|
}
|
|
|
|
$PROG --version | grep -osqE "^Compile time options:.* DHCPv6( |$)" && DHCPv6CAPABLE=1 || DHCPv6CAPABLE=0
|
|
|
|
|
|
if [ -x /usr/sbin/odhcpd -a -x /etc/init.d/odhcpd ] ; then
|
|
local odhcpd_is_main odhcpd_is_enabled
|
|
config_get odhcpd_is_main odhcpd maindhcp
|
|
/etc/init.d/odhcpd enabled && odhcpd_is_enabled=1 || odhcpd_is_enabled=0
|
|
|
|
|
|
if [ "$odhcpd_is_enabled" -eq 0 -a "$DHCPv6CAPABLE" -eq 1 ] ; then
|
|
# DHCP V4 and V6 in DNSMASQ
|
|
DNSMASQ_DHCP_VER=6
|
|
elif [ "$odhcpd_is_main" -gt 0 ] ; then
|
|
# ODHCPD is doing it all
|
|
DNSMASQ_DHCP_VER=0
|
|
else
|
|
# You have ODHCPD but use DNSMASQ for DHCPV4
|
|
DNSMASQ_DHCP_VER=4
|
|
fi
|
|
|
|
elif [ "$DHCPv6CAPABLE" -eq 1 ] ; then
|
|
# DHCP V4 and V6 in DNSMASQ
|
|
DNSMASQ_DHCP_VER=6
|
|
else
|
|
DNSMASQ_DHCP_VER=4
|
|
fi
|
|
|
|
|
|
append_bool "$cfg" authoritative "--dhcp-authoritative"
|
|
append_bool "$cfg" nodaemon "--no-daemon"
|
|
append_bool "$cfg" domainneeded "--domain-needed"
|
|
append_bool "$cfg" filterwin2k "--filterwin2k"
|
|
append_bool "$cfg" nohosts "--no-hosts"
|
|
append_bool "$cfg" nonegcache "--no-negcache"
|
|
append_bool "$cfg" strictorder "--strict-order"
|
|
append_bool "$cfg" logqueries "--log-queries=extra"
|
|
append_bool "$cfg" noresolv "--no-resolv"
|
|
append_bool "$cfg" localise_queries "--localise-queries"
|
|
append_bool "$cfg" readethers "--read-ethers"
|
|
append_bool "$cfg" dbus "--enable-dbus"
|
|
append_bool "$cfg" boguspriv "--bogus-priv"
|
|
append_bool "$cfg" expandhosts "--expand-hosts"
|
|
config_get tftp_root "$cfg" "tftp_root"
|
|
[ -d "$tftp_root" ] && append_bool "$cfg" enable_tftp "--enable-tftp"
|
|
append_bool "$cfg" tftp_no_fail "--tftp-no-fail"
|
|
append_bool "$cfg" nonwildcard "--bind-dynamic"
|
|
append_bool "$cfg" fqdn "--dhcp-fqdn"
|
|
append_bool "$cfg" proxydnssec "--proxy-dnssec"
|
|
append_bool "$cfg" localservice "--local-service"
|
|
append_bool "$cfg" logdhcp "--log-dhcp"
|
|
append_bool "$cfg" quietdhcp "--quiet-dhcp"
|
|
append_bool "$cfg" sequential_ip "--dhcp-sequential-ip"
|
|
append_bool "$cfg" allservers "--all-servers"
|
|
append_bool "$cfg" noping "--no-ping"
|
|
|
|
append_parm "$cfg" logfacility "--log-facility"
|
|
append_parm "$cfg" dhcpscript "--dhcp-script"
|
|
append_parm "$cfg" cachesize "--cache-size"
|
|
append_parm "$cfg" dnsforwardmax "--dns-forward-max"
|
|
append_parm "$cfg" port "--port"
|
|
append_parm "$cfg" ednspacket_max "--edns-packet-max"
|
|
append_parm "$cfg" dhcpleasemax "--dhcp-lease-max"
|
|
append_parm "$cfg" "queryport" "--query-port"
|
|
append_parm "$cfg" "minport" "--min-port"
|
|
append_parm "$cfg" "maxport" "--max-port"
|
|
append_parm "$cfg" "domain" "--domain"
|
|
append_parm "$cfg" "local" "--server"
|
|
config_list_foreach "$cfg" "server" append_server
|
|
config_list_foreach "$cfg" "address" append_address
|
|
config_list_foreach "$cfg" "ipset" append_ipset
|
|
config_list_foreach "$cfg" "interface" append_interface
|
|
config_list_foreach "$cfg" "notinterface" append_notinterface
|
|
config_list_foreach "$cfg" "addnhosts" append_addnhosts
|
|
config_list_foreach "$cfg" "bogusnxdomain" append_bogusnxdomain
|
|
append_parm "$cfg" "leasefile" "--dhcp-leasefile" "/tmp/dhcp.leases"
|
|
append_parm "$cfg" "resolvfile" "--resolv-file" "/tmp/resolv.conf.auto"
|
|
append_parm "$cfg" "serversfile" "--servers-file"
|
|
append_parm "$cfg" "tftp_root" "--tftp-root"
|
|
append_parm "$cfg" "dhcp_boot" "--dhcp-boot"
|
|
append_parm "$cfg" "local_ttl" "--local-ttl"
|
|
append_parm "$cfg" "pxe_prompt" "--pxe-prompt"
|
|
config_list_foreach "$cfg" "pxe_service" append_pxe_service
|
|
config_get DOMAIN "$cfg" domain
|
|
|
|
config_get_bool ADD_LOCAL_DOMAIN "$cfg" add_local_domain 1
|
|
config_get_bool ADD_LOCAL_HOSTNAME "$cfg" add_local_hostname 1
|
|
config_get ADD_LOCAL_FQDN "$cfg" add_local_fqdn ""
|
|
config_get ADD_WAN_FQDN "$cfg" add_wan_fqdn 0
|
|
|
|
if [ -z "$ADD_LOCAL_FQDN" ] ; then
|
|
# maintain support for previous UCI
|
|
ADD_LOCAL_FQDN="$ADD_LOCAL_HOSTNAME"
|
|
fi
|
|
|
|
config_get_bool readethers "$cfg" readethers
|
|
[ "$readethers" = "1" -a \! -e "/etc/ethers" ] && touch /etc/ethers
|
|
|
|
config_get resolvfile $cfg resolvfile
|
|
config_get dhcpscript $cfg dhcpscript
|
|
|
|
config_get leasefile $cfg leasefile "/tmp/dhcp.leases"
|
|
[ -n "$leasefile" -a \! -e "$leasefile" ] && touch "$leasefile"
|
|
config_get_bool cachelocal "$cfg" cachelocal 1
|
|
|
|
config_get_bool noresolv "$cfg" noresolv 0
|
|
if [ "$noresolv" != "1" ]; then
|
|
config_get resolvfile "$cfg" resolvfile "/tmp/resolv.conf.auto"
|
|
# So jail doesn't complain if file missing
|
|
[ -n "$resolvfile" -a \! -e "$resolvfile" ] && touch "$resolvfile"
|
|
fi
|
|
|
|
config_get hostsfile "$cfg" dhcphostsfile
|
|
[ -e "$hostsfile" ] && xappend "--dhcp-hostsfile=$hostsfile"
|
|
|
|
local rebind
|
|
config_get_bool rebind "$cfg" rebind_protection 1
|
|
[ $rebind -gt 0 ] && {
|
|
log_once \
|
|
"DNS rebinding protection is active," \
|
|
"will discard upstream RFC1918 responses!"
|
|
xappend "--stop-dns-rebind"
|
|
|
|
local rebind_localhost
|
|
config_get_bool rebind_localhost "$cfg" rebind_localhost 0
|
|
[ $rebind_localhost -gt 0 ] && {
|
|
log_once "Allowing 127.0.0.0/8 responses"
|
|
xappend "--rebind-localhost-ok"
|
|
}
|
|
|
|
append_rebind_domain() {
|
|
log_once "Allowing RFC1918 responses for domain $1"
|
|
xappend "--rebind-domain-ok=$1"
|
|
}
|
|
|
|
config_list_foreach "$cfg" rebind_domain append_rebind_domain
|
|
}
|
|
|
|
config_get_bool dnssec "$cfg" dnssec 0
|
|
[ "$dnssec" -gt 0 ] && {
|
|
xappend "--conf-file=$TRUSTANCHORSFILE"
|
|
xappend "--dnssec"
|
|
[ -x /etc/init.d/sysntpd ] && {
|
|
/etc/init.d/sysntpd enabled
|
|
[ "$?" -ne 0 -o "$(uci_get system.ntp.enabled)" = "1" ] && {
|
|
[ -f "$TIMEVALIDFILE" ] || xappend "--dnssec-no-timecheck"
|
|
}
|
|
}
|
|
append_bool "$cfg" dnsseccheckunsigned "--dnssec-check-unsigned"
|
|
}
|
|
|
|
config_get addmac "$cfg" addmac 0
|
|
[ "$addmac" != "0" ] && {
|
|
[ "$addmac" = "1" ] && addmac=
|
|
xappend "--add-mac${addmac:+="$addmac"}"
|
|
}
|
|
|
|
dhcp_option_add "$cfg" "" 0
|
|
|
|
xappend "--dhcp-broadcast=tag:needs-broadcast"
|
|
|
|
xappend "--addn-hosts=$(dirname $HOSTFILE)"
|
|
|
|
config_get dnsmasqconfdir "$cfg" confdir "/tmp/dnsmasq.d"
|
|
[ ! -d "$dnsmasqconfdir" ] && mkdir -p $dnsmasqconfdir
|
|
xappend "--conf-dir=$dnsmasqconfdir"
|
|
xappend "--user=dnsmasq"
|
|
xappend "--group=dnsmasq"
|
|
echo >> $CONFIGFILE_TMP
|
|
|
|
config_get_bool enable_tftp "$cfg" enable_tftp 0
|
|
[ "$enable_tftp" -gt 0 ] && {
|
|
config_get tftp_root "$cfg" tftp_root
|
|
append EXTRA_MOUNT $tftp_root
|
|
}
|
|
|
|
config_foreach filter_dnsmasq host dhcp_host_add "$cfg"
|
|
echo >> $CONFIGFILE_TMP
|
|
config_foreach filter_dnsmasq boot dhcp_boot_add "$cfg"
|
|
config_foreach filter_dnsmasq mac dhcp_mac_add "$cfg"
|
|
config_foreach filter_dnsmasq tag dhcp_tag_add "$cfg"
|
|
config_foreach filter_dnsmasq vendorclass dhcp_vendorclass_add "$cfg"
|
|
config_foreach filter_dnsmasq userclass dhcp_userclass_add "$cfg"
|
|
config_foreach filter_dnsmasq circuitid dhcp_circuitid_add "$cfg"
|
|
config_foreach filter_dnsmasq remoteid dhcp_remoteid_add "$cfg"
|
|
config_foreach filter_dnsmasq subscrid dhcp_subscrid_add "$cfg"
|
|
config_foreach filter_dnsmasq match dhcp_match_add "$cfg"
|
|
config_foreach filter_dnsmasq domain dhcp_domain_add "$cfg"
|
|
config_foreach filter_dnsmasq hostrecord dhcp_hostrecord_add "$cfg"
|
|
config_foreach filter_dnsmasq relay dhcp_relay_add "$cfg"
|
|
|
|
echo >> $CONFIGFILE_TMP
|
|
config_foreach filter_dnsmasq srvhost dhcp_srv_add "$cfg"
|
|
config_foreach filter_dnsmasq mxhost dhcp_mx_add "$cfg"
|
|
echo >> $CONFIGFILE_TMP
|
|
|
|
|
|
if [ "$DNSMASQ_DHCP_VER" -gt 4 ] ; then
|
|
# Enable RA feature for when/if it is constructed,
|
|
# and RA is selected per interface pool (RA, DHCP, or both),
|
|
# but no one (should) want RA broadcast in syslog
|
|
config_foreach dhcp_add dhcp
|
|
xappend "--enable-ra"
|
|
xappend "--quiet-ra"
|
|
|
|
elif [ "$DNSMASQ_DHCP_VER" -gt 0 ] ; then
|
|
config_foreach dhcp_add dhcp
|
|
fi
|
|
|
|
|
|
echo >> $CONFIGFILE_TMP
|
|
config_foreach filter_dnsmasq cname dhcp_cname_add "$cfg"
|
|
echo >> $CONFIGFILE_TMP
|
|
|
|
echo >> $CONFIGFILE_TMP
|
|
mv -f $CONFIGFILE_TMP $CONFIGFILE
|
|
|
|
[ "$resolvfile" = "/tmp/resolv.conf.auto" ] && {
|
|
rm -f /tmp/resolv.conf
|
|
[ $ADD_LOCAL_DOMAIN -eq 1 ] && [ -n "$DOMAIN" ] && {
|
|
echo "search $DOMAIN" >> /tmp/resolv.conf
|
|
}
|
|
DNS_SERVERS="$DNS_SERVERS 127.0.0.1"
|
|
for DNS_SERVER in $DNS_SERVERS ; do
|
|
echo "nameserver $DNS_SERVER" >> /tmp/resolv.conf
|
|
done
|
|
}
|
|
|
|
procd_open_instance $cfg
|
|
procd_set_param command $PROG -C $CONFIGFILE -k -x /var/run/dnsmasq/dnsmasq."${cfg}".pid
|
|
procd_set_param file $CONFIGFILE
|
|
procd_set_param respawn
|
|
|
|
local dnsmasqconffile="/etc/dnsmasq.${cfg}.conf"
|
|
if [ ! -r "$dnsmasqconffile" ]; then
|
|
dnsmasqconffile=/etc/dnsmasq.conf
|
|
fi
|
|
|
|
procd_add_jail dnsmasq ubus log
|
|
procd_add_jail_mount $CONFIGFILE $TRUSTANCHORSFILE $HOSTFILE /etc/passwd /etc/group /etc/TZ /dev/null /dev/urandom $dnsmasqconffile $dnsmasqconfdir $resolvfile $dhcpscript /etc/hosts /etc/ethers $EXTRA_MOUNT
|
|
procd_add_jail_mount_rw /var/run/dnsmasq/ $leasefile
|
|
|
|
procd_close_instance
|
|
}
|
|
|
|
dnsmasq_stop()
|
|
{
|
|
local cfg="$1"
|
|
|
|
config_get resolvfile "$cfg" "resolvfile"
|
|
|
|
#relink /tmp/resolve.conf only for main instance
|
|
[ "$resolvfile" = "/tmp/resolv.conf.auto" ] && {
|
|
[ -f /tmp/resolv.conf ] && {
|
|
rm -f /tmp/resolv.conf
|
|
ln -s "$resolvfile" /tmp/resolv.conf
|
|
}
|
|
}
|
|
|
|
rm -f ${BASEDHCPSTAMPFILE}.${cfg}.*.dhcp
|
|
}
|
|
|
|
service_triggers()
|
|
{
|
|
procd_add_reload_trigger "dhcp"
|
|
procd_add_raw_trigger "interface.*" 2000 /etc/init.d/dnsmasq reload
|
|
}
|
|
|
|
boot()
|
|
{
|
|
BOOT=1
|
|
start "$@"
|
|
}
|
|
|
|
start_service() {
|
|
local instance="$1"
|
|
local instance_found=0
|
|
|
|
[ -n "$BOOT" ] && return
|
|
|
|
. /lib/functions/network.sh
|
|
|
|
config_cb() {
|
|
local type="$1"
|
|
local name="$2"
|
|
if [ "$type" = "dnsmasq" ]; then
|
|
if [ -n "$instance" -a "$instance" = "$name" ]; then
|
|
instance_found=1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
config_load dhcp
|
|
|
|
if [ -n "$instance" ]; then
|
|
[ "$instance_found" -gt 0 ] || return
|
|
dnsmasq_start "$instance"
|
|
else
|
|
config_foreach dnsmasq_start dnsmasq
|
|
fi
|
|
}
|
|
|
|
reload_service() {
|
|
rc_procd start_service "$@"
|
|
return 0
|
|
}
|
|
|
|
stop_service() {
|
|
local instance="$1"
|
|
local instance_found=0
|
|
|
|
config_cb() {
|
|
local type="$1"
|
|
local name="$2"
|
|
if [ "$type" = "dnsmasq" ]; then
|
|
if [ -n "$instance" -a "$instance" = "$name" ]; then
|
|
instance_found=1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
config_load dhcp
|
|
|
|
if [ -n "$instance" ]; then
|
|
[ "$instance_found" -gt 0 ] || return
|
|
dnsmasq_stop "$instance"
|
|
else
|
|
config_foreach dnsmasq_stop dnsmasq
|
|
fi
|
|
}
|