From 6da847c446c29c349b0edd757c1afd529565c619 Mon Sep 17 00:00:00 2001
From: Jo-Philipp Wich <jow@openwrt.org>
Date: Tue, 12 Mar 2013 19:43:41 +0100
Subject: [PATCH] Emit hotplug calls when flushing / creating zone chains

---
 main.c    | 12 +++++++++---
 options.h |  1 +
 utils.c   | 41 +++++++++++++++++++++++++++++++++++++++++
 utils.h   |  3 +++
 zones.c   | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 zones.h   |  7 ++++---
 6 files changed, 112 insertions(+), 6 deletions(-)

diff --git a/main.c b/main.c
index c1f6505..3c2b4eb 100644
--- a/main.c
+++ b/main.c
@@ -171,6 +171,9 @@ stop(struct fw3_state *state, bool complete, bool reload)
 		return rv;
 	}
 
+	if (!print_rules)
+		fw3_hotplug_zones(false, state);
+
 	for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
 	{
 		if (!complete && !family_running(state, family))
@@ -303,11 +306,14 @@ start(struct fw3_state *state, bool reload)
 	{
 		fw3_set_defaults(state);
 
-		if (!reload && !print_rules)
-			fw3_run_includes(state);
-
 		if (!print_rules)
+		{
+			if (!reload)
+				fw3_run_includes(state);
+
+			fw3_hotplug_zones(true, state);
 			fw3_write_statefile(state);
+		}
 	}
 
 	return rv;
diff --git a/options.h b/options.h
index 392db13..c84fd28 100644
--- a/options.h
+++ b/options.h
@@ -77,6 +77,7 @@ enum fw3_flag
 	FW3_FLAG_SYN_FLOOD     = 16,
 	FW3_FLAG_MTU_FIX       = 17,
 	FW3_FLAG_DROP_INVALID  = 18,
+	FW3_FLAG_HOTPLUG       = 19,
 
 	__FW3_FLAG_MAX
 };
diff --git a/utils.c b/utils.c
index 1942cbc..dbc713c 100644
--- a/utils.c
+++ b/utils.c
@@ -244,6 +244,7 @@ __fw3_command_pipe(bool silent, const char *command, ...)
 		signal(SIGPIPE, SIG_IGN);
 		pipe_pid = pid;
 		close(pfds[0]);
+		fcntl(pfds[1], F_SETFD, fcntl(pfds[1], F_GETFD) | FD_CLOEXEC);
 	}
 
 	pipe_fd = fdopen(pfds[1], "w");
@@ -597,3 +598,43 @@ fw3_pr_rulespec(int table, int family, uint32_t *flags, uint32_t mask,
 
 	return rv;
 }
+
+
+bool
+fw3_hotplug(bool add, void *zone, void *device)
+{
+	struct fw3_zone *z = zone;
+	struct fw3_device *d = device;
+
+	if (!d->network)
+		return false;
+
+	switch (fork())
+	{
+	case -1:
+		warn("Unable to fork(): %s\n", strerror(errno));
+		return false;
+
+	case 0:
+		break;
+
+	default:
+		return true;
+	}
+
+	close(0);
+	close(1);
+	close(2);
+	chdir("/");
+
+	clearenv();
+	setenv("ACTION",    add ? "add" : "remove", 1);
+	setenv("ZONE",      z->name,                1);
+	setenv("INTERFACE", d->network->name,       1);
+	setenv("DEVICE",    d->name,                1);
+
+	execl(FW3_HOTPLUG, FW3_HOTPLUG, "firewall", NULL);
+
+	/* unreached */
+	return false;
+}
diff --git a/utils.h b/utils.h
index e28408b..eee1f40 100644
--- a/utils.h
+++ b/utils.h
@@ -35,6 +35,7 @@
 
 #define FW3_STATEFILE	"/var/run/fw3.state"
 #define FW3_LOCKFILE	"/var/run/fw3.lock"
+#define FW3_HOTPLUG     "/sbin/hotplug-call"
 
 extern bool fw3_pr_debug;
 
@@ -109,4 +110,6 @@ struct fw3_rule_spec {
 bool fw3_pr_rulespec(int table, int family, uint32_t *flags, uint32_t mask,
                      const struct fw3_rule_spec *r, const char *fmt, ...);
 
+bool fw3_hotplug(bool add, void *zone, void *device);
+
 #endif
diff --git a/zones.c b/zones.c
index 79f037e..846e430 100644
--- a/zones.c
+++ b/zones.c
@@ -521,6 +521,40 @@ fw3_flush_zones(enum fw3_table table, enum fw3_family family,
 	}
 }
 
+void
+fw3_hotplug_zones(bool add, struct fw3_state *state)
+{
+	struct fw3_zone *z;
+	struct fw3_device *d;
+
+	if (add)
+	{
+		list_for_each_entry(z, &state->running_zones, running_list)
+		{
+			if (!hasbit(z->flags[0], FW3_FLAG_HOTPLUG))
+			{
+				list_for_each_entry(d, &z->devices, list)
+					fw3_hotplug(add, z, d);
+
+				setbit(z->flags[0], FW3_FLAG_HOTPLUG);
+			}
+		}
+	}
+	else
+	{
+		list_for_each_entry(z, &state->running_zones, running_list)
+		{
+			if (hasbit(z->flags[0], FW3_FLAG_HOTPLUG))
+			{
+				list_for_each_entry(d, &z->running_devices, list)
+					fw3_hotplug(add, z, d);
+
+				delbit(z->flags[0], FW3_FLAG_HOTPLUG);
+			}
+		}
+	}
+}
+
 struct fw3_zone *
 fw3_lookup_zone(struct fw3_state *state, const char *name, bool running)
 {
@@ -542,3 +576,23 @@ fw3_lookup_zone(struct fw3_state *state, const char *name, bool running)
 
 	return NULL;
 }
+
+void
+fw3_free_zone(struct fw3_zone *zone)
+{
+	struct fw3_device *dev, *tmp;
+
+	list_for_each_entry_safe(dev, tmp, &zone->running_devices, list)
+	{
+		list_del(&dev->list);
+		free(dev);
+	}
+
+	list_for_each_entry_safe(dev, tmp, &zone->running_networks, list)
+	{
+		list_del(&dev->list);
+		free(dev);
+	}
+
+	fw3_free_object(zone, fw3_zone_opts);
+}
diff --git a/zones.h b/zones.h
index 8f94ccf..bda96ab 100644
--- a/zones.h
+++ b/zones.h
@@ -36,13 +36,14 @@ void fw3_print_zone_rules(enum fw3_table table, enum fw3_family family,
 void fw3_flush_zones(enum fw3_table table, enum fw3_family family,
                      bool pass2, bool reload, struct fw3_state *state);
 
+void fw3_hotplug_zones(bool add, struct fw3_state *state);
+
 struct fw3_zone * fw3_lookup_zone(struct fw3_state *state, const char *name,
                                   bool running);
 
+void fw3_free_zone(struct fw3_zone *zone);
+
 #define fw3_to_src_target(t) \
 	(FW3_FLAG_SRC_ACCEPT - FW3_FLAG_ACCEPT + t)
 
-#define fw3_free_zone(zone) \
-	fw3_free_object(zone, fw3_zone_opts)
-
 #endif
-- 
2.25.1