commit 1098cb94070574a98a44b4ab160e2a4d1785925a
Author: Viktor G <viktor@netgate.com>
Date:   Fri Dec 17 15:22:34 2021 +0300

    IGMP Proxy service improvements. Fixes #12609

diff --git a/src/etc/inc/services.inc b/src/etc/inc/services.inc
index a982f1b280..7f43d2b12d 100644
--- a/src/etc/inc/services.inc
+++ b/src/etc/inc/services.inc
@@ -1920,17 +1920,39 @@ EOD;
 	return 0;
 }
 
-function services_igmpproxy_configure() {
+function services_igmpproxy_configure($interface='') {
 	global $config, $g;
 
-	/* kill any running igmpproxy */
-	killbyname("igmpproxy");
+	if (!empty($interface) && is_array($config['igmpproxy']['igmpentry'])) { 
+		foreach ($config['igmpproxy']['igmpentry'] as $igmpentry) {
+			if ($igmpentry['ifname'] == $interface) {
+				$igmpinf = true;
+				break;
+			}
+		}
+		if (!$igmpinf) {
+			return false;
+		}
+	}
 
 	if (!isset($config['igmpproxy']['enable'])) {
-		return 0;
+		if (isvalidproc("igmpproxy")) {
+			log_error(gettext("Stopping IGMP Proxy service."));
+			killbyname("igmpproxy");
+		}
+		return true;
 	}
 	if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0)) {
-		return 1;
+		return false;
+	}
+
+	if (platform_booting()) {
+		echo gettext("Starting IGMP Proxy service...");
+	}
+
+	if (isvalidproc("igmpproxy")) {
+		log_error(gettext("Restarting IGMP Proxy service."));
+		killbyname("igmpproxy");
 	}
 
 	$iflist = get_configured_interface_list();
@@ -1945,6 +1967,9 @@ quickleave
 EOD;
 
 	foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) {
+		if (empty($config['interfaces'][$igmpcf['ifname']]['ipaddr'])) {
+			continue;
+		}
 		unset($iflist[$igmpcf['ifname']]);
 		$realif = get_real_interface($igmpcf['ifname']);
 		if (empty($igmpcf['threshold'])) {
@@ -1961,6 +1986,11 @@ EOD;
 			}
 		}
 		$igmpconf .= "\n";
+		if ($igmpcf['type'] == 'upstream') {
+		       $upstream = true;	
+		} else {
+		       $downstream = true;	
+		}
 	}
 	foreach ($iflist as $ifn) {
 		$realif = get_real_interface($ifn);
@@ -1968,9 +1998,14 @@ EOD;
 	}
 	$igmpconf .= "\n";
 
+	if (!$upstream || !$downstream) {
+		log_error(gettext("Could not find upstream or downstream IGMP Proxy interfaces!"));
+		return;
+	}
+
 	$igmpfl = fopen($g['varetc_path'] . "/igmpproxy.conf", "w");
 	if (!$igmpfl) {
-		log_error(gettext("Could not write Igmpproxy configuration file!"));
+		log_error(gettext("Could not write IGMP Proxy configuration file!"));
 		return;
 	}
 	fwrite($igmpfl, $igmpconf);
@@ -1983,9 +2018,13 @@ EOD;
 		mwexec_bg("/usr/local/sbin/igmpproxy {$g['varetc_path']}/igmpproxy.conf");
 	}
 
-	log_error(gettext("Started IGMP proxy service."));
+	log_error(gettext("Started IGMP Proxy service."));
 
-	return 0;
+	if (platform_booting()) {
+		echo gettext("done.") . "\n";
+	}
+
+	return true;
 }
 
 function services_dhcrelay_configure() {
diff --git a/src/etc/rc.newwanip b/src/etc/rc.newwanip
index 40441c5c0f..01797c753c 100755
--- a/src/etc/rc.newwanip
+++ b/src/etc/rc.newwanip
@@ -265,7 +265,7 @@ if (!is_ipaddr($oldip) || ($curwanip != $oldip) || file_exists("{$g['tmp_path']}
 	enable_rrd_graphing();
 
 	/* reload igmpproxy */
-	services_igmpproxy_configure();
+	services_igmpproxy_configure($interface);
 
 	/* restart snmp */
 	services_snmpd_configure();
diff --git a/src/usr/local/www/interfaces.php b/src/usr/local/www/interfaces.php
index 2f953bad82..10d039bad4 100755
--- a/src/usr/local/www/interfaces.php
+++ b/src/usr/local/www/interfaces.php
@@ -527,6 +527,8 @@ if ($_POST['apply']) {
 		if (isset($config['syslog']['enable']) && ($ifapply == $config['syslog']['sourceip'])) {
 			system_syslogd_start();
 		}
+
+		services_igmpproxy_configure($ifapply);
 	}
 	@unlink("{$g['tmp_path']}/.interfaces.apply");
 } else if ($_POST['save']) {
diff --git a/src/usr/local/www/services_igmpproxy.php b/src/usr/local/www/services_igmpproxy.php
index c791bc1285..dad0ff3176 100644
--- a/src/usr/local/www/services_igmpproxy.php
+++ b/src/usr/local/www/services_igmpproxy.php
@@ -56,17 +56,37 @@ if (isset($config['igmpproxy']['enable'])) {
 $pconfig['igmpxverbose'] = isset($config['syslog']['igmpxverbose']);
 
 if ($_POST['save']) {
+	unset($input_errors);
 	$pconfig = $_POST;
+
 	if (isset($pconfig['enable'])) {
-		$config['igmpproxy']['enable'] = true;
-	} else {
-		unset($config['igmpproxy']['enable']);
+		if (is_array($config['igmpproxy']['igmpentry']) && 
+		    !empty($config['igmpproxy']['igmpentry'])) {
+			foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) {
+				if ($igmpcf['type'] == 'upstream') {
+				       $upstream = true;	
+				} else {
+				       $downstream = true;	
+				}
+			}
+		}
+		if (!$upstream || !$downstream) {
+			$input_errors[] = gettext("At least one upstream and one downstream interface must be added.");
+		}
+	}
+
+	if (!$input_errors) {
+		if (isset($pconfig['enable'])) {
+			$config['igmpproxy']['enable'] = true;
+		} else {
+			unset($config['igmpproxy']['enable']);
+		}
+		$config['syslog']['igmpxverbose'] = $_POST['igmpxverbose'] ? true : false;
+		write_config("IGMP Proxy settings saved");
+		mark_subsystem_dirty('igmpproxy');
+		header("Location: services_igmpproxy.php");
+		exit;
 	}
-	$config['syslog']['igmpxverbose'] = $_POST['igmpxverbose'] ? true : false;
-	write_config("IGMP Proxy settings saved");
-	mark_subsystem_dirty('igmpproxy');
-	header("Location: services_igmpproxy.php");
-	exit;
 }
 
 if ($_POST['act'] == "del") {
@@ -82,6 +102,10 @@ if ($_POST['act'] == "del") {
 $pgtitle = array(gettext("Services"), gettext("IGMP Proxy"));
 include("head.inc");
 
+if ($input_errors) {
+	print_input_errors($input_errors);
+}
+
 if ($changes_applied) {
 	print_apply_result_box($retval);
 }
diff --git a/src/usr/local/www/services_igmpproxy_edit.php b/src/usr/local/www/services_igmpproxy_edit.php
index f5c414b8fb..03b6220351 100644
--- a/src/usr/local/www/services_igmpproxy_edit.php
+++ b/src/usr/local/www/services_igmpproxy_edit.php
@@ -155,7 +155,9 @@ $optionlist = array();
 $iflist = get_configured_interface_with_descr();
 
 foreach ($iflist as $ifnam => $ifdescr) {
-	$optionlist[$ifnam] = $ifdescr;
+	if (!empty($config['interfaces'][$ifnam]['ipaddr'])) {
+		$optionlist[$ifnam] = $ifdescr;
+	}
 }
 
 $section->addInput(new Form_Select(
