DNSMASQ Pure Insidious Evil on OpenWRT

Wiki.TerraBase.info
Jump to navigation Jump to search

What happens if you have an OpenWRT Router that is configured to use a DNS Server external to itself. Is DNSMASQ involved? Doesn't seem like it should be. Doesn't seem like it needs to be either. BUT IT DOES! AND IT IS!

WTF? Really? Yup!

Internal DNS

What happens if you want to create an internal (IE, relative to a local area network) DNS infrastructure with TLDs ("Top Level Domains") like .LAN or .local? That's fine. That's legitimate. But what will and won't work? Well, any sort of name resolution for .LAN or .local or anything else that isn't an 'official TLD'.

Back to WTF

Well, it turns out, the /etc/init.d/dnsmasq file contains scripts that hijack the way DNS should work. Even erasing the entire /etc/config/dhcp (yup, that another interesting thing right there, for DNS, go to the dhcp config file, and yes, DNSMASQ does do both DHCP and DNS) will not resolve the issue. It is the

It turns out that DNSMASQ, when running and even if not explicitly configured as the DNS server for a router, is still inserting itself into the DNS resolution chain as a forwarder.

DNSMASQ and resolv.conf

Below is how DNSMASQ is still 'working' even though it seems like an external DNS server has been configured.

The /etc/resolve.conf (/tmp/resolv.conf) File will look like the following;

search lan
nameserver 127.0.0.1
nameserver ::1

If DNSMASQ is stopped or disabled, it looks like the following;

# Interface lan
nameserver 10.100.100.254

The Evidence

Stop the DNSMASQ Service

Run DNSMASQ at the command line: /usr/sbin/dnsmasq --no-daemon --log-queries=extra --log-dhcp --log-debug -C /var/etc/dnsmasq.conf.cfg01411c -k -x /var/run/dnsmasq/dn

smasq.cfg01411c.pid

Below is an example of pinging nfl.com and a .lan "Domain Name"

dnsmasq: UBus support enabled: connected to system bus
dnsmasq: using only locally-known addresses for test
dnsmasq: using only locally-known addresses for onion
dnsmasq: using only locally-known addresses for localhost
dnsmasq: using only locally-known addresses for local
dnsmasq: using only locally-known addresses for invalid
dnsmasq: using only locally-known addresses for bind
dnsmasq: reading /tmp/resolv.conf.d/resolv.conf.auto
dnsmasq: using nameserver 10.100.100.254#53
dnsmasq: using only locally-known addresses for test
dnsmasq: using only locally-known addresses for onion
dnsmasq: using only locally-known addresses for localhost
dnsmasq: using only locally-known addresses for local
dnsmasq: using only locally-known addresses for invalid
dnsmasq: using only locally-known addresses for bind
dnsmasq: read /etc/hosts - 6 names
dnsmasq: read /tmp/hosts/dhcp.cfg01411c - 0 names
dnsmasq: read /tmp/hosts/odhcpd - 0 names
dnsmasq: 1 127.0.0.1/54110 query[A] nfl.com from 127.0.0.1
dnsmasq: 1 127.0.0.1/54110 forwarded nfl.com to 10.100.100.254
dnsmasq: 2 ::1/54110 query[A] nfl.com from ::1
dnsmasq: 3 ::1/54110 query[AAAA] nfl.com from ::1
dnsmasq: 3 ::1/54110 forwarded nfl.com to 10.100.100.254
dnsmasq: 4 127.0.0.1/54110 query[AAAA] nfl.com from 127.0.0.1
dnsmasq: 1 127.0.0.1/54110 reply nfl.com is 151.101.129.153
dnsmasq: 1 127.0.0.1/54110 reply nfl.com is 151.101.193.153
dnsmasq: 1 127.0.0.1/54110 reply nfl.com is 151.101.1.153
dnsmasq: 1 127.0.0.1/54110 reply nfl.com is 151.101.65.153
dnsmasq: 2 ::1/54110 reply query is duplicate
dnsmasq: 3 ::1/54110 reply nfl.com is NODATA-IPv6
dnsmasq: 4 127.0.0.1/54110 reply query is duplicate
dnsmasq: 5 ::1/59411 query[PTR] 153.129.101.151.in-addr.arpa from ::1
dnsmasq: 5 ::1/59411 forwarded 153.129.101.151.in-addr.arpa to 10.100.100.254
dnsmasq: 6 127.0.0.1/59411 query[PTR] 153.129.101.151.in-addr.arpa from 127.0.0.1
dnsmasq: 5 ::1/59411 reply 151.101.129.153 is NXDOMAIN
dnsmasq: 6 127.0.0.1/59411 reply query is duplicate
dnsmasq: 7 ::1/42558 query[PTR] 153.129.101.151.in-addr.arpa from ::1
dnsmasq: 7 ::1/42558 cached 151.101.129.153 is NXDOMAIN
dnsmasq: 8 127.0.0.1/42558 query[PTR] 153.129.101.151.in-addr.arpa from 127.0.0.1
dnsmasq: 8 127.0.0.1/42558 cached 151.101.129.153 is NXDOMAIN
dnsmasq: 9 ::1/60490 query[PTR] 153.129.101.151.in-addr.arpa from ::1
dnsmasq: 9 ::1/60490 cached 151.101.129.153 is NXDOMAIN
dnsmasq: 10 127.0.0.1/60490 query[PTR] 153.129.101.151.in-addr.arpa from 127.0.0.1
dnsmasq: 10 127.0.0.1/60490 cached 151.101.129.153 is NXDOMAIN
dnsmasq: 11 ::1/40424 query[PTR] 153.129.101.151.in-addr.arpa from ::1
dnsmasq: 11 ::1/40424 cached 151.101.129.153 is NXDOMAIN
dnsmasq: 12 127.0.0.1/40424 query[PTR] 153.129.101.151.in-addr.arpa from 127.0.0.1
dnsmasq: 12 127.0.0.1/40424 cached 151.101.129.153 is NXDOMAIN
dnsmasq: 13 ::1/56895 query[PTR] 153.129.101.151.in-addr.arpa from ::1
dnsmasq: 13 ::1/56895 cached 151.101.129.153 is NXDOMAIN
dnsmasq: 14 127.0.0.1/56895 query[PTR] 153.129.101.151.in-addr.arpa from 127.0.0.1
dnsmasq: 14 127.0.0.1/56895 cached 151.101.129.153 is NXDOMAIN




dnsmasq: 15 127.0.0.1/53548 query[A] osm.lan from 127.0.0.1
dnsmasq: 15 127.0.0.1/53548 forwarded osm.lan to 10.100.100.254
dnsmasq: 16 ::1/53548 query[A] osm.lan from ::1
dnsmasq: 17 ::1/53548 query[AAAA] osm.lan from ::1
dnsmasq: 17 ::1/53548 forwarded osm.lan to 10.100.100.254
dnsmasq: 18 127.0.0.1/53548 query[AAAA] osm.lan from 127.0.0.1
dnsmasq: possible DNS-rebind attack detected: osm.lan
dnsmasq: 16 ::1/53548 reply query is duplicate
dnsmasq: 17 ::1/53548 reply osm.lan is NODATA-IPv6
dnsmasq: 18 127.0.0.1/53548 reply query is duplicate

See? Forwarding queries. And, the /var/etc/dnsmasqX.conf File created and sourced from /etc/config/dhcp;

# auto-generated config file from /etc/config/dhcp
conf-file=/etc/dnsmasq.conf
enable-ubus=dnsmasq
bind-dynamic
addn-hosts=/tmp/hosts
dhcp-leasefile=/tmp/dhcp.leases
resolv-file=/tmp/resolv.conf.d/resolv.conf.auto
stop-dns-rebind
dhcp-broadcast=tag:needs-broadcast
conf-dir=/tmp/dnsmasq.d
user=dnsmasq
group=dnsmasq


dhcp-ignore-names=tag:dhcp_bogus_hostname
conf-file=/usr/share/dnsmasq/dhcpbogushostname.conf


bogus-priv
conf-file=/usr/share/dnsmasq/rfc6761.conf

See the stop-dns-rebind directive? That's what causes the .LAN or .local or whatever made up "TLD" to fail.

Bottom Line

The DNSMASQ Configuration in OpenWRT modifies the /etc/resolv.conf file to use DNSMASQ as a forwarder and to also 'block' custom "TLD" Domain Names. It views it as a DNS Rebind attack. You know, the type of attack Firefox, Chrome, and all other browsers protect against too. Which of course leaves DNSMASQ just making wild guesses about local IP addresses with zero context. IE, how on this planet will the PING or DIG command compromise a router or computer get 'attacked' or infected if it visits a malicious website that implements DNS Rebind attacks? Answer: It won't. Sometimes there is too much protection.

Want proof? Below is the reply of ChatGPT to this situation;

OpenWRT's use of stop-dns-rebind is overly aggressive and unnecessary in contexts like using the ping command, where DNS rebinding attacks aren't even possible, because no scriptable or origin-bound activity occurs.

And remember, ChatGPT is dumb as a forking stump, so if it gets it, anyone should be able to get it.

Follow Up

Remember, the title is DNSMASQ Pure Insidious Evil on OpenWRT. So DNSMASQ itself is not evil, but rather DNSMASQ on OpenWRT is. Why, because it is the /etc/init.d/dnsmasq in OpenWRT that makes DNSMASQ act the way it does;

#!/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"
TRUSTANCHORSFILE="/usr/share/dnsmasq/trust-anchors.conf"
TIMEVALIDFILE="/var/state/dnsmasqsec"
BASEDHCPSTAMPFILE="/var/run/dnsmasq"
DHCPBOGUSHOSTNAMEFILE="/usr/share/dnsmasq/dhcpbogushostname.conf"
RFC6761FILE="/usr/share/dnsmasq/rfc6761.conf"
DHCPSCRIPT="/usr/lib/dnsmasq/dhcp-script.sh"

DNSMASQ_DHCP_VER=4

dnsmasq_ignore_opt() {
	local opt="$1"

	if [ -z "$dnsmasq_features" ]; then
		dnsmasq_features="$(dnsmasq --version | grep -m1 'Compile time options:' | cut -d: -f2) "
		[ "${dnsmasq_features#* DHCP }" = "$dnsmasq_features" ] || dnsmasq_has_dhcp=1
		[ "${dnsmasq_features#* DHCPv6 }" = "$dnsmasq_features" ] || dnsmasq_has_dhcp6=1
		[ "${dnsmasq_features#* DNSSEC }" = "$dnsmasq_features" ] || dnsmasq_has_dnssec=1
		[ "${dnsmasq_features#* TFTP }" = "$dnsmasq_features" ] || dnsmasq_has_tftp=1
		[ "${dnsmasq_features#* ipset }" = "$dnsmasq_features" ] || dnsmasq_has_ipset=1
	fi

	case "$opt" in
		dhcp-duid|\
		ra-param)
			[ -z "$dnsmasq_has_dhcp6" ] ;;
		dhcp-*|\
		bootp-*|\
		pxe-*)
			[ -z "$dnsmasq_has_dhcp" ] ;;
		dnssec-*|\
		trust-anchor)
			[ -z "$dnsmasq_has_dnssec" ] ;;
		tftp-*)
			[ -z "$dnsmasq_has_tftp" ] ;;
		ipset)
			[ -z "$dnsmasq_has_ipset" ] ;;
		*)
			return 1
	esac
}

xappend() {
	local value="${1#--}"
	local opt="${value%%=*}"

	if ! dnsmasq_ignore_opt "$opt"; then
		echo "$value" >>$CONFIGFILE_TMP
	fi
}

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 "$@"
}

has_handler() {
	local file

	for file in /etc/hotplug.d/dhcp/* /etc/hotplug.d/tftp/* /etc/hotplug.d/neigh/*; do
		[ -f "$file" ] && return 0
	done

	return 1
}

append_bool() {
	local section="$1"
	local option="$2"
	local value="$3"
	local default="$4"
	local _loctmp
	[ -z "$default" ] && default="0"
	config_get_bool _loctmp "$section" "$option" "$default"
	[ $_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_rev_server() {
        xappend "--rev-server=$1"
}

append_address() {
	xappend "--address=$1"
}

append_ipset() {
	xappend "--ipset=$1"
}

append_interface() {
	network_get_device ifname "$1" || ifname="$1"
	xappend "--interface=$ifname"
}

append_listenaddress() {
	xappend "--listen-address=$1"
}

append_notinterface() {
	network_get_device ifname "$1" || ifname="$1"
	xappend "--except-interface=$ifname"
}

append_addnhosts() {
	xappend "--addn-hosts=$1"
}

append_bogusnxdomain() {
	xappend "--bogus-nxdomain=$1"
}

append_pxe_service() {
	xappend "--pxe-service=$1"
}

append_interface_name() {
	xappend "--interface-name=$1,$2"
}

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 duids macs tags

	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_TMP
	}

	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
		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

	if [ -n "$tag" ]; then
		for t in $tag; do append tags "$t" ",set:"; done
	fi

	config_get_bool broadcast "$cfg" broadcast 0
	config_get leasetime "$cfg" leasetime

	[ "$broadcast" = "0" ] && broadcast= || broadcast=",set:needs-broadcast"

	hosttag="${networkid:+,set:${networkid}}${tags:+,set:${tags}}$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
					append_interface_name "$ifdashname.$routername.$DOMAIN" "$ifname"
				fi

				append_interface_name "$routername.$DOMAIN" "$ifname"
			fi

			# All IP addresses discovered by dnsmasq will be labeled (except fe80::)
			append_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
	local tags

	config_get net "$cfg" interface
	[ -n "$net" ] || return 0

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || networkid="$net"

	network_get_device ifname "$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
	}

	network_get_subnet subnet "$net" || return 0
	network_get_protocol proto "$net" || 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
	config_get dns "$cfg" dns

	config_list_foreach "$cfg" "interface_name" append_interface_name "$ifname"

	# 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" )"

	add_tag() {
		tags="${tags}tag:$1,"
	}
	config_list_foreach "$cfg" tag add_tag

	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=$tags$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

		if [ -n "$dns" ]; then
			dnss=""
			for d in $dns; do append dnss "[$d]" ","; done
		else
			dnss="[::]"
		fi

		dhcp_option_append "option6:dns-server,$dnss" "$networkid"
	fi

	dhcp_option_add "$cfg" "$networkid" 0
	dhcp_option_add "$cfg" "$networkid" 2
}

dhcp_option_append() {
	local option="$1"
	local networkid="$2"
	local force="$3"

	xappend "--dhcp-option${force:+-force}=${networkid:+$networkid,}$option"
}

dhcp_option_add() {
	# NOTE: dnsmasq has explicit "option6:" prefix for DHCPv6 so no collisions
	local cfg="$1"
	local networkid="$2"
	local force="$3"
	local opt="dhcp_option"

	[ "$force" = "0" ] && force=
	[ "$force" = "2" ] && opt="dhcp_option_force"

	local list_len
	config_get list_len "$cfg" "${opt}_LENGTH"

	if [ -n "$list_len" ]; then
		config_list_foreach "$cfg" "$opt" dhcp_option_append "$networkid" "$force"
	else
		config_get dhcp_option "$cfg" "$opt"

		[ -n "$dhcp_option" ] && echo "Warning: the 'option $opt' syntax is deprecated, use 'list $opt'" >&2

		local option
		for option in $dhcp_option; do
			dhcp_option_append "$option" "$networkid" "$force"
		done
	fi
}

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_TMP
}

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
		network_get_device ifname "$interface" || return
		xappend "--dhcp-relay=$local_addr,$server_addr,$ifname"
	fi
}

dnsmasq_start()
{
	local cfg="$1"
	local disabled user_dhcpscript
	local resolvfile localuse=0

	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}"
	HOSTFILE_TMP="${HOSTFILE}.$$"
	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

	echo "# auto-generated config file from /etc/config/dhcp" > $CONFIGFILE_TMP
	echo "# auto-generated config file from /etc/config/dhcp" > $HOSTFILE_TMP

	local dnsmasqconffile="/etc/dnsmasq.${cfg}.conf"
	if [ ! -r "$dnsmasqconffile" ]; then
		dnsmasqconffile=/etc/dnsmasq.conf
	fi

	# if we did this last, we could override auto-generated config
	[ -f "${dnsmasqconffile}" ] && {
		xappend "--conf-file=${dnsmasqconffile}"
	}

	$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 0
		/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

	# Allow DHCP/DHCPv6 to be handled by ISC DHCPD
	if [ -x /usr/sbin/dhcpd ] ; then
		if [ -x /etc/init.d/dhcpd ] ; then
			/etc/init.d/dhcpd enabled && DNSMASQ_DHCP_VER=0
		fi
		if [ -x /etc/init.d/dhcpd6 -a "$DNSMASQ_DHCP_VER" -gt 0 ] ; then
			/etc/init.d/dhcpd6 enabled && DNSMASQ_DHCP_VER=4
		fi
	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" ubus "--enable-ubus"	1
	append_bool "$cfg" expandhosts "--expand-hosts"
	config_get tftp_root "$cfg" "tftp_root"
	[ -n "$tftp_root" ] && mkdir -p "$tftp_root" && append_bool "$cfg" enable_tftp "--enable-tftp"
	append_bool "$cfg" tftp_no_fail "--tftp-no-fail"
	append_bool "$cfg" nonwildcard "--bind-dynamic" 1
	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_bool "$cfg" rapidcommit "--dhcp-rapid-commit"
	append_bool "$cfg" scriptarp "--script-arp"

	append_parm "$cfg" logfacility "--log-facility"

	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" "listen_address" append_listenaddress
	config_list_foreach "$cfg" "server" append_server
	config_list_foreach "$cfg" "rev_server" append_rev_server
	config_list_foreach "$cfg" "address" append_address
	config_list_foreach "$cfg" "ipset" append_ipset
	[ -n "$BOOT" ] || {
		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" "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" "max_ttl" "--max-ttl"
	append_parm "$cfg" "min_cache_ttl" "--min-cache-ttl"
	append_parm "$cfg" "max_cache_ttl" "--max-cache-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 user_dhcpscript $cfg dhcpscript
	if has_handler || [ -n "$user_dhcpscript" ]; then
		xappend "--dhcp-script=$DHCPSCRIPT"
		xappend "--script-arp"
	fi

	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
		[ -n "$resolvfile" -a ! -e "$resolvfile" ] && touch "$resolvfile"
		xappend "--resolv-file=$resolvfile"
		[ "$resolvfile" = "/tmp/resolv.conf.auto" ] && localuse=1
	fi
	config_get_bool localuse "$cfg" localuse "$localuse"

	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 ] && {
			if /etc/init.d/sysntpd enabled || [ "$(uci_get system.ntp.enabled)" = "1" ] ; then
				[ -f "$TIMEVALIDFILE" ] || xappend "--dnssec-no-timecheck"
			fi
		}
		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
	dhcp_option_add "$cfg" "" 2

	xappend "--dhcp-broadcast=tag:needs-broadcast"

	xappend "--addn-hosts=$(dirname $HOSTFILE)"

	config_get dnsmasqconfdir "$cfg" confdir "/tmp/dnsmasq.d"
	xappend "--conf-dir=$dnsmasqconfdir"
	dnsmasqconfdir="${dnsmasqconfdir%%,*}"
	[ ! -d "$dnsmasqconfdir" ] && mkdir -p $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_get_bool dhcpbogushostname "$cfg" dhcpbogushostname 1
	[ "$dhcpbogushostname" -gt 0 ] && {
		xappend "--dhcp-ignore-names=tag:dhcp_bogus_hostname"
		[ -r "$DHCPBOGUSHOSTNAMEFILE" ] && xappend "--conf-file=$DHCPBOGUSHOSTNAMEFILE"
	}

	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"
	[ -n "$BOOT" ] || 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

	config_get_bool boguspriv "$cfg" boguspriv 1
	[ "$boguspriv" -gt 0 ] && {
		xappend "--bogus-priv"
		[ -r "$RFC6761FILE" ] && xappend "--conf-file=$RFC6761FILE"
	}

	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
		[ -n "$BOOT" ] || config_foreach filter_dnsmasq dhcp dhcp_add "$cfg"
		xappend "--enable-ra"
		xappend "--quiet-ra"
		append_bool "$cfg" quietdhcp "--quiet-dhcp6"

	elif [ "$DNSMASQ_DHCP_VER" -gt 0 ] ; then
		[ -n "$BOOT" ] || config_foreach filter_dnsmasq dhcp dhcp_add "$cfg"
	fi


	echo >> $CONFIGFILE_TMP
	config_foreach filter_dnsmasq cname dhcp_cname_add "$cfg"
	echo >> $CONFIGFILE_TMP

	echo >> $CONFIGFILE_TMP
	mv -f $CONFIGFILE_TMP $CONFIGFILE
	mv -f $HOSTFILE_TMP $HOSTFILE

	[ "$localuse" -gt 0 ] && {
		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
	[ -n "$user_dhcpscript" ] && procd_set_param env USER_DHCPSCRIPT="$user_dhcpscript"
	procd_set_param respawn

	procd_add_jail dnsmasq ubus log
	procd_add_jail_mount $CONFIGFILE $TRUSTANCHORSFILE $HOSTFILE $RFC6761FILE $DHCPBOGUSHOSTNAMEFILE /etc/passwd /etc/group /etc/TZ /dev/null /dev/urandom $dnsmasqconffile $dnsmasqconfdir $resolvfile $user_dhcpscript /etc/hosts /etc/ethers /sbin/hotplug-call $EXTRA_MOUNT $DHCPSCRIPT
	procd_add_jail_mount_rw /var/run/dnsmasq/ $leasefile

	procd_close_instance
}

dnsmasq_stop()
{
	local cfg="$1"
	local noresolv resolvfile localuse=0

	config_get_bool noresolv "$cfg" noresolv 0
	config_get resolvfile "$cfg" "resolvfile"

	[ "$noresolv" = 0 -a "$resolvfile" = "/tmp/resolv.conf.auto" ] && localuse=1
	config_get_bool localuse "$cfg" localuse "$localuse"
	[ "$localuse" -gt 0 ] && ln -sf "/tmp/resolv.conf.auto" /tmp/resolv.conf

	rm -f ${BASEDHCPSTAMPFILE}.${cfg}.*.dhcp
}

add_interface_trigger()
{
	local interface ignore

	config_get interface "$1" interface
	config_get_bool ignore "$1" ignore 0

	[ -n "$interface" -a $ignore -eq 0 ] && procd_add_interface_trigger "interface.*" "$interface" /etc/init.d/dnsmasq reload
}

service_triggers()
{
	procd_add_reload_trigger "dhcp" "system"

	config_load dhcp
	config_foreach add_interface_trigger dhcp
	config_foreach add_interface_trigger relay
}

boot()
{
	BOOT=1
	start "$@"
}

start_service() {
	local instance="$1"
	local instance_found=0

	. /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 "$@"
	procd_send_signal dnsmasq "$@"
}

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
}