Browse Source

Bump dnsmasq version to v2.78 to fix several CVEs Fixes CVE-2017-14491, CVE-2017-14492, CVE-2017-14493, CVE-2017-14494, 2017-CVE-14495, 2017-CVE-14496

RISCi_ATOM 6 years ago
parent
commit
aa52029f6e

+ 16 - 6
package/network/services/dnsmasq/Makefile

@@ -8,12 +8,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=dnsmasq
-PKG_VERSION:=2.77
-PKG_RELEASE:=6
+PKG_VERSION:=2.78
+PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
 PKG_SOURCE_URL:=http://thekelleys.org.uk/dnsmasq/
-PKG_HASH:=6eac3b1c50ae25170e3ff8c96ddb55236cf45007633fdb8a35b1f3e02f5f8b8a
+PKG_HASH:=89949f438c74b0c7543f06689c319484bd126cc4b1f8c745c742ab397681252b
 
 PKG_LICENSE:=GPL-2.0
 PKG_LICENSE_FILES:=COPYING
@@ -37,6 +37,7 @@ define Package/dnsmasq/Default
   CATEGORY:=Base system
   TITLE:=DNS and DHCP server
   URL:=http://www.thekelleys.org.uk/dnsmasq/
+  DEPENDS:=+libubus
   USERID:=dnsmasq=453:dnsmasq=453
 endef
 
@@ -48,14 +49,14 @@ endef
 define Package/dnsmasq-dhcpv6
 $(call Package/dnsmasq/Default)
   TITLE += (with DHCPv6 support)
-  DEPENDS:=@IPV6
+  DEPENDS+=@IPV6
   VARIANT:=dhcpv6
 endef
 
 define Package/dnsmasq-full
 $(call Package/dnsmasq/Default)
   TITLE += (with DNSSEC, DHCPv6, Auth DNS, IPset, Conntrack, NO_ID enabled by default)
-  DEPENDS:=+PACKAGE_dnsmasq_full_dnssec:libnettle \
+  DEPENDS+=+PACKAGE_dnsmasq_full_dnssec:libnettle \
 	+PACKAGE_dnsmasq_full_ipset:kmod-ipt-ipset \
 	+PACKAGE_dnsmasq_full_conntrack:libnetfilter-conntrack
   VARIANT:=full
@@ -137,7 +138,7 @@ endif
 
 MAKE_FLAGS := \
 	$(TARGET_CONFIGURE_OPTS) \
-	CFLAGS="$(TARGET_CFLAGS)" \
+	CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \
 	LDFLAGS="$(TARGET_LDFLAGS)" \
 	COPTS="$(COPTS)" \
 	PREFIX="/usr"
@@ -150,8 +151,17 @@ define Package/dnsmasq/install
 	$(INSTALL_DATA) ./files/dnsmasq.conf $(1)/etc/dnsmasq.conf
 	$(INSTALL_DIR) $(1)/etc/init.d
 	$(INSTALL_BIN) ./files/dnsmasq.init $(1)/etc/init.d/dnsmasq
+	$(INSTALL_DIR) $(1)/etc/hotplug.d/dhcp
+	$(INSTALL_DIR) $(1)/etc/hotplug.d/neigh
 	$(INSTALL_DIR) $(1)/etc/hotplug.d/ntp
+	$(INSTALL_DIR) $(1)/etc/hotplug.d/tftp
 	$(INSTALL_DATA) ./files/dnsmasqsec.hotplug $(1)/etc/hotplug.d/ntp/25-dnsmasqsec
+	$(INSTALL_DIR) $(1)/usr/share/dnsmasq
+	$(INSTALL_DATA) ./files/rfc6761.conf $(1)/usr/share/dnsmasq/
+	$(INSTALL_DIR) $(1)/usr/lib/dnsmasq
+	$(INSTALL_BIN) ./files/dhcp-script.sh $(1)/usr/lib/dnsmasq/dhcp-script.sh
+	$(INSTALL_DIR) $(1)/usr/share/acl.d
+	$(INSTALL_DATA) ./files/dnsmasq_acl.json $(1)/usr/share/acl.d/
 endef
 
 Package/dnsmasq-dhcpv6/install = $(Package/dnsmasq/install)

+ 46 - 0
package/network/services/dnsmasq/files/dhcp-script.sh

@@ -0,0 +1,46 @@
+#!/bin/sh
+
+[ -f "$USER_DHCPSCRIPT" ] && . "$USER_DHCPSCRIPT" "$@"
+
+case "$1" in
+	add)
+		export ACTION="add"
+		export MACADDR="$2"
+		export IPADDR="$3"
+		export HOSTNAME="$4"
+		exec /sbin/hotplug-call dhcp
+	;;
+	del)
+		export ACTION="remove"
+		export MACADDR="$2"
+		export IPADDR="$3"
+		export HOSTNAME="$4"
+		exec /sbin/hotplug-call dhcp
+	;;
+	old)
+		export ACTION="update"
+		export MACADDR="$2"
+		export IPADDR="$3"
+		export HOSTNAME="$4"
+		exec /sbin/hotplug-call dhcp
+	;;
+	arp-add)
+		export ACTION="add"
+		export MACADDR="$2"
+		export IPADDR="$3"
+		exec /sbin/hotplug-call neigh
+	;;
+	arp-del)
+		export ACTION="remove"
+		export MACADDR="$2"
+		export IPADDR="$3"
+		exec /sbin/hotplug-call neigh
+	;;
+	tftp)
+		export ACTION="add"
+		export TFTP_SIZE="$2"
+		export TFTP_ADDR="$3"
+		export TFTP_PATH="$4"
+		exec /sbin/hotplug-call tftp
+	;;
+esac

+ 1 - 1
package/network/services/dnsmasq/files/dhcp.conf

@@ -15,7 +15,7 @@ config dnsmasq
 	option leasefile	'/tmp/dhcp.leases'
 	option resolvfile	'/tmp/resolv.conf.auto'
 	#list server		'/mycompany.local/1.2.3.4'
-	#option nonwildcard	1
+	option nonwildcard	1 # bind to & keep track of interfaces
 	#list interface		br-lan
 	#list notinterface	lo
 	#list bogusnxdomain     '64.94.110.11'

+ 304 - 67
package/network/services/dnsmasq/files/dnsmasq.init

@@ -8,6 +8,8 @@ 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"
@@ -15,6 +17,10 @@ BASETIMESTAMPFILE="/etc/dnsmasq.time"
 TRUSTANCHORSFILE="/usr/share/dnsmasq/trust-anchors.conf"
 TIMEVALIDFILE="/var/state/dnsmasqsec"
 BASEDHCPSTAMPFILE="/var/run/dnsmasq"
+RFC6761FILE="/usr/share/dnsmasq/rfc6761.conf"
+DHCPSCRIPT="/usr/lib/dnsmasq/dhcp-script.sh"
+
+DNSMASQ_DHCP_VER=4
 
 xappend() {
 	local value="$1"
@@ -83,12 +89,24 @@ log_once() {
 		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
-	config_get_bool _loctmp "$section" "$option" 0
+	[ -z "$default" ] && default="0"
+	config_get_bool _loctmp "$section" "$option" "$default"
 	[ $_loctmp -gt 0 ] && xappend "$value"
 }
 
@@ -107,6 +125,10 @@ append_server() {
 	xappend "--server=$1"
 }
 
+append_rev_server() {
+        xappend "--rev-server=$1"
+}
+
 append_address() {
 	xappend "--address=$1"
 }
@@ -116,12 +138,12 @@ append_ipset() {
 }
 
 append_interface() {
-	network_get_device ifname "$1" || return
+	network_get_device ifname "$1" || ifname="$1"
 	xappend "--interface=$ifname"
 }
 
 append_notinterface() {
-	network_get_device ifname "$1" || return
+	network_get_device ifname "$1" || ifname="$1"
 	xappend "--except-interface=$ifname"
 }
 
@@ -137,6 +159,10 @@ 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
 
@@ -180,6 +206,7 @@ dhcp_remoteid_add() {
 }
 
 dhcp_circuitid_add() {
+	# TODO: DHCPV6 does not have circuitid; catch "option6:"
 	local cfg="$1"
 
 	config_get networkid "$cfg" networkid
@@ -212,6 +239,7 @@ dhcp_userclass_add() {
 }
 
 dhcp_vendorclass_add() {
+	# TODO: DHCPV6 vendor class has stricter definitions; catch? fixup?
 	local cfg="$1"
 
 	config_get networkid "$cfg" networkid
@@ -245,6 +273,7 @@ dhcp_match_add() {
 
 dhcp_host_add() {
 	local cfg="$1"
+	local hosttag nametime addrs duids
 
 	config_get_bool force "$cfg" force 0
 
@@ -256,7 +285,9 @@ dhcp_host_add() {
 
 	config_get name "$cfg" name
 	config_get ip "$cfg" ip
-	[ -n "$ip" -o -n "$name" ] || return 0
+	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" ] && {
@@ -264,36 +295,105 @@ dhcp_host_add() {
 	}
 
 	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
-	else
-		# --dhcp-host=lap,192.168.0.199
+	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
 
-	config_get tag "$cfg" tag
+	if [ -n "$hostid" ]; then
+		hex_to_hostid hostid "$hostid"
+	fi
 
-	if [ "$DHCPv6CAPABLE" -eq 1 ]; then
-		config_get duid "$cfg" duid
-		config_get hostid "$cfg" hostid
-		if [ -n "$hostid" ]; then
-			hex_to_hostid hostid "$hostid"
-		fi
+	tags=""
+	if [ -n "$tag" ]; then
+		for t in $tag; do append tags "$t" ",set:"; done
 	fi
 
 	config_get_bool broadcast "$cfg" broadcast 0
-	[ "$broadcast" = "0" ] && broadcast=
-
 	config_get leasetime "$cfg" leasetime
 
-	xappend "--dhcp-host=$macs${duid:+,id:$duid}${networkid:+,net:$networkid}${broadcast:+,set:needs-broadcast}${tag:+,set:$tag}${ip:+,$ip${hostid:+,[::$hostid]}}${name:+,$name}${leasetime:+,$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"
@@ -324,6 +424,7 @@ dhcp_mac_add() {
 }
 
 dhcp_boot_add() {
+	# TODO: BOOTURL is different between DHCPv4 and DHCPv6
 	local cfg="$1"
 
 	config_get networkid "$cfg" networkid
@@ -346,24 +447,30 @@ dhcp_boot_add() {
 
 dhcp_add() {
 	local cfg="$1"
+	local dhcp6range="::"
+	local nettag
+	local tags
+
 	config_get net "$cfg" interface
 	[ -n "$net" ] || return 0
 
-	config_get dhcpv4 "$cfg" dhcpv4
-	[ "$dhcpv4" != "disabled" ] || 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" && return 0
+	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
@@ -375,21 +482,107 @@ dhcp_add() {
 	config_get_bool force "$cfg" force 0
 	[ $force -gt 0 ] || dhcp_check "$ifname" || return 0
 
-	config_get start "$cfg" start
-	config_get limit "$cfg" limit
-	config_get leasetime "$cfg" leasetime
+	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
 
-	leasetime="${leasetime:-12h}"
-	start="$(dhcp_calc "${start:-100}")"
-	limit="${limit:-150}"
-	[ "$limit" -gt 0 ] && limit=$((limit-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"; fi
-	xappend "--dhcp-range=$networkid,$START,$END,$NETMASK,$leasetime${options:+ $options}"
 
-	dhcp_option_add "$cfg" "$networkid"
+	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() {
@@ -401,21 +594,24 @@ dhcp_option_append() {
 }
 
 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" dhcp_option_LENGTH
+	config_get list_len "$cfg" "${opt}_LENGTH"
 
 	if [ -n "$list_len" ]; then
-		config_list_foreach "$cfg" dhcp_option dhcp_option_append "$networkid" "$force"
+		config_list_foreach "$cfg" "$opt" dhcp_option_append "$networkid" "$force"
 	else
-		config_get dhcp_option "$cfg" dhcp_option
+		config_get dhcp_option "$cfg" "$opt"
 
-		[ -n "$dhcp_option" ] && echo "Warning: the 'option dhcp_option' syntax is deprecated, use 'list dhcp_option'" >&2
+		[ -n "$dhcp_option" ] && echo "Warning: the 'option $opt' syntax is deprecated, use 'list $opt'" >&2
 
 		local option
 		for option in $dhcp_option; do
@@ -533,7 +729,7 @@ dhcp_relay_add() {
 
 dnsmasq_start()
 {
-	local cfg="$1" disabled
+	local cfg="$1" disabled resolvfile user_dhcpscript
 
 	config_get_bool disabled "$cfg" disabled 0
 	[ "$disabled" -gt 0 ] && return 0
@@ -566,6 +762,41 @@ dnsmasq_start()
 
 	$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"
@@ -578,12 +809,11 @@ dnsmasq_start()
 	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"
 	[ -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"
+	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"
@@ -594,7 +824,7 @@ dnsmasq_start()
 	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"
@@ -606,6 +836,7 @@ dnsmasq_start()
 	append_parm "$cfg" "domain" "--domain"
 	append_parm "$cfg" "local" "--server"
 	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
 	config_list_foreach "$cfg" "interface" append_interface
@@ -613,7 +844,6 @@ dnsmasq_start()
 	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"
@@ -624,12 +854,21 @@ dnsmasq_start()
 
 	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 user_dhcpscript $cfg dhcpscript
+	if has_handler || [ -n "$user_dhcpscript" ]; then
+		xappend "--dhcp-script=$DHCPSCRIPT"
+	fi
 
 	config_get leasefile $cfg leasefile "/tmp/dhcp.leases"
 	[ -n "$leasefile" -a \! -e "$leasefile" ] && touch "$leasefile"
@@ -642,6 +881,8 @@ dnsmasq_start()
 		[ -n "$resolvfile" -a \! -e "$resolvfile" ] && touch "$resolvfile"
 	fi
 
+	[ -n "$resolvfile" ] && xappend "--resolv-file=$resolvfile"
+
 	config_get hostsfile "$cfg" dhcphostsfile
 	[ -e "$hostsfile" ] && xappend "--dhcp-hostsfile=$hostsfile"
 
@@ -688,6 +929,7 @@ dnsmasq_start()
 	}
 
 	dhcp_option_add "$cfg" "" 0
+	dhcp_option_add "$cfg" "" 2
 
 	xappend "--dhcp-broadcast=tag:needs-broadcast"
 
@@ -721,37 +963,31 @@ dnsmasq_start()
 	config_foreach filter_dnsmasq hostrecord dhcp_hostrecord_add "$cfg"
 	config_foreach filter_dnsmasq relay dhcp_relay_add "$cfg"
 
-	# add own hostname
-	[ $ADD_LOCAL_HOSTNAME -eq 1 ] && {
-		local lanaddr lanaddr6
-		local ulaprefix="$(uci_get network @globals[0] ula_prefix)"
-		local hostname="$(uci_get system @system[0] hostname Lede)"
-
-		network_get_ipaddr lanaddr "lan" && {
-			dhcp_domain_add "" "$hostname" "$lanaddr"
-		}
-
-		[ -n "$ulaprefix" ] && network_get_ipaddrs6 lanaddr6 "lan" && {
-			for lanaddr6 in $lanaddr6; do
-				case "$lanaddr6" in
-					"${ulaprefix%%:/*}"*)
-						dhcp_domain_add "" "$hostname" "$lanaddr6"
-					;;
-				esac
-			done
-		}
-	}
-
 	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 odhcpd_is_active odhcpd maindhcp
-	if [ "$odhcpd_is_active" != "1" ]; then
+	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
+		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
 		config_foreach filter_dnsmasq dhcp dhcp_add "$cfg"
 	fi
 
+
 	echo >> $CONFIGFILE_TMP
 	config_foreach filter_dnsmasq cname dhcp_cname_add "$cfg"
 	echo >> $CONFIGFILE_TMP
@@ -773,6 +1009,7 @@ dnsmasq_start()
 	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
 
 	local dnsmasqconffile="/etc/dnsmasq.${cfg}.conf"
@@ -781,7 +1018,7 @@ dnsmasq_start()
 	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 $CONFIGFILE $TRUSTANCHORSFILE $HOSTFILE $RFC6761FILE /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

+ 4 - 0
package/network/services/dnsmasq/files/dnsmasq_acl.json

@@ -0,0 +1,4 @@
+{
+	"user": "dnsmasq",
+	"publish": [ "dnsmasq" ]
+}

+ 15 - 0
package/network/services/dnsmasq/files/rfc6761.conf

@@ -0,0 +1,15 @@
+# RFC6761 included configuration file for dnsmasq
+#
+# includes a list of domains that should not be forwarded to Internet name servers
+# to reduce burden on them, asking questions that they won't know the answer to.
+
+server=/bind/
+server=/example/
+server=/example.com/
+server=/example.org/
+server=/example.net/
+server=/invalid/
+server=/local/
+server=/localhost/
+server=/onion/
+server=/test/

+ 0 - 25
package/network/services/dnsmasq/patches/010-Tweak-ICMP-ping-check-logic-for-DHCPv4.patch

@@ -1,25 +0,0 @@
-From 4bb68866a8aeb31db8100492bceae051e33be5d0 Mon Sep 17 00:00:00 2001
-From: Simon Kelley <simon@thekelleys.org.uk>
-Date: Thu, 15 Jun 2017 23:18:44 +0100
-Subject: [PATCH] Tweak ICMP ping check logic for DHCPv4.
-
----
- src/rfc2131.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/rfc2131.c b/src/rfc2131.c
-index 1c850e5..75792da 100644
---- a/src/rfc2131.c
-+++ b/src/rfc2131.c
-@@ -1040,7 +1040,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
- 		  else if (have_config(config, CONFIG_DECLINED) &&
- 			   difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
- 		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
--		  else if (!do_icmp_ping(now, config->addr, 0, loopback))
-+		  else if ((!lease || lease->addr.s_addr != config->addr.s_addr) && !do_icmp_ping(now, config->addr, 0, loopback))
- 		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by another host"), addrs);
- 		  else
- 		    conf = config->addr;
--- 
-1.9.1
-

+ 0 - 28
package/network/services/dnsmasq/patches/011-Remove-ping-check-of-configured-DHCP-address.patch

@@ -1,28 +0,0 @@
-From 1d224949cced9e82440d00b3dbaf32c262bac2ff Mon Sep 17 00:00:00 2001
-From: Simon Kelley <simon@thekelleys.org.uk>
-Date: Sat, 8 Jul 2017 20:52:55 +0100
-Subject: [PATCH 1/2] Remove ping-check of configured DHCP address.
-
-This was added in 5ce3e76fbf89e942e8c54ef3e3389facf0d9067a but
-it trips over too many buggy clients that leave an interface configured
-even in DHCPDISCOVER case.
----
- src/rfc2131.c | 2 --
- 1 file changed, 2 deletions(-)
-
-diff --git a/src/rfc2131.c b/src/rfc2131.c
-index 86230b4..785e15c 100644
---- a/src/rfc2131.c
-+++ b/src/rfc2131.c
-@@ -1040,8 +1040,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
- 		  else if (have_config(config, CONFIG_DECLINED) &&
- 			   difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
- 		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
--		  else if ((!lease || lease->addr.s_addr != config->addr.s_addr) && !do_icmp_ping(now, config->addr, 0, loopback))
--		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by another host"), addrs);
- 		  else
- 		    conf = config->addr;
- 		}
--- 
-2.13.2
-

+ 0 - 31
package/network/services/dnsmasq/patches/020-Try-other-servers-if-first-returns-REFUSED-when-stri.patch

@@ -1,31 +0,0 @@
-From 9396752c115b3ab733fa476b30da73237e12e7ba Mon Sep 17 00:00:00 2001
-From: Hans Dedecker <dedeckeh@gmail.com>
-Date: Tue, 27 Jun 2017 22:08:47 +0100
-Subject: [PATCH] Try other servers if first returns REFUSED when
- --strict-order active.
-
-If a DNS server replies REFUSED for a given DNS query in strict order mode
-no failover to the next DNS server is triggered as the failover logic only
-covers non strict mode.
-As a result the client will be returned the REFUSED reply without first
-falling back to the secondary DNS server(s).
-
-Make failover support work as well for strict mode config in case REFUSED is
-replied by deleting the strict order check and rely only on forwardall being
-equal to 0 which is the case in non strict mode when a single server has been
-contacted or when strict order mode has been configured.
----
- CHANGELOG     | 4 ++++
- src/forward.c | 1 -
- 2 files changed, 4 insertions(+), 1 deletion(-)
-
---- a/src/forward.c
-+++ b/src/forward.c
-@@ -790,7 +790,6 @@ void reply_query(int fd, int family, tim
-   /* Note: if we send extra options in the EDNS0 header, we can't recreate
-      the query from the reply. */
-   if (RCODE(header) == REFUSED &&
--      !option_bool(OPT_ORDER) &&
-       forward->forwardall == 0 &&
-       !(forward->flags & FREC_HAS_EXTRADATA))
-     /* for broken servers, attempt to send to another one. */

+ 0 - 94
package/network/services/dnsmasq/patches/025-backport-fix-CVE-2017-13704.patch

@@ -1,94 +0,0 @@
-From 63437ffbb58837b214b4b92cb1c54bc5f3279928 Mon Sep 17 00:00:00 2001
-From: Simon Kelley <simon@thekelleys.org.uk>
-Date: Wed, 6 Sep 2017 22:34:21 +0100
-Subject: [PATCH] Fix CVE-2017-13704, which resulted in a crash on a large DNS
- query.
-
-A DNS query recieved by UDP which exceeds 512 bytes (or the EDNS0 packet size,
-if different.) is enough to cause SIGSEGV.
----
- CHANGELOG     |    7 +++++++
- src/auth.c    |    5 -----
- src/forward.c |    8 ++++++++
- src/rfc1035.c |    5 -----
- 4 files changed, 15 insertions(+), 10 deletions(-)
-
-diff --git a/CHANGELOG b/CHANGELOG
-index 3a640f3..7e65912 100644
---- a/CHANGELOG
-+++ b/CHANGELOG
-@@ -17,6 +17,13 @@ version 2.78
- 	--strict-order active. Thanks to Hans Dedecker
- 	for the patch
- 
-+	Fix regression in 2.77, ironically added as a security
-+	improvement, which resulted in a crash when a DNS
-+	query exceeded 512 bytes (or the EDNS0 packet size,
-+	if different.) Thanks to Christian Kujau, Arne Woerner
-+	Juan Manuel Fernandez and Kevin Darbyshire-Bryant for
-+	chasing this one down.  CVE-2017-13704 applies.
-+
- 	
- version 2.77
- 	Generate an error when configured with a CNAME loop,
-diff --git a/src/auth.c b/src/auth.c
-index 2c24e16..7f95f98 100644
---- a/src/auth.c
-+++ b/src/auth.c
-@@ -119,11 +119,6 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
-   struct cname *a, *candidate;
-   unsigned int wclen;
-   
--  /* Clear buffer beyond request to avoid risk of
--     information disclosure. */
--  memset(((char *)header) + qlen, 0, 
--	 (limit - ((char *)header)) - qlen);
--  
-   if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
-     return 0;
- 
-diff --git a/src/forward.c b/src/forward.c
-index f22556a..e3fa94b 100644
---- a/src/forward.c
-+++ b/src/forward.c
-@@ -1188,6 +1188,10 @@ void receive_query(struct listener *listen, time_t now)
-       (msg.msg_flags & MSG_TRUNC) ||
-       (header->hb3 & HB3_QR))
-     return;
-+
-+  /* Clear buffer beyond request to avoid risk of
-+     information disclosure. */
-+  memset(daemon->packet + n, 0, daemon->edns_pktsz - n);
-   
-   source_addr.sa.sa_family = listen->family;
-   
-@@ -1688,6 +1692,10 @@ unsigned char *tcp_request(int confd, time_t now,
-   
-       if (size < (int)sizeof(struct dns_header))
- 	continue;
-+
-+      /* Clear buffer beyond request to avoid risk of
-+	 information disclosure. */
-+      memset(payload + size, 0, 65536 - size);
-       
-       query_count++;
- 
-diff --git a/src/rfc1035.c b/src/rfc1035.c
-index 26f5301..af2fe46 100644
---- a/src/rfc1035.c
-+++ b/src/rfc1035.c
-@@ -1223,11 +1223,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
-   struct mx_srv_record *rec;
-   size_t len;
- 
--  /* Clear buffer beyond request to avoid risk of
--     information disclosure. */
--  memset(((char *)header) + qlen, 0, 
--	 (limit - ((char *)header)) - qlen);
--  
-   if (ntohs(header->ancount) != 0 ||
-       ntohs(header->nscount) != 0 ||
-       ntohs(header->qdcount) == 0 || 
--- 
-1.7.10.4
-

+ 0 - 44
package/network/services/dnsmasq/patches/030-fix-arcount-edns0-behaviour.patch

@@ -1,44 +0,0 @@
-From a3303e196e5d304ec955c4d63afb923ade66c6e8 Mon Sep 17 00:00:00 2001
-From: Simon Kelley <simon@thekelleys.org.uk>
-Date: Thu, 7 Sep 2017 20:45:00 +0100
-Subject: [PATCH] Don't return arcount=1 if EDNS0 RR won't fit in the packet.
-
-Omitting the EDNS0 RR but setting arcount gives a malformed packet.
-Also, don't accept UDP packet size less than 512 in recieved EDNS0.
----
- src/edns0.c   |    5 ++++-
- src/forward.c |    2 ++
- 2 files changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/src/edns0.c b/src/edns0.c
-index 3fde17f..f5b798c 100644
---- a/src/edns0.c
-+++ b/src/edns0.c
-@@ -208,7 +208,10 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
- 	  free(buff);
- 	  p += rdlen;
- 	}
--      header->arcount = htons(ntohs(header->arcount) + 1);
-+      
-+      /* Only bump arcount if RR is going to fit */ 
-+      if (((ssize_t)optlen) <= (limit - (p + 4)))
-+	header->arcount = htons(ntohs(header->arcount) + 1);
-     }
-   
-   if (((ssize_t)optlen) > (limit - (p + 4)))
-diff --git a/src/forward.c b/src/forward.c
-index e3fa94b..942b02d 100644
---- a/src/forward.c
-+++ b/src/forward.c
-@@ -1412,6 +1412,8 @@ void receive_query(struct listener *listen, time_t now)
- 	 defaults to 512 */
-       if (udp_size > daemon->edns_pktsz)
- 	udp_size = daemon->edns_pktsz;
-+      else if (udp_size < PACKETSZ)
-+	udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */
-     }
- 
- #ifdef HAVE_AUTH
--- 
-1.7.10.4
-

+ 1 - 1
package/network/services/dnsmasq/patches/230-fix-poll-h-include-warning-on-musl.patch

@@ -7,7 +7,7 @@ Signed-off-by: Kevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk>
 
 --- a/src/dnsmasq.h
 +++ b/src/dnsmasq.h
-@@ -82,7 +82,7 @@ typedef unsigned long long u64;
+@@ -88,7 +88,7 @@ typedef unsigned long long u64;
  #if defined(HAVE_SOLARIS_NETWORK)
  #  include <sys/sockio.h>
  #endif

+ 126 - 0
package/network/services/dnsmasq/patches/240-ubus.patch

@@ -0,0 +1,126 @@
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -19,6 +19,8 @@
+ 
+ #include "dnsmasq.h"
+ 
++#include <libubus.h>
++
+ struct daemon *daemon;
+ 
+ static volatile pid_t pid = 0;
+@@ -32,6 +34,62 @@ static void fatal_event(struct event_des
+ static int read_event(int fd, struct event_desc *evp, char **msg);
+ static void poll_resolv(int force, int do_reload, time_t now);
+ 
++static struct ubus_context *ubus;
++static struct blob_buf b;
++
++static struct ubus_object_type ubus_object_type = {
++	.name = "dnsmasq",
++};
++
++static struct ubus_object ubus_object = {
++	.name = "dnsmasq",
++	.type = &ubus_object_type,
++};
++
++void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name)
++{
++	if (!ubus || !ubus_object.has_subscribers)
++		return;
++
++	blob_buf_init(&b, 0);
++	if (mac)
++		blobmsg_add_string(&b, "mac", mac);
++	if (ip)
++		blobmsg_add_string(&b, "ip", ip);
++	if (name)
++		blobmsg_add_string(&b, "name", name);
++	ubus_notify(ubus, &ubus_object, type, b.head, -1);
++}
++
++static void set_ubus_listeners(void)
++{
++	if (!ubus)
++		return;
++
++	poll_listen(ubus->sock.fd, POLLIN);
++	poll_listen(ubus->sock.fd, POLLERR);
++	poll_listen(ubus->sock.fd, POLLHUP);
++}
++
++static void check_ubus_listeners()
++{
++	if (!ubus) {
++		ubus = ubus_connect(NULL);
++		if (ubus)
++			ubus_add_object(ubus, &ubus_object);
++		else
++			return;
++	}
++
++	if (poll_check(ubus->sock.fd, POLLIN))
++		ubus_handle_event(ubus);
++
++	if (poll_check(ubus->sock.fd, POLLHUP)) {
++		ubus_free(ubus);
++		ubus = NULL;
++	}
++}
++
+ int main (int argc, char **argv)
+ {
+   int bind_fallback = 0;
+@@ -911,6 +969,7 @@ int main (int argc, char **argv)
+       set_dbus_listeners();
+ #endif	
+   
++      set_ubus_listeners();
+ #ifdef HAVE_DHCP
+       if (daemon->dhcp || daemon->relay4)
+ 	{
+@@ -1041,6 +1100,8 @@ int main (int argc, char **argv)
+       check_dbus_listeners();
+ #endif
+       
++      check_ubus_listeners();
++
+       check_dns_listeners(now);
+ 
+ #ifdef HAVE_TFTP
+--- a/Makefile
++++ b/Makefile
+@@ -85,7 +85,7 @@ all : $(BUILDDIR)
+ 	@cd $(BUILDDIR) && $(MAKE) \
+  top="$(top)" \
+  build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
+- build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs)" \
++ build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) -lubox -lubus" \
+  -f $(top)/Makefile dnsmasq 
+ 
+ mostly_clean :
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -1397,6 +1397,8 @@ void emit_dbus_signal(int action, struct
+ #  endif
+ #endif
+ 
++void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name);
++
+ /* ipset.c */
+ #ifdef HAVE_IPSET
+ void ipset_init(void);
+--- a/src/rfc2131.c
++++ b/src/rfc2131.c
+@@ -1621,6 +1621,10 @@ static void log_packet(char *type, void
+ 	      daemon->namebuff,
+ 	      string ? string : "",
+ 	      err ? err : "");
++  if (!strcmp(type, "DHCPACK"))
++	  ubus_event_bcast("dhcp.ack", addr ? inet_ntoa(a) : NULL, daemon->namebuff, string ? string : NULL);
++  else if (!strcmp(type, "DHCPRELEASE"))
++	  ubus_event_bcast("dhcp.release", addr ? inet_ntoa(a) : NULL, daemon->namebuff, string ? string : NULL);
+ }
+ 
+ static void log_options(unsigned char *start, u32 xid)