示例#1
0
static void
update_connection (NMDevice *device, NMConnection *connection)
{
	NMDeviceTun *self = NM_DEVICE_TUN (device);
	NMSettingTun *s_tun = nm_connection_get_setting_tun (connection);
	NMPlatformTunProperties props;
	NMSettingTunMode mode;
	gint64 user, group;
	char *str;

	if (!s_tun) {
		s_tun = (NMSettingTun *) nm_setting_tun_new ();
		nm_connection_add_setting (connection, (NMSetting *) s_tun);
	}

	if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
		_LOGW (LOGD_HW, "failed to get TUN interface info while updating connection.");
		return;
	}

	mode = tun_mode_from_string (props.mode);

	if (mode != nm_setting_tun_get_mode (s_tun))
		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MODE, mode, NULL);

	user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1);
	group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1);

	if (props.owner != user) {
		str = props.owner >= 0 ? g_strdup_printf ("%" G_GINT32_FORMAT, (gint32) props.owner) : NULL;
		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_OWNER, str, NULL);
		g_free (str);
	}

	if (props.group != group) {
		str = props.group >= 0 ? g_strdup_printf ("%" G_GINT32_FORMAT, (gint32) props.group) : NULL;
		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_GROUP, str, NULL);
		g_free (str);
	}

	if ((!props.no_pi) != nm_setting_tun_get_pi (s_tun))
		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_PI, !props.no_pi, NULL);
	if (props.vnet_hdr != nm_setting_tun_get_vnet_hdr (s_tun))
		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_VNET_HDR, props.vnet_hdr, NULL);
	if (props.multi_queue != nm_setting_tun_get_multi_queue (s_tun))
		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MULTI_QUEUE, props.multi_queue, NULL);
}
int
plugin_init (void)
{
	GDBusConnection *bus;
	GError *error = NULL;
	const char *bus_name;

	nm_g_type_init ();

	g_return_val_if_fail (!gl.proxy, -1);

	bus_name = getenv ("NM_DBUS_SERVICE_L2TP");
	if (!bus_name)
		bus_name = NM_DBUS_SERVICE_L2TP;

	gl.log_level = _nm_utils_ascii_str_to_int64 (getenv ("NM_VPN_LOG_LEVEL"),
	                                             10, 0, LOG_DEBUG,
	                                             LOG_NOTICE);
	gl.log_prefix_token = getenv ("NM_VPN_LOG_PREFIX_TOKEN") ?: "???";

	_LOGI ("initializing");

	bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
	if (!bus) {
		_LOGE ("couldn't connect to system bus: %s",
		       error->message);
		g_error_free (error);
		return -1;
	}

	gl.proxy = g_dbus_proxy_new_sync (bus,
	                                  G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
	                                  NULL,
	                                  bus_name,
	                                  NM_DBUS_PATH_L2TP_PPP,
	                                  NM_DBUS_INTERFACE_L2TP_PPP,
	                                  NULL, &error);
	g_object_unref (bus);

	if (!gl.proxy) {
		_LOGE ("couldn't create D-Bus proxy: %s",
		       error->message);
		g_error_free (error);
		return -1;
	}

	chap_passwd_hook = get_credentials;
	chap_check_hook = get_chap_check;
	pap_passwd_hook = get_credentials;
	pap_check_hook = get_pap_check;
#ifdef USE_EAPTLS
	eaptls_passwd_hook = get_credentials;
#endif

	add_notifier (&phasechange, nm_phasechange, NULL);
	add_notifier (&ip_up_notifier, nm_ip_up, NULL);
	add_notifier (&exitnotify, nm_exit_notify, NULL);
	return 0;
}
static void
handle_event (GDBusConnection  *connection,
              const char       *sender_name,
              const char       *object_path,
              const char       *interface_name,
              const char       *signal_name,
              GVariant         *parameters,
              gpointer          user_data)
{
	NMDhcpListener *self = NM_DHCP_LISTENER (user_data);
	char *iface = NULL;
	char *pid_str = NULL;
	char *reason = NULL;
	gint pid;
	gboolean handled = FALSE;
	GVariant *options;

	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})")))
		return;

	g_variant_get (parameters, "(@a{sv})", &options);

	iface = get_option (options, "interface");
	if (iface == NULL) {
		nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
		goto out;
	}

	pid_str = get_option (options, "pid");
	pid = _nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1);
	if (pid == -1) {
		nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
		goto out;
	}

	reason = get_option (options, "reason");
	if (reason == NULL) {
		nm_log_warn (LOGD_DHCP, "(pid %d) DHCP event didn't have a reason", pid);
		goto out;
	}

	g_signal_emit (self, signals[EVENT], 0, iface, pid, options, reason, &handled);
	if (!handled) {
		if (g_ascii_strcasecmp (reason, "RELEASE") == 0) {
			/* Ignore event when the dhcp client gets killed and we receive its last message */
			nm_log_dbg (LOGD_DHCP, "(pid %d) unhandled RELEASE DHCP event for interface %s", pid, iface);
		} else
			nm_log_warn (LOGD_DHCP, "(pid %d) unhandled DHCP event for interface %s", pid, iface);
	}

out:
	g_free (iface);
	g_free (pid_str);
	g_free (reason);
	g_variant_unref (options);
}
示例#4
0
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection)
{
	NMDeviceTun *self = NM_DEVICE_TUN (device);
	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
	NMSettingTunMode mode;
	NMSettingTun *s_tun;
	gint64 user, group;

	if (!NM_DEVICE_CLASS (nm_device_tun_parent_class)->check_connection_compatible (device, connection))
		return FALSE;

	s_tun = nm_connection_get_setting_tun (connection);
	if (!s_tun)
		return FALSE;

	if (nm_device_is_real (device)) {
		mode = tun_mode_from_string (priv->mode);
		if (mode != nm_setting_tun_get_mode (s_tun))
			return FALSE;

		user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1);
		group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1);

		if (user != priv->props.owner)
			return FALSE;
		if (group != priv->props.group)
			return FALSE;
		if (nm_setting_tun_get_pi (s_tun) == priv->props.no_pi)
			return FALSE;
		if (nm_setting_tun_get_vnet_hdr (s_tun) != priv->props.vnet_hdr)
			return FALSE;
		if (nm_setting_tun_get_multi_queue (s_tun) != priv->props.multi_queue)
			return FALSE;
	}

	return TRUE;
}
示例#5
0
static gboolean
create_and_realize (NMDevice *device,
                    NMConnection *connection,
                    NMDevice *parent,
                    const NMPlatformLink **out_plink,
                    GError **error)
{
	const char *iface = nm_device_get_iface (device);
	NMPlatformError plerr;
	NMSettingTun *s_tun;
	gint64 user, group;

	s_tun = nm_connection_get_setting_tun (connection);
	g_assert (s_tun);

	user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1);
	group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1);

	plerr = nm_platform_link_tun_add (NM_PLATFORM_GET, iface,
	                                  nm_setting_tun_get_mode (s_tun) == NM_SETTING_TUN_MODE_TAP,
	                                  user, group,
	                                  nm_setting_tun_get_pi (s_tun),
	                                  nm_setting_tun_get_vnet_hdr (s_tun),
	                                  nm_setting_tun_get_multi_queue (s_tun),
	                                  out_plink);
	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
		             "Failed to create TUN/TAP interface '%s' for '%s': %s",
		             iface,
		             nm_connection_get_id (connection),
		             nm_platform_error_to_string (plerr));
		return FALSE;
	}

	return TRUE;
}
static gboolean
create_and_realize (NMDevice *device,
                    NMConnection *connection,
                    NMDevice *parent,
                    const NMPlatformLink **out_plink,
                    GError **error)
{
	const char *iface = nm_device_get_iface (device);
	NMSettingIPTunnel *s_ip_tunnel;
	NMPlatformError plerr;
	NMPlatformLnkGre lnk_gre = { };
	NMPlatformLnkSit lnk_sit = { };
	NMPlatformLnkIpIp lnk_ipip = { };
	NMPlatformLnkIp6Tnl lnk_ip6tnl = { };
	const char *str;
	gint64 val;

	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
	g_assert (s_ip_tunnel);

	switch (nm_setting_ip_tunnel_get_mode (s_ip_tunnel)) {
	case NM_IP_TUNNEL_MODE_GRE:
		if (parent)
			lnk_gre.parent_ifindex = nm_device_get_ifindex (parent);

		str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
		if (str)
			inet_pton (AF_INET, str, &lnk_gre.local);

		str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
		g_assert (str);
		inet_pton (AF_INET, str, &lnk_gre.remote);

		lnk_gre.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
		lnk_gre.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
		lnk_gre.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel);

		val = _nm_utils_ascii_str_to_int64 (nm_setting_ip_tunnel_get_input_key (s_ip_tunnel),
		                                    10,
		                                    0,
		                                    G_MAXUINT32,
		                                    -1);
		if (val != -1) {
			lnk_gre.input_key = val;
			lnk_gre.input_flags = NM_GRE_KEY;
		}

		val = _nm_utils_ascii_str_to_int64 (nm_setting_ip_tunnel_get_output_key (s_ip_tunnel),
		                                    10,
		                                    0,
		                                    G_MAXUINT32,
		                                    -1);
		if (val != -1) {
			lnk_gre.output_key = val;
			lnk_gre.output_flags = NM_GRE_KEY;
		}

		plerr = nm_platform_link_gre_add (NM_PLATFORM_GET, iface, &lnk_gre, out_plink);
		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
			             "Failed to create GRE interface '%s' for '%s': %s",
			             iface,
			             nm_connection_get_id (connection),
			             nm_platform_error_to_string (plerr));
			return FALSE;
		}
		break;
	case NM_IP_TUNNEL_MODE_SIT:
		if (parent)
			lnk_sit.parent_ifindex = nm_device_get_ifindex (parent);

		str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
		if (str)
			inet_pton (AF_INET, str, &lnk_sit.local);

		str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
		g_assert (str);
		inet_pton (AF_INET, str, &lnk_sit.remote);

		lnk_sit.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
		lnk_sit.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
		lnk_sit.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel);

		plerr = nm_platform_link_sit_add (NM_PLATFORM_GET, iface, &lnk_sit, out_plink);
		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
					"Failed to create SIT interface '%s' for '%s': %s",
					iface,
					nm_connection_get_id (connection),
					nm_platform_error_to_string (plerr));
			return FALSE;
		}
		break;
	case NM_IP_TUNNEL_MODE_IPIP:
		if (parent)
			lnk_ipip.parent_ifindex = nm_device_get_ifindex (parent);

		str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
		if (str)
			inet_pton (AF_INET, str, &lnk_ipip.local);

		str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
		g_assert (str);
		inet_pton (AF_INET, str, &lnk_ipip.remote);

		lnk_ipip.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
		lnk_ipip.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
		lnk_ipip.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel);

		plerr = nm_platform_link_ipip_add (NM_PLATFORM_GET, iface, &lnk_ipip, out_plink);
		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
					"Failed to create IPIP interface '%s' for '%s': %s",
					iface,
					nm_connection_get_id (connection),
					nm_platform_error_to_string (plerr));
			return FALSE;
		}
		break;
	case NM_IP_TUNNEL_MODE_IPIP6:
	case NM_IP_TUNNEL_MODE_IP6IP6:
		if (parent)
			lnk_ip6tnl.parent_ifindex = nm_device_get_ifindex (parent);

		str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
		if (str)
			inet_pton (AF_INET6, str, &lnk_ip6tnl.local);

		str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
		g_assert (str);
		inet_pton (AF_INET6, str, &lnk_ip6tnl.remote);

		lnk_ip6tnl.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
		lnk_ip6tnl.tclass = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
		lnk_ip6tnl.encap_limit = nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel);
		lnk_ip6tnl.flow_label = nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel);
		lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6 ? IPPROTO_IPIP : IPPROTO_IPV6;

		plerr = nm_platform_link_ip6tnl_add (NM_PLATFORM_GET, iface, &lnk_ip6tnl, out_plink);
		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
			             "Failed to create IPIP interface '%s' for '%s': %s",
			             iface,
			             nm_connection_get_id (connection),
			             nm_platform_error_to_string (plerr));
			return FALSE;
		}
		break;
	default:
		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
		             "Failed to create IP tunnel interface '%s' for '%s': mode %d not supported",
		             iface,
		             nm_connection_get_id (connection),
		             (int) nm_setting_ip_tunnel_get_mode (s_ip_tunnel));
		return FALSE;
	}

	return TRUE;
}
int
main (int argc, char *argv[])
{
	NMDBusLibreswanHelper *proxy;
	GVariantBuilder environment;
	GError *err = NULL;
	gchar **env;
	gchar **p;
	const char *bus_name = NM_DBUS_SERVICE_LIBRESWAN;
	char *str = NULL;
	char **i_env;

#if !GLIB_CHECK_VERSION (2, 35, 0)
	g_type_init ();
#endif

	/* support old command line arguments. The only reason for that is to
	 * support update of the plugin while being connected. */
	switch (argc) {
	case 1:
		break;
	case 4:
		gl.log_level = _nm_utils_ascii_str_to_int64 (argv[1], 10, 0, LOG_DEBUG, 0);
		gl.log_prefix_token = argv[2];
		bus_name = argv[3];
		break;
	case 3:
		if (strcmp (argv[1], "--bus-name") == 0) {
			bus_name = argv[2];
			break;
		}
		/* fallthrough */
	default:
		g_printerr ("Usage: %s <LEVEL> <PREFIX_TOKEN> <BUS_NAME>\n", argv[0]);
		exit (1);
	}

	if (!g_dbus_is_name (bus_name)) {
		g_printerr ("Not a valid bus name: '%s'\n", bus_name);
		exit (1);
	}

	_LOGD ("command line: %s", (str = g_strjoinv (" ", argv)));
	g_clear_pointer (&str, g_free);

	for (i_env = environ; i_env && *i_env; i_env++)
		_LOGD ("environment: %s", *i_env);

	proxy = nmdbus_libreswan_helper_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
	                                                        G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
	                                                        bus_name,
	                                                        NM_DBUS_PATH_LIBRESWAN_HELPER,
	                                                        NULL, &err);
	if (!proxy) {
		_LOGW ("Could not create a D-Bus proxy: %s", err->message);
		g_error_free (err);
		exit (1);
	}

	g_variant_builder_init (&environment, G_VARIANT_TYPE ("a{ss}"));
	env = g_listenv ();
	for (p = env; *p; p++) {
		if (strncmp ("PLUTO_", *p, 6))
			continue;
		g_variant_builder_add (&environment, "{ss}", *p, g_getenv (*p));
	}
	g_strfreev (env);

	if (!nmdbus_libreswan_helper_call_callback_sync (proxy,
	                                                 g_variant_builder_end (&environment),
	                                                 NULL, &err)) {
		_LOGW ("Could not call the plugin: %s", err->message);
		g_error_free (err);
		g_object_unref (proxy);
		exit (1);
	}

	g_object_unref (proxy);

	exit (0);
}
static gboolean
verify (NMSetting *setting, NMConnection *connection, GError **error)
{
	NMSettingIPTunnelPrivate *priv = NM_SETTING_IP_TUNNEL_GET_PRIVATE (setting);
	int family = AF_UNSPEC;

	switch (priv->mode) {
	case NM_IP_TUNNEL_MODE_IPIP:
	case NM_IP_TUNNEL_MODE_SIT:
	case NM_IP_TUNNEL_MODE_ISATAP:
	case NM_IP_TUNNEL_MODE_GRE:
	case NM_IP_TUNNEL_MODE_VTI:
		family = AF_INET;
		break;
	case NM_IP_TUNNEL_MODE_IP6IP6:
	case NM_IP_TUNNEL_MODE_IPIP6:
	case NM_IP_TUNNEL_MODE_IP6GRE:
	case NM_IP_TUNNEL_MODE_VTI6:
		family = AF_INET6;
		break;
	case NM_IP_TUNNEL_MODE_UNKNOWN:
		break;
	}

	if (family == AF_UNSPEC) {
		g_set_error (error,
		             NM_CONNECTION_ERROR,
		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
		             _("'%d' is not a valid tunnel mode"),
		             (int) priv->mode);
		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME, NM_SETTING_IP_TUNNEL_MODE);
		return FALSE;
	}

	if (   priv->parent
	    && !nm_utils_iface_valid_name (priv->parent)
	    && !nm_utils_is_uuid (priv->parent)) {
		g_set_error (error,
		             NM_CONNECTION_ERROR,
		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
		             _("'%s' is neither an UUID nor an interface name"),
		             priv->parent);
		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME,
		                NM_SETTING_IP_TUNNEL_PARENT);
		return FALSE;
	}

	if (priv->local && !nm_utils_ipaddr_valid (family, priv->local)) {
		g_set_error (error,
		             NM_CONNECTION_ERROR,
		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
		             _("'%s' is not a valid IPv%c address"),
		             priv->local,
		             family == AF_INET ? '4' : '6');
		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME, NM_SETTING_IP_TUNNEL_LOCAL);
		return FALSE;
	}

	if (!priv->remote) {
		g_set_error_literal (error,
		                     NM_CONNECTION_ERROR,
		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
		                     _("property is missing"));
		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME, NM_SETTING_IP_TUNNEL_REMOTE);
		return FALSE;
	}

	if (!nm_utils_ipaddr_valid (family, priv->remote)) {
		g_set_error (error,
		             NM_CONNECTION_ERROR,
		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
		             _("'%s' is not a valid IPv%c address"),
		             priv->remote,
		             family == AF_INET ? '4' : '6');
		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME, NM_SETTING_IP_TUNNEL_REMOTE);
		return FALSE;
	}

	if (   (priv->input_key && priv->input_key[0])
	    || (priv->output_key && priv->output_key[0])) {
		if (   priv->mode != NM_IP_TUNNEL_MODE_GRE
		    && priv->mode != NM_IP_TUNNEL_MODE_IP6GRE) {
			g_set_error_literal (error,
			                     NM_CONNECTION_ERROR,
			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
			                     _("tunnel keys can only be specified for GRE tunnels"));
			return FALSE;
		}
	}

	if (priv->input_key && priv->input_key[0]) {
		gint64 val;

		val = _nm_utils_ascii_str_to_int64 (priv->input_key, 10, 0, G_MAXUINT32, -1);
		if (val == -1) {
			g_set_error (error,
			             NM_CONNECTION_ERROR,
			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
			             _("'%s' is not a valid tunnel key"),
			             priv->input_key);
			g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME,
			                NM_SETTING_IP_TUNNEL_INPUT_KEY);
		return FALSE;
		}
	}

	if (priv->output_key && priv->output_key[0]) {
		gint64 val;

		val = _nm_utils_ascii_str_to_int64 (priv->output_key, 10, 0, G_MAXUINT32, -1);
		if (val == -1) {
			g_set_error (error,
			             NM_CONNECTION_ERROR,
			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
			             _("'%s' is not a valid tunnel key"),
			             priv->output_key);
			g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME,
			                NM_SETTING_IP_TUNNEL_OUTPUT_KEY);
		return FALSE;
		}
	}

	if (!priv->path_mtu_discovery && priv->ttl != 0) {
		g_set_error_literal (error,
		                     NM_CONNECTION_ERROR,
		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
		                     _("a fixed TTL is allowed only when path MTU discovery is enabled"));
		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME,
		                NM_SETTING_IP_TUNNEL_TTL);
		return FALSE;
	}

	return TRUE;
}
int
main (int argc, char *argv[])
{
	GDBusProxy *proxy;
	GVariantBuilder builder, ip4builder, ip6builder;
	GVariant *ip4config, *ip6config;
	char *tmp;
	GVariant *val;
	int i;
	GError *err = NULL;
	GPtrArray *dns4_list, *dns6_list;
	GPtrArray *nbns_list;
	GPtrArray *dns_domains;
	struct in_addr temp_addr;
	int tapdev = -1;
	char **iter;
	int shift = 0;
	gboolean is_restart;
	gboolean has_ip4_prefix = FALSE;
	gboolean has_ip4_address = FALSE;
	gboolean has_ip6_address = FALSE;
	gchar *bus_name = NM_DBUS_SERVICE_OPENVPN;

#if !GLIB_CHECK_VERSION (2, 35, 0)
	g_type_init ();
#endif

	for (i = 1; i < argc; i++) {
		if (!strcmp (argv[i], "--")) {
			i++;
			break;
		}
		if (nm_streq (argv[i], "--debug")) {
			if (i + 2 >= argc) {
				g_printerr ("Missing debug arguments (requires <LEVEL> <PREFIX_TOKEN>)\n");
				exit (1);
			}
			gl.log_level = _nm_utils_ascii_str_to_int64 (argv[++i], 10, 0, LOG_DEBUG, 0);
			gl.log_prefix_token = argv[++i];
		} else if (!strcmp (argv[i], "--tun"))
			tapdev = 0;
		else if (!strcmp (argv[i], "--tap"))
			tapdev = 1;
		else if (!strcmp (argv[i], "--bus-name")) {
			if (++i == argc) {
				g_printerr ("Missing bus name argument\n");
				exit (1);
			}
			if (!g_dbus_is_name (argv[i])) {
				g_printerr ("Invalid bus name\n");
				exit (1);
			}
			bus_name = argv[i];
		} else
			break;
	}
	shift = i - 1;

	if (_LOGD_enabled ()) {
		GString *args;

		args = g_string_new (NULL);
		for (i = 0; i < argc; i++) {
			if (i > 0)
				g_string_append_c (args, ' ');
			if (shift && 1 + shift == i)
				g_string_append (args, "  ");
			tmp = g_strescape (argv[i], NULL);
			g_string_append_printf (args, "\"%s\"", tmp);
			g_free (tmp);
		}

		_LOGD ("command line: %s", args->str);
		g_string_free (args, TRUE);

		for (iter = environ; iter && *iter; iter++)
			_LOGD ("environment: %s", *iter);
	}

	/* shift the arguments to the right leaving only those provided by openvpn */
	argv[shift] = argv[0];
	argv += shift;
	argc -= shift;

	is_restart = argc >= 7 && !g_strcmp0 (argv[6], "restart");

	proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
	                                       G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
	                                       NULL,
	                                       bus_name,
	                                       NM_VPN_DBUS_PLUGIN_PATH,
	                                       NM_VPN_DBUS_PLUGIN_INTERFACE,
	                                       NULL, &err);
	if (!proxy) {
		_LOGW ("Could not create a D-Bus proxy: %s", err->message);
		g_error_free (err);
		exit (1);
	}

	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
	g_variant_builder_init (&ip4builder, G_VARIANT_TYPE_VARDICT);
	g_variant_builder_init (&ip6builder, G_VARIANT_TYPE_VARDICT);

	/* External world-visible VPN gateway */
	val = trusted_remote_to_gvariant ();
	if (val)
		g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, val);
	else
		helper_failed (proxy, "VPN Gateway");

	/* Internal VPN subnet gateway */
	tmp = getenv ("route_vpn_gateway");
	val = addr4_to_gvariant (tmp);
	if (val)
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val);
	else {
		val = addr6_to_gvariant (tmp);
		if (val)
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, val);
	}

	/* VPN device */
	tmp = getenv ("dev");
	val = str_to_gvariant (tmp, FALSE);
	if (val)
		g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_TUNDEV, val);
	else
		helper_failed (proxy, "Tunnel Device");

	if (tapdev == -1)
		tapdev = strncmp (tmp, "tap", 3) == 0;

	/* IPv4 address */
	tmp = getenv ("ifconfig_local");
	if (!tmp && is_restart)
		tmp = argv[4];
	if (tmp && strlen (tmp)) {
		val = addr4_to_gvariant (tmp);
		if (val) {
			has_ip4_address = TRUE;
			g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
		} else
			helper_failed (proxy, "IP4 Address");
	}

	/* PTP address; for vpnc PTP address == internal IP4 address */
	tmp = getenv ("ifconfig_remote");
	if (!tmp && is_restart)
		tmp = argv[5];
	val = addr4_to_gvariant (tmp);
	if (val) {
		/* Sigh.  Openvpn added 'topology' stuff in 2.1 that changes the meaning
		 * of the ifconfig bits without actually telling you what they are
		 * supposed to mean; basically relying on specific 'ifconfig' behavior.
		 */
		if (tmp && !strncmp (tmp, "255.", 4)) {
			guint32 addr;

			/* probably a netmask, not a PTP address; topology == subnet */
			addr = g_variant_get_uint32 (val);
			g_variant_unref (val);
			val = g_variant_new_uint32 (nm_utils_ip4_netmask_to_prefix (addr));
			g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
			has_ip4_prefix = TRUE;
		} else
			g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
	}

	/* Netmask
	 *
	 * Either TAP or TUN modes can have an arbitrary netmask in newer versions
	 * of openvpn, while in older versions only TAP mode would.  So accept a
	 * netmask if passed, otherwise default to /32 for TUN devices since they
	 * are usually point-to-point.
	 */
	tmp = getenv ("ifconfig_netmask");
	if (tmp && inet_pton (AF_INET, tmp, &temp_addr) > 0) {
		val = g_variant_new_uint32 (nm_utils_ip4_netmask_to_prefix (temp_addr.s_addr));
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
	} else if (!tapdev) {
		if (has_ip4_address && !has_ip4_prefix) {
			val = g_variant_new_uint32 (32);
			g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
		}
	} else
		_LOGW ("No IP4 netmask/prefix (missing or invalid 'ifconfig_netmask')");

	val = get_ip4_routes ();
	if (val)
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, val);
	else if (is_restart) {
		g_variant_builder_add (&ip4builder, "{sv}",
		                       NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES,
		                       g_variant_new_boolean (TRUE));
	}

	/* IPv6 address */
	tmp = getenv ("ifconfig_ipv6_local");
	if (tmp && strlen (tmp)) {
		val = addr6_to_gvariant (tmp);
		if (val) {
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, val);
			has_ip6_address = TRUE;
		} else
			helper_failed (proxy, "IP6 Address");
	}

	/* IPv6 remote address */
	tmp = getenv ("ifconfig_ipv6_remote");
	if (tmp && strlen (tmp)) {
		val = addr6_to_gvariant (tmp);
		if (val)
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PTP, val);
		else
			helper_failed (proxy, "IP6 PTP Address");
	}

	/* IPv6 netbits */
	tmp = getenv ("ifconfig_ipv6_netbits");
	if (tmp && strlen (tmp)) {
		long int netbits;

		errno = 0;
		netbits = strtol (tmp, NULL, 10);
		if (errno || netbits < 0 || netbits > 128) {
			_LOGW ("Ignoring invalid prefix '%s'", tmp);
		} else {
			val = g_variant_new_uint32 ((guint32) netbits);
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, val);
		}
	}

	val = get_ip6_routes ();
	if (val)
		g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, val);
	else if (is_restart) {
		g_variant_builder_add (&ip6builder, "{sv}",
		                       NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES,
		                       g_variant_new_boolean (TRUE));
	}

	/* DNS and WINS servers */
	dns_domains = g_ptr_array_sized_new (3);
	dns4_list = g_ptr_array_new ();
	dns6_list = g_ptr_array_new ();
	nbns_list = g_ptr_array_new ();

	for (i = 1; i < 256; i++) {
		char *env_name;

		env_name = g_strdup_printf ("foreign_option_%d", i);
		tmp = getenv (env_name);
		g_free (env_name);

		if (!tmp || strlen (tmp) < 1)
			break;

		if (!g_str_has_prefix (tmp, "dhcp-option "))
			continue;

		tmp += 12; /* strlen ("dhcp-option ") */

		if (g_str_has_prefix (tmp, "DNS "))
			parse_addr_list (dns4_list, dns6_list, tmp + 4);
		else if (g_str_has_prefix (tmp, "WINS "))
			parse_addr_list (nbns_list, NULL, tmp + 5);
		else if (g_str_has_prefix (tmp, "DOMAIN ") && is_domain_valid (tmp + 7))
			g_ptr_array_add (dns_domains, tmp + 7);
	}

	if (dns4_list->len) {
		val = g_variant_new_array (G_VARIANT_TYPE_UINT32, (GVariant **) dns4_list->pdata, dns4_list->len);
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS, val);
	}

	if (has_ip6_address && dns6_list->len) {
		val = g_variant_new_array (G_VARIANT_TYPE ("ay"), (GVariant **) dns6_list->pdata, dns6_list->len);
		g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DNS, val);
	}

	if (nbns_list->len) {
		val = g_variant_new_array (G_VARIANT_TYPE_UINT32, (GVariant **) nbns_list->pdata, nbns_list->len);
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NBNS, val);
	}

	if (dns_domains->len) {
		val = g_variant_new_strv ((const gchar **) dns_domains->pdata, dns_domains->len);
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS, val);

		/* Domains apply to both IPv4 and IPv6 configurations */
		if (has_ip6_address) {
			val = g_variant_new_strv ((const gchar **) dns_domains->pdata, dns_domains->len);
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS, val);
		}
	}

	g_ptr_array_unref (dns4_list);
	g_ptr_array_unref (dns6_list);
	g_ptr_array_unref (nbns_list);
	g_ptr_array_unref (dns_domains);

	/* Tunnel MTU */
	tmp = getenv ("tun_mtu");
	if (tmp && strlen (tmp)) {
		long int mtu;

		errno = 0;
		mtu = strtol (tmp, NULL, 10);
		if (errno || mtu < 0 || mtu > 20000) {
			_LOGW ("Ignoring invalid tunnel MTU '%s'", tmp);
		} else {
			val = g_variant_new_uint32 ((guint32) mtu);
			g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_MTU, val);
		}
	}

	ip4config = g_variant_builder_end (&ip4builder);

	if (g_variant_n_children (ip4config)) {
		val = g_variant_new_boolean (TRUE);
		g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP4, val);
	} else {
		g_variant_unref (ip4config);
		ip4config = NULL;
	}

	ip6config = g_variant_builder_end (&ip6builder);

	if (g_variant_n_children (ip6config)) {
		val = g_variant_new_boolean (TRUE);
		g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP6, val);
	} else {
		g_variant_unref (ip6config);
		ip6config = NULL;
	}

	if (!ip4config && !ip6config)
		helper_failed (proxy, "IPv4 or IPv6 configuration");

	/* Send the config info to nm-openvpn-service */
	send_config (proxy, g_variant_builder_end (&builder), ip4config, ip6config);

	g_object_unref (proxy);

	return 0;
}