NetworkManager r3969 - in trunk: . src



Author: dcbw
Date: Fri Aug 15 15:34:28 2008
New Revision: 3969
URL: http://svn.gnome.org/viewvc/NetworkManager?rev=3969&view=rev

Log:
2008-08-15  Dan Williams  <dcbw redhat com>

	Do connection sharing in a cleaner manner; all required iptables rules
	are now stored in the activation request and pertain only to the device
	which is being shared to other computers.

	* src/nm-activation-request.c
	  src/nm-activation-request.h
		- (nm_act_request_add_share_rule): new function; add a sharing rule to
			the activation request which will get torn down automatically when
			the activation request dies
		- (nm_act_request_set_shared): push sharing rules to iptables when sharing
			is started, and tear them down when sharing is stopped

	* src/nm-device.c
		- (start_sharing): start up sharing by doing the required iptables magic
		- (share_init): poke the right bits of the kernel and load the right
			modules for NAT
		- (nm_device_activate_stage5_ip_config_commit): start NAT-ing this
			connection if it's a 'shared' connection

	* src/NetworkManagerPolicy.c
		- Remove all sharing stuff; done in the device code itself



Modified:
   trunk/ChangeLog
   trunk/src/NetworkManagerPolicy.c
   trunk/src/nm-activation-request.c
   trunk/src/nm-activation-request.h
   trunk/src/nm-device.c

Modified: trunk/src/NetworkManagerPolicy.c
==============================================================================
--- trunk/src/NetworkManagerPolicy.c	(original)
+++ trunk/src/NetworkManagerPolicy.c	Fri Aug 15 15:34:28 2008
@@ -20,17 +20,7 @@
  * (C) Copyright 2005 Red Hat, Inc.
  */
 
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <sys/select.h>
 #include <string.h>
-#include <stdlib.h>
-#include <sys/wait.h>
 
 #include "NetworkManagerPolicy.h"
 #include "NetworkManagerUtils.h"
@@ -345,178 +335,6 @@
 	return nm_act_request_get_connection (req);
 }
 
-static gboolean
-do_cmd (const char *fmt, ...)
-{
-	va_list args;
-	char *cmd;
-	int ret;
-
-	va_start (args, fmt);
-	cmd = g_strdup_vprintf (fmt, args);
-	va_end (args);
-
-	nm_info ("Executing: %s", cmd);
-	ret = system (cmd);
-	g_free (cmd);
-
-	if (ret == -1) {
-		nm_info ("** Error executing command.");
-		return FALSE;
-	} else if (WEXITSTATUS (ret)) {
-		nm_info ("** Command returned exit status %d.", WEXITSTATUS (ret));
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static void
-sharing_init (void)
-{
-	do_cmd ("echo \"1\" > /proc/sys/net/ipv4/ip_forward");
-	do_cmd ("echo \"1\" > /proc/sys/net/ipv4/ip_dynaddr");
-	do_cmd ("/sbin/modprobe ip_tables iptable_nat ip_nat_ftp ip_nat_irc");
-	do_cmd ("/sbin/iptables -P INPUT ACCEPT");
-	do_cmd ("/sbin/iptables -F INPUT");
-	do_cmd ("/sbin/iptables -P OUTPUT ACCEPT");
-	do_cmd ("/sbin/iptables -F OUTPUT");
-	do_cmd ("/sbin/iptables -P FORWARD DROP");
-	do_cmd ("/sbin/iptables -F FORWARD");
-	do_cmd ("/sbin/iptables -t nat -F");
-}
-
-static void
-sharing_stop (NMActRequest *req)
-{
-	do_cmd ("/sbin/iptables -F INPUT");
-	do_cmd ("/sbin/iptables -F OUTPUT");
-	do_cmd ("/sbin/iptables -P FORWARD DROP");
-	do_cmd ("/sbin/iptables -F FORWARD");
-	do_cmd ("/sbin/iptables -F -t nat");
-
-	// Delete all User-specified chains
-	do_cmd ("/sbin/iptables -X");
-
-	// Reset all IPTABLES counters
-	do_cmd ("/sbin/iptables -Z");
-
-	nm_act_request_set_shared (req, FALSE);
-}
-
-/* Given a default activation request, start NAT-ing if there are any shared
- * connections.
- */
-static void
-sharing_restart (NMPolicy *policy, NMActRequest *req)
-{
-	GSList *devices, *iter;
-	const char *extif;
-	gboolean have_shared = FALSE;
-
-	if (nm_act_request_get_shared (req))
-		sharing_stop (req);
-
-	extif = nm_device_get_ip_iface (NM_DEVICE (nm_act_request_get_device (req)));
-	g_assert (extif);
-
-	/* Start NAT-ing every 'shared' connection */
-	devices = nm_manager_get_devices (policy->manager);
-	for (iter = devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *candidate = NM_DEVICE (iter->data);
-		NMSettingIP4Config *s_ip4;
-		NMConnection *connection;
-		const char *intif;
-
-		if (nm_device_get_state (candidate) != NM_DEVICE_STATE_ACTIVATED)
-			continue;
-
-		connection = get_device_connection (candidate);
-		g_assert (connection);
-
-		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
-		if (!s_ip4 || strcmp (s_ip4->method, "shared"))
-			continue;
-
-		/* Init sharing if there's a shared connection to NAT */
-		if (!have_shared) {
-			sharing_init ();
-			have_shared = TRUE;
-		}
-
-		// FWD: Allow all connections OUT and only existing and related ones IN
-		intif = nm_device_get_ip_iface (candidate);
-		g_assert (intif);
-		do_cmd ("/sbin/iptables -A FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT", extif, intif);
-		do_cmd ("/sbin/iptables -A FORWARD -i %s -o %s -j ACCEPT", extif, intif);
-		do_cmd ("/sbin/iptables -A FORWARD -i %s -o %s -j ACCEPT", intif, extif);
-	}
-
-	if (have_shared) {
-		// Enabling SNAT (MASQUERADE) functionality on $EXTIF
-		do_cmd ("/sbin/iptables -t nat -A POSTROUTING -o %s -j MASQUERADE", extif);
-
-		nm_act_request_set_shared (req, TRUE);
-	}
-}
-
-static void
-check_sharing (NMPolicy *policy, NMDevice *device, NMConnection *connection)
-{
-	NMSettingIP4Config *s_ip4;
-	GSList *devices, *iter;
-	NMActRequest *default_req = NULL;
-
-	if (!connection)
-		return;
-
-	/* We only care about 'shared' connections going up or down */
-	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
-	if (!s_ip4 || strcmp (s_ip4->method, "shared"))
-		return;
-
-	/* Find the default connection, if any */
-	devices = nm_manager_get_devices (policy->manager);
-	for (iter = devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *candidate = NM_DEVICE (iter->data);
-		NMActRequest *req = nm_device_get_act_request (candidate);
-
-		if (req && nm_act_request_get_default (req)) {
-			default_req = req;
-			break;
-		}
-	}
-
-	/* Restart sharing if there's a default active connection */
-	if (default_req)
-		sharing_restart (policy, default_req);
-}
-
-static void
-active_connection_default_changed (NMActRequest *req,
-                                   GParamSpec *pspec,
-                                   NMPolicy *policy)
-{
-	gboolean is_default = nm_act_request_get_default (req);
-
-	if (is_default) {
-		if (nm_act_request_get_shared (req)) {
-			/* Already shared, shouldn't get here */
-			nm_warning ("%s: Active connection '%s' already shared.",
-			            __func__, nm_act_request_get_active_connection_path (req));
-			return;
-		}
-
-		sharing_restart (policy, req);
-	} else {
-		if (!nm_act_request_get_shared (req))
-			return;  /* Don't care about non-shared connections */
-
-		/* Tear down all NAT-ing */
-		sharing_stop (req);
-	}
-}
-
 static void
 device_state_changed (NMDevice *device,
                       NMDeviceState new_state,
@@ -535,27 +353,19 @@
 			nm_info ("Marking connection '%s' invalid.", get_connection_id (connection));
 		}
 		schedule_activate_check (policy, device);
-		check_sharing (policy, device, connection);
 		break;
 	case NM_DEVICE_STATE_ACTIVATED:
 		/* Clear the invalid tag on the connection */
 		if (connection)
 			g_object_set_data (G_OBJECT (connection), INVALID_TAG, NULL);
 
-		g_signal_connect (G_OBJECT (nm_device_get_act_request (device)),
-		                  "notify::default",
-		                  G_CALLBACK (active_connection_default_changed),
-		                  policy);
-
 		update_routing_and_dns (policy, FALSE);
-		check_sharing (policy, device, connection);
 		break;
 	case NM_DEVICE_STATE_UNMANAGED:
 	case NM_DEVICE_STATE_UNAVAILABLE:
 	case NM_DEVICE_STATE_DISCONNECTED:
 		update_routing_and_dns (policy, FALSE);
 		schedule_activate_check (policy, device);
-		check_sharing (policy, device, connection);
 		break;
 	default:
 		break;

Modified: trunk/src/nm-activation-request.c
==============================================================================
--- trunk/src/nm-activation-request.c	(original)
+++ trunk/src/nm-activation-request.c	Fri Aug 15 15:34:28 2008
@@ -20,6 +20,9 @@
  */
 
 #include <string.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
 #include <dbus/dbus-glib.h>
 
 #include "nm-activation-request.h"
@@ -49,6 +52,10 @@
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+typedef struct {
+	char *table;
+	char *rule;
+} ShareRule;
 
 typedef struct {
 	gboolean disposed;
@@ -63,6 +70,7 @@
 	NMActiveConnectionState state;
 	gboolean is_default;
 	gboolean shared;
+	GSList *share_rules;
 
 	char *ac_path;
 } NMActRequestPrivate;
@@ -206,12 +214,33 @@
 
 	cleanup_secrets_dbus_call (NM_ACT_REQUEST (object));
 
+	/* Clear any share rules */
+	nm_act_request_set_shared (NM_ACT_REQUEST (object), FALSE);
+
 	g_object_unref (priv->connection);
 
 	G_OBJECT_CLASS (nm_act_request_parent_class)->dispose (object);
 }
 
 static void
+clear_share_rules (NMActRequest *req)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	GSList *iter;
+
+	for (iter = priv->share_rules; iter; iter = g_slist_next (iter)) {
+		ShareRule *rule = (ShareRule *) iter->data;
+
+		g_free (rule->table);
+		g_free (rule->rule);
+		g_free (rule);
+	}
+
+	g_slist_free (priv->share_rules);
+	priv->share_rules = NULL;
+}
+
+static void
 finalize (GObject *object)
 {
 	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object);
@@ -219,6 +248,8 @@
 	g_free (priv->specific_object);
 	g_free (priv->ac_path);
 
+	clear_share_rules (NM_ACT_REQUEST (object));
+
 	G_OBJECT_CLASS (nm_act_request_parent_class)->finalize (object);
 }
 
@@ -646,12 +677,69 @@
 	return NM_ACT_REQUEST_GET_PRIVATE (req)->is_default;
 }
 
+static void
+share_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
 void
 nm_act_request_set_shared (NMActRequest *req, gboolean shared)
 {
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	GSList *list, *iter;
+
 	g_return_if_fail (NM_IS_ACT_REQUEST (req));
 
 	NM_ACT_REQUEST_GET_PRIVATE (req)->shared = shared;
+
+	/* Tear the rules down in reverse order when sharing is stopped */
+	list = g_slist_copy (priv->share_rules);
+	if (!shared)
+		list = g_slist_reverse (list);
+
+	/* Send the rules to iptables */
+	for (iter = list; iter; iter = g_slist_next (iter)) {
+		ShareRule *rule = (ShareRule *) iter->data;
+		char *envp[1] = { NULL };
+		char **argv;
+		char *cmd;
+		int status;
+		GError *error = NULL;
+
+		if (shared)
+			cmd = g_strdup_printf ("/sbin/iptables --table %s --insert %s", rule->table, rule->rule);
+		else
+			cmd = g_strdup_printf ("/sbin/iptables --table %s --delete %s", rule->table, rule->rule);
+
+		argv = g_strsplit (cmd, " ", 0);
+		if (!argv || !argv[0]) {
+			continue;
+			g_free (cmd);
+		}
+
+		nm_info ("Executing: %s", cmd);
+		g_free (cmd);
+
+		if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+		                   share_child_setup, NULL, NULL, NULL, &status, &error)) {
+			nm_info ("Error executing command: (%d) %s",
+			         error ? error->code : 0, (error && error->message) ? error->message : "unknown");
+			if (error)
+				g_error_free (error);
+		} else if (WEXITSTATUS (status))
+			nm_info ("** Command returned exit status %d.", WEXITSTATUS (status));
+
+		g_strfreev (argv);
+	}
+
+	g_slist_free (list);
+
+	/* Clear the share rule list when sharing is stopped */
+	if (!shared)
+		clear_share_rules (req);
 }
 
 gboolean
@@ -662,6 +750,24 @@
 	return NM_ACT_REQUEST_GET_PRIVATE (req)->shared;
 }
 
+void
+nm_act_request_add_share_rule (NMActRequest *req,
+                               const char *table,
+                               const char *table_rule)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	ShareRule *rule;
+
+	g_return_if_fail (NM_IS_ACT_REQUEST (req));
+	g_return_if_fail (table != NULL);
+	g_return_if_fail (table_rule != NULL);
+	
+	rule = g_malloc0 (sizeof (ShareRule));
+	rule->table = g_strdup (table);
+	rule->rule = g_strdup (table_rule);
+	priv->share_rules = g_slist_append (priv->share_rules, rule);
+}
+
 GObject *
 nm_act_request_get_device (NMActRequest *req)
 {

Modified: trunk/src/nm-activation-request.h
==============================================================================
--- trunk/src/nm-activation-request.h	(original)
+++ trunk/src/nm-activation-request.h	Fri Aug 15 15:34:28 2008
@@ -95,6 +95,10 @@
 
 void          nm_act_request_set_shared (NMActRequest *req, gboolean shared);
 
+void          nm_act_request_add_share_rule (NMActRequest *req,
+                                             const char *table,
+                                             const char *rule);
+
 GObject *     nm_act_request_get_device (NMActRequest *req);
 
 #endif /* NM_ACTIVATION_REQUEST_H */

Modified: trunk/src/nm-device.c
==============================================================================
--- trunk/src/nm-device.c	(original)
+++ trunk/src/nm-device.c	Fri Aug 15 15:34:28 2008
@@ -33,6 +33,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <arpa/inet.h>
+#include <fcntl.h>
 
 #include "nm-device-interface.h"
 #include "nm-device.h"
@@ -1199,6 +1200,140 @@
 	         nm_device_get_iface (self));
 }
 
+static void
+share_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static gboolean
+share_init (void)
+{
+	int fd, count, status;
+	char *modules[] = { "ip_tables", "iptable_nat", "nf_nat_ftp", "nf_nat_irc",
+	                    "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323",
+	                    NULL };
+	char **iter;
+
+	fd = open ("/proc/sys/net/ipv4/ip_forward", O_WRONLY | O_TRUNC);
+	if (fd) {
+		count = write (fd, "1\n", 2);
+		if (count != 2) {
+			nm_warning ("%s: Error starting IP forwarding: (%d) %s",
+			            __func__, errno, strerror (errno));
+			return FALSE;
+		}
+		close (fd);
+	}
+
+	fd = open ("/proc/sys/net/ipv4/ip_dynaddr", O_WRONLY | O_TRUNC);
+	if (fd) {
+		count = write (fd, "1\n", 2);
+		if (count != 2) {
+			nm_warning ("%s: Error starting IP forwarding: (%d) %s",
+			            __func__, errno, strerror (errno));
+		}
+		close (fd);
+	}
+
+	for (iter = modules; *iter; iter++) {
+		char *argv[3] = { "/sbin/modprobe", *iter, NULL };
+		char *envp[1] = { NULL };
+		GError *error = NULL;
+
+		if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+		                   share_child_setup, NULL, NULL, NULL, &status, &error)) {
+			nm_info ("%s: Error loading NAT module %s: (%d) %s",
+			         __func__, *iter, error ? error->code : 0,
+			         (error && error->message) ? error->message : "unknown");
+			if (error)
+				g_error_free (error);
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+add_share_rule (NMActRequest *req, const char *table, const char *fmt, ...)
+{
+	va_list args;
+	char *cmd;
+
+	va_start (args, fmt);
+	cmd = g_strdup_vprintf (fmt, args);
+	va_end (args);
+
+	nm_act_request_add_share_rule (req, table, cmd);
+	g_free (cmd);
+}
+
+static gboolean
+start_sharing (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActRequest *req;
+	GError *error = NULL;
+	char str_addr[INET_ADDRSTRLEN + 1];
+	char str_mask[INET_ADDRSTRLEN + 1];
+	guint32 netmask, network;
+	NMIP4Config *ip4_config;
+	const NMSettingIP4Address *ip4_addr;
+	const char *iface;
+
+	iface = nm_device_get_ip_iface (self);
+	if (!iface)
+		iface = nm_device_get_iface (self);
+
+	ip4_config = nm_device_get_ip4_config (self);
+	if (!ip4_config)
+		return FALSE;
+
+	ip4_addr = nm_ip4_config_get_address (ip4_config, 0);
+	if (!ip4_addr || !ip4_addr->address)
+		return FALSE;
+
+	netmask = nm_utils_ip4_prefix_to_netmask (ip4_addr->prefix);
+	if (!inet_ntop (AF_INET, &netmask, str_mask, sizeof (str_mask)))
+		return FALSE;
+
+	network = ip4_addr->address & netmask;
+	if (!inet_ntop (AF_INET, &network, str_addr, sizeof (str_addr)))
+		return FALSE;
+
+	if (!share_init ())
+		return FALSE;
+
+	req = nm_device_get_act_request (self);
+	g_assert (req);
+
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT", iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT", iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT", iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT", iface);
+	add_share_rule (req, "filter", "FORWARD --in-interface %s --jump REJECT", iface);
+	add_share_rule (req, "filter", "FORWARD --out-interface %s --jump REJECT", iface);
+	add_share_rule (req, "filter", "FORWARD --in-interface %s --out-interface %s --jump ACCEPT", iface, iface);
+	add_share_rule (req, "filter", "FORWARD --source %s/%s --in-interface %s --jump ACCEPT", str_addr, str_mask, iface);
+	add_share_rule (req, "filter", "FORWARD --destination %s/%s --out-interface %s --match state --state ESTABLISHED,RELATED --jump ACCEPT", str_addr, str_mask, iface);
+	add_share_rule (req, "nat", "POSTROUTING --source %s/%s --destination ! %s/%s --jump MASQUERADE", str_addr, str_mask, str_addr, str_mask);
+
+	nm_act_request_set_shared (req, TRUE);
+
+	if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, ip4_config, &error)) {
+		nm_warning ("(%s): failed to start dnsmasq: %s", iface, error->message);
+		g_error_free (error);
+		nm_act_request_set_shared (req, FALSE);
+		return FALSE;
+	}
+
+	priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, "state-changed",
+	                                           G_CALLBACK (dnsmasq_state_changed_cb),
+	                                           self);
+	return TRUE;
+}
 
 /*
  * nm_device_activate_stage5_ip_config_commit
@@ -1237,18 +1372,11 @@
 	connection = nm_act_request_get_connection (nm_device_get_act_request (self));
 	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
 	if (s_ip4 && !strcmp (s_ip4->method, "shared")) {
-		GError *error = NULL;
-
-		if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, ip4_config, &error)) {
-			nm_warning ("(%s): failed to start dnsmasq: %s", iface, error->message);
-			g_error_free (error);
+		if (!start_sharing (self)) {
+			nm_warning ("Activation (%s) Stage 5 of 5 (IP Configure Commit) start sharing failed.", iface);
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
 			goto out;
 		}
-
-		priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, "state-changed",
-		                                           G_CALLBACK (dnsmasq_state_changed_cb),
-		                                           self);
 	}
 
 	nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]