diff --git a/ChangeLog b/ChangeLog index 82c34ce84..c56182cd7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,18 @@ 2007-08-15 Tambet Ingo + * src/ppp-manager: Implement ppp-manager. It's sort of dead code for now since + nothing is using it at the moment, but it'll be all useful and stuff later on. + + * libnm-util/nm-setting.h: Define NMSettingPPP. + + * libnm-util/nm-setting.c: Implement NMSettingPPP. + + * libnm-util/nm-connection.c (register_default_creators): Register ppp setting. + + * src/Makefile.am: Add ppp-manager to SUBDIRS. + + * configure.in: Require ppp headers. Build Makefile for ppp-manager. + * introspection/Makefile.am: Add nm-manager-client.xml to EXTRA_DIST. 2007-08-14 Tambet Ingo diff --git a/configure.in b/configure.in index 7d570caa2..eb89cd8ad 100644 --- a/configure.in +++ b/configure.in @@ -215,6 +215,11 @@ fi AC_DEFINE_UNQUOTED(IP_BINARY_PATH, "$IP_BINARY_PATH", [Define to path of ip binary]) AC_SUBST(IP_BINARY_PATH) +# PPPD +AC_CHECK_HEADERS(pppd/pppd.h,, + AC_MSG_ERROR(couldn't find pppd.h. pppd-devel package is required.)) + + AC_ARG_ENABLE(more-warnings, AC_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings]), set_more_warnings="$enableval",[ @@ -259,6 +264,7 @@ src/named-manager/Makefile src/vpn-manager/Makefile src/dhcp-manager/Makefile src/supplicant-manager/Makefile +src/ppp-manager/Makefile src/backends/Makefile libnm-util/libnm-util.pc libnm-util/Makefile diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c index a56c9a16b..6ec30f0e6 100644 --- a/libnm-util/nm-connection.c +++ b/libnm-util/nm-connection.c @@ -17,6 +17,7 @@ register_default_creators (void) { "802-11-wireless", nm_setting_wireless_new_from_hash }, { "ipv4", nm_setting_ip4_config_new_from_hash }, { "802-11-wireless-security", nm_setting_wireless_security_new_from_hash }, + { "ppp", nm_setting_ppp_new_from_hash }, { NULL, NULL} }; diff --git a/libnm-util/nm-setting.c b/libnm-util/nm-setting.c index 991603e9f..5e2e6f67c 100644 --- a/libnm-util/nm-setting.c +++ b/libnm-util/nm-setting.c @@ -1,3 +1,5 @@ +/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ + #include #include #include @@ -1147,3 +1149,149 @@ nm_setting_wireless_security_new_from_hash (GHashTable *settings) return setting; } + +/* PPP */ + +static gboolean +setting_ppp_verify (NMSetting *setting, GHashTable *all_settings) +{ + /* FIXME: Do we even want this or can we just let pppd evaluate the options? */ + return TRUE; +} + +static GHashTable * +setting_ppp_hash (NMSetting *setting) +{ + NMSettingPPP *self = (NMSettingPPP *) setting; + GHashTable *hash; + + hash = setting_hash_new (); + + g_hash_table_insert (hash, "noauth", boolean_to_gvalue (self->noauth)); + g_hash_table_insert (hash, "refuse-eap", boolean_to_gvalue (self->refuse_eap)); + g_hash_table_insert (hash, "refuse-chap", boolean_to_gvalue (self->refuse_chap)); + g_hash_table_insert (hash, "refuse-mschap", boolean_to_gvalue (self->refuse_mschap)); + g_hash_table_insert (hash, "nobsdcomp", boolean_to_gvalue (self->nobsdcomp)); + g_hash_table_insert (hash, "nodeflate", boolean_to_gvalue (self->nodeflate)); + g_hash_table_insert (hash, "require-mppe", boolean_to_gvalue (self->require_mppe)); + g_hash_table_insert (hash, "require-mppe-128", boolean_to_gvalue (self->require_mppe_128)); + g_hash_table_insert (hash, "mppe-stateful", boolean_to_gvalue (self->mppe_stateful)); + g_hash_table_insert (hash, "require-mppc", boolean_to_gvalue (self->require_mppc)); + g_hash_table_insert (hash, "crtscts", boolean_to_gvalue (self->crtscts)); + g_hash_table_insert (hash, "usepeerdns", boolean_to_gvalue (self->usepeerdns)); + + g_hash_table_insert (hash, "baud", int_to_gvalue (self->baud)); + g_hash_table_insert (hash, "mru", int_to_gvalue (self->mru)); + g_hash_table_insert (hash, "mtu", int_to_gvalue (self->mtu)); + g_hash_table_insert (hash, "lcp-echo-failure", int_to_gvalue (self->lcp_echo_failure)); + g_hash_table_insert (hash, "lcp-echo-interval", int_to_gvalue (self->lcp_echo_interval)); + + return hash; +} + + +static void +setting_ppp_destroy (NMSetting *setting) +{ + NMSettingPPP *self = (NMSettingPPP *) setting; + + g_slice_free (NMSettingPPP, self); +} + +NMSetting * +nm_setting_ppp_new (void) +{ + NMSetting *setting; + + setting = (NMSetting *) g_slice_new0 (NMSettingPPP); + + setting->name = g_strdup ("ppp"); + setting->verify_fn = setting_ppp_verify; + setting->hash_fn = setting_ppp_hash; + setting->destroy_fn = setting_ppp_destroy; + + return setting; +} + +NMSetting * +nm_setting_ppp_new_from_hash (GHashTable *settings) +{ + NMSettingPPP *self; + NMSetting *setting; + GValue *value; + + g_return_val_if_fail (settings != NULL, NULL); + + setting = nm_setting_ppp_new (); + self = (NMSettingPPP *) setting; + + value = (GValue *) g_hash_table_lookup (settings, "noauth"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->noauth = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "refuse-eap"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->refuse_eap = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "refuse-chap"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->refuse_chap = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "refuse-mschap"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->refuse_mschap = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "nobsdcomp"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->nobsdcomp = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "nodeflate"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->nodeflate = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "require-mppe"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->require_mppe = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "require-mppe-128"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->require_mppe_128 = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "mppe-stateful"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->mppe_stateful = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "require-mppc"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->require_mppc = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "crtscts"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->crtscts = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "usepeerdns"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) + self->usepeerdns = g_value_get_boolean (value); + + value = (GValue *) g_hash_table_lookup (settings, "baud"); + if (value && G_VALUE_HOLDS_INT (value)) + self->baud = g_value_get_int (value); + + value = (GValue *) g_hash_table_lookup (settings, "mru"); + if (value && G_VALUE_HOLDS_INT (value)) + self->mru = g_value_get_int (value); + + value = (GValue *) g_hash_table_lookup (settings, "mtu"); + if (value && G_VALUE_HOLDS_INT (value)) + self->mtu = g_value_get_int (value); + + value = (GValue *) g_hash_table_lookup (settings, "lcp-echo-failure"); + if (value && G_VALUE_HOLDS_INT (value)) + self->lcp_echo_failure = g_value_get_int (value); + + value = (GValue *) g_hash_table_lookup (settings, "lcp-echo-interval"); + if (value && G_VALUE_HOLDS_INT (value)) + self->lcp_echo_interval = g_value_get_int (value); + + return setting; +} diff --git a/libnm-util/nm-setting.h b/libnm-util/nm-setting.h index 7f16d41f3..193345e7f 100644 --- a/libnm-util/nm-setting.h +++ b/libnm-util/nm-setting.h @@ -1,3 +1,5 @@ +/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ + #ifndef NM_SETTING_H #define NM_SETTING_H @@ -136,6 +138,34 @@ typedef struct { NMSetting *nm_setting_wireless_security_new (void); NMSetting *nm_setting_wireless_security_new_from_hash (GHashTable *settings); +/* PPP */ + +typedef struct { + NMSetting parent; + + gboolean noauth; + gboolean refuse_eap; + gboolean refuse_chap; + gboolean refuse_mschap; + gboolean nobsdcomp; + gboolean nodeflate; + gboolean require_mppe; + gboolean require_mppe_128; + gboolean mppe_stateful; + gboolean require_mppc; + gboolean crtscts; + gboolean usepeerdns; + + gint32 baud; + gint32 mru; + gint32 mtu; + gint32 lcp_echo_failure; + gint32 lcp_echo_interval; +} NMSettingPPP; + +NMSetting *nm_setting_ppp_new (void); +NMSetting *nm_setting_ppp_new_from_hash (GHashTable *settings); + G_END_DECLS #endif /* NM_SETTING_H */ diff --git a/src/Makefile.am b/src/Makefile.am index d5885e015..cc27309ba 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=named-manager vpn-manager dhcp-manager supplicant-manager backends +SUBDIRS=named-manager vpn-manager dhcp-manager supplicant-manager ppp-manager backends INCLUDES = -I${top_srcdir} \ -I${top_srcdir}/include \ diff --git a/src/ppp-manager/Makefile.am b/src/ppp-manager/Makefile.am new file mode 100644 index 000000000..9fb133ada --- /dev/null +++ b/src/ppp-manager/Makefile.am @@ -0,0 +1,62 @@ +INCLUDES = -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/utils \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src + +noinst_LTLIBRARIES = libppp-manager.la + +BUILT_SOURCES = \ + nm-ppp-marshal.h \ + nm-ppp-marshal.c + +libppp_manager_la_SOURCES = \ + nm-ppp-manager.c \ + nm-ppp-manager.h \ + nm-ppp-status.h \ + nm-ppp-marshal-main.c + +libppp_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(HAL_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLIBDIR=\"$(libdir)\" + +libppp_manager_la_LIBADD = $(DBUS_LIBS) $(GLIB_LIBS) + +nm_pppd_plugindir = $(libdir) +nm_pppd_plugin_PROGRAMS = nm-pppd-plugin.so + +nm_pppd_plugin_so_SOURCES = \ + nm-pppd-plugin.c \ + nm-pppd-plugin.h \ + nm-ppp-status.h + +nm_pppd_plugin_so_CPPFLAGS = $(DBUS_CFLAGS) $(GLIB_CFLAGS) + +nm_pppd_plugin_so_LDFLAGS = -shared +nm_pppd_plugin_so_LDADD = \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(top_builddir)/libnm-util/libnm-util.la + + + +EXTRA_DIST = nm-ppp-marshal.list +CLEANFILES = $(BUILT_SOURCES) + +nm-ppp-marshal.h: nm-ppp-marshal.list + $(GLIB_GENMARSHAL) --prefix=nm_ppp_marshal $(srcdir)/nm-ppp-marshal.list --header > \ + xgen-gmh \ + && (cmp -s xgen-gmh nm-ppp-marshal.h || cp xgen-gmh nm-ppp-marshal.h) \ + && rm -f xgen-gmh xgen-gmh~ + +nm-ppp-marshal.c: nm-ppp-marshal.list + $(GLIB_GENMARSHAL) --prefix=nm_ppp_marshal $(srcdir)/nm-ppp-marshal.list --body > \ + xgen-gmc \ + && cp xgen-gmc nm-ppp-marshal.c \ + && rm -f xgen-gmc xgen-gmc~ + +nm-ppp-marshal-main.c: nm-ppp-marshal.c nm-ppp-marshal.h + diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c new file mode 100644 index 000000000..2cf344388 --- /dev/null +++ b/src/ppp-manager/nm-ppp-manager.c @@ -0,0 +1,597 @@ +/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ + +#include +#include +#include +#include + +#include "nm-ppp-manager.h" +#include "nm-dbus-manager.h" +#include "nm-utils.h" +#include "dbus-dict-helpers.h" +#include "nm-ppp-marshal.h" + +#define NM_PPPD_PLUGIN LIBDIR "/nm-pppd-plugin.so" +#define NM_PPP_WAIT_PPPD 10000 /* 10 seconds */ + +typedef struct { + GPid pid; + NMDBusManager *dbus_manager; + + guint32 signal_handler; + guint32 ppp_timeout_handler; + guint32 name_owner_changed_handler; +} NMPPPManagerPrivate; + +#define NM_PPP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PPP_MANAGER, NMPPPManagerPrivate)) + +G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, G_TYPE_OBJECT) + +enum { + STATE_CHANGED, + IP4_CONFIG, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +GQuark +nm_ppp_manager_error_quark (void) +{ + static GQuark quark; + + if (!quark) + quark = g_quark_from_static_string ("nm_ppp_manager_error"); + + return quark; +} + +static void +nm_ppp_manager_init (NMPPPManager *manager) +{ +} + +static void +finalize (GObject *object) +{ + nm_ppp_manager_stop (NM_PPP_MANAGER (object)); + + G_OBJECT_CLASS (nm_ppp_manager_parent_class)->finalize (object); +} + +static void +nm_ppp_manager_class_init (NMPPPManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMPPPManagerPrivate)); + + object_class->finalize = finalize; + + /* signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMPPPManagerClass, state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + + signals[IP4_CONFIG] = + g_signal_new ("ip4-config", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMPPPManagerClass, ip4_config), + NULL, NULL, + nm_ppp_marshal_VOID__STRING_OBJECT, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_OBJECT); +} + +NMPPPManager * +nm_ppp_manager_new (void) +{ + return (NMPPPManager *) g_object_new (NM_TYPE_PPP_MANAGER, NULL); +} + +/*******************************************/ + +typedef struct { + GPtrArray *array; + GStringChunk *chunk; +} NMCmdLine; + +static NMCmdLine * +nm_cmd_line_new (void) +{ + NMCmdLine *cmd; + + cmd = g_slice_new (NMCmdLine); + cmd->array = g_ptr_array_new (); + cmd->chunk = g_string_chunk_new (1024); + + return cmd; +} + +static void +nm_cmd_line_destroy (NMCmdLine *cmd) +{ + g_ptr_array_free (cmd->array, TRUE); + g_string_chunk_free (cmd->chunk); + g_slice_free (NMCmdLine, cmd); +} + +static char * +nm_cmd_line_to_str (NMCmdLine *cmd) +{ + char *str; + + g_ptr_array_add (cmd->array, NULL); + str = g_strjoinv (" ", (gchar **) cmd->array->pdata); + g_ptr_array_remove_index (cmd->array, cmd->array->len - 1); + + return str; +} + +static void +nm_cmd_line_add_string (NMCmdLine *cmd, const char *str) +{ + g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str)); +} + +static void +nm_cmd_line_add_int (NMCmdLine *cmd, int i) +{ + char *str; + + str = g_strdup_printf ("%d", i); + nm_cmd_line_add_string (cmd, str); + g_free (str); +} + +/*******************************************/ + +static inline const char *nm_find_pppd (void) +{ + static const char *pppd_binary_paths[] = + { + "/usr/local/sbin/pppd", + "/usr/sbin/pppd", + "/sbin/pppd", + NULL + }; + + const char **pppd_binary = pppd_binary_paths; + + while (*pppd_binary != NULL) { + if (g_file_test (*pppd_binary, G_FILE_TEST_EXISTS)) + break; + pppd_binary++; + } + + return *pppd_binary; +} + +static void +ppp_exit_code (guint pppd_exit_status) +{ + const char *msg; + + switch (pppd_exit_status) { + case 1: + msg = "Fatal pppd error"; + break; + case 2: + msg = "pppd options error"; + break; + case 3: + msg = "No root priv error"; + break; + case 4: + msg = "No ppp module error"; + break; + case 5: + msg = "pppd received a signal"; + break; + case 6: + msg = "Serial port lock failed"; + break; + case 7: + msg = "Serial port open failed"; + break; + case 8: + msg = "Connect script failed"; + break; + case 9: + msg = "Pty program error"; + break; + case 10: + msg = "PPP negotiation failed"; + break; + case 11: + msg = "Peer didn't authenticatie itself"; + break; + case 12: + msg = "Link idle: Idle Seconds reached."; + break; + case 13: + msg = "Connect time limit reached."; + break; + case 14: + msg = "Callback negotiated, call should come back."; + break; + case 15: + msg = "Lack of LCP echo responses"; + break; + case 16: + msg = "A modem hung up the phone"; + break; + case 17: + msg = "Loopback detected"; + break; + case 18: + msg = "The init script failed"; + break; + case 19: + msg = "Authentication error.\n" + "We failed to authenticate ourselves to the peer.\n" + "Maybe bad account or password?"; + break; + default: + msg = "Unknown error"; + } + + g_warning ("pppd exited with error: %s", msg); +} + +static void +ppp_watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (user_data); + guint err; + + if (WIFEXITED (status)) { + err = WEXITSTATUS (status); + if (err != 0) + ppp_exit_code (err); + } else if (WIFSTOPPED (status)) + g_warning ("ppp stopped unexpectedly with signal %d", WSTOPSIG (status)); + else if (WIFSIGNALED (status)) + g_warning ("ppp died with signal %d", WTERMSIG (status)); + else + g_warning ("ppp died from an unknown cause"); + + /* Reap child if needed. */ + waitpid (pid, NULL, WNOHANG); + + priv->pid = 0; +} + +#define HANDLE_DICT_ITEM(in_key, in_type, in_ary_type, op) \ + if (!strcmp (entry.key, in_key)) { \ + if (entry.type != in_type) { \ + nm_warning (in_key "had invalid type in PPP IP Config message."); \ + } else { \ + if (in_type == DBUS_TYPE_ARRAY && entry.array_type != in_ary_type) { \ + nm_warning (in_key "had invalid type in PPP IP Config message."); \ + } else { \ + op \ + } \ + } \ + goto next; \ + } + +static gboolean +parse_ip4_config (DBusMessage *message, char **interface, NMIP4Config **config) +{ + DBusMessageIter iter; + DBusMessageIter iter_dict; + NMUDictEntry entry; + gboolean success = FALSE; + + dbus_message_iter_init (message, &iter); + if (!nmu_dbus_dict_open_read (&iter, &iter_dict)) { + nm_warning ("Warning: couldn't get config dictionary from PPP IP Config message."); + goto out; + } + + *config = nm_ip4_config_new (); + + while (nmu_dbus_dict_has_dict_entry (&iter_dict)) { + int i; + + if (!nmu_dbus_dict_get_entry (&iter_dict, &entry)) { + nm_warning ("Error: couldn't read dict entryfrom PPP IP Config message."); + goto out; + } + + HANDLE_DICT_ITEM("interface", DBUS_TYPE_STRING, 0, + { if (strlen (entry.str_value)) *interface = g_strdup (entry.str_value); }); + /* IP specific options */ + HANDLE_DICT_ITEM("address", DBUS_TYPE_UINT32, 0, + { nm_ip4_config_set_address (*config, entry.uint32_value); }); + HANDLE_DICT_ITEM("netmask", DBUS_TYPE_UINT32, 0, + { nm_ip4_config_set_netmask (*config, entry.uint32_value ? entry.uint32_value : 0x00FF); }); + HANDLE_DICT_ITEM("gateway", DBUS_TYPE_UINT32, 0, + { nm_ip4_config_set_gateway (*config, entry.uint32_value); }); + + /* Multiple DNS servers are allowed */ + HANDLE_DICT_ITEM("dns_server", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + { + for (i = 0; i < entry.array_len; i++) + nm_ip4_config_add_nameserver (*config, entry.uint32array_value[i]); + }); + + /* FIXME: Ignoring WINS servers for now since IP4Config doesn't have a place for it */ + + next: + nmu_dbus_dict_entry_clear (&entry); + } + + success = TRUE; + + out: + if (!success && *config) { + g_object_unref (*config); + *config = NULL; + g_free (*interface); + *interface = NULL; + } + + return success; +} + +static gboolean +nm_ppp_manager_dbus_signal_handler (DBusConnection *connection, + DBusMessage *message, + gpointer user_data) +{ + NMPPPManager *manager = NM_PPP_MANAGER (user_data); + gboolean handled = FALSE; + + if (dbus_message_is_signal (message, NM_DBUS_INTERFACE_PPP, "Status")) { + guint32 state; + + if (dbus_message_get_args (message, NULL, + DBUS_TYPE_UINT32, &state, + DBUS_TYPE_INVALID)) { + + g_signal_emit (manager, signals[STATE_CHANGED], 0, state); + handled = TRUE; + } + } else if (dbus_message_is_signal (message, NM_DBUS_INTERFACE_PPP, "IP4Config")) { + char *iface = NULL; + NMIP4Config *config = NULL; + + if (parse_ip4_config (message, &iface, &config)) { + g_signal_emit (manager, signals[IP4_CONFIG], 0, iface, config); + g_free (iface); + g_object_unref (config); + handled = TRUE; + } + } + + return handled; +} + +static gboolean +pppd_timed_out (gpointer data) +{ + NMPPPManager *manager = NM_PPP_MANAGER (data); + + nm_warning ("Looks like pppd didn't initialize our dbus module"); + nm_ppp_manager_stop (manager); + + return FALSE; +} + +static void +name_owner_changed (NMDBusManager *dbus_manager, + const char *name, + const char *old, + const char *new, + gpointer user_data) +{ + NMPPPManager *manager = NM_PPP_MANAGER (user_data); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + gboolean old_owner_good = (old && (strlen (old) > 0)); + gboolean new_owner_good = (new && (strlen (new) > 0)); + + if (strcmp (name, NM_DBUS_SERVICE_PPP)) + return; + + if (!old_owner_good && new_owner_good) { + if (priv->ppp_timeout_handler) { + g_source_remove (priv->ppp_timeout_handler); + priv->ppp_timeout_handler = 0; + } + + priv->signal_handler = nm_dbus_manager_register_signal_handler (priv->dbus_manager, + NM_DBUS_INTERFACE_PPP, + NM_DBUS_SERVICE_PPP, + nm_ppp_manager_dbus_signal_handler, + manager); + } else if (old_owner_good && !new_owner_good) { + nm_ppp_manager_stop (manager); + } +} + +static void +start_dbus_watcher (NMPPPManager *manager) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + priv->ppp_timeout_handler = g_timeout_add (NM_PPP_WAIT_PPPD, pppd_timed_out, manager); + + priv->dbus_manager = nm_dbus_manager_get (); + priv->name_owner_changed_handler = g_signal_connect (priv->dbus_manager, "name-owner-changed", + G_CALLBACK (name_owner_changed), + manager); +} + +static NMCmdLine * +create_pppd_cmd_line (NMSettingPPP *setting, const char *device, GError **err) +{ + const char *ppp_binary; + NMCmdLine *cmd; + + ppp_binary = nm_find_pppd (); + if (!ppp_binary) { + g_set_error (err, NM_PPP_MANAGER_ERROR, NM_PPP_MANAGER_ERROR, + "Could not find ppp binary."); + return NULL; + } + + /* Create pppd command line */ + cmd = nm_cmd_line_new (); + nm_cmd_line_add_string (cmd, ppp_binary); + + nm_cmd_line_add_string (cmd, "nodetach"); + nm_cmd_line_add_string (cmd, "lock"); + nm_cmd_line_add_string (cmd, device); + + if (setting->baud) + nm_cmd_line_add_int (cmd, setting->baud); + + if (setting->noauth) + nm_cmd_line_add_string (cmd, "noauth"); + if (setting->noauth) + nm_cmd_line_add_string (cmd, "refuse-eap"); + if (setting->refuse_eap) + nm_cmd_line_add_string (cmd, "refuse-chap"); + if (setting->refuse_chap) + nm_cmd_line_add_string (cmd, "refuse-mschap"); + if (setting->refuse_mschap) + nm_cmd_line_add_string (cmd, "nobsdcomp"); + if (setting->nobsdcomp) + nm_cmd_line_add_string (cmd, "nodeflate"); + if (setting->nodeflate) + nm_cmd_line_add_string (cmd, "require-mppe"); + if (setting->require_mppe) + nm_cmd_line_add_string (cmd, "require-mppe-128"); + if (setting->require_mppe_128) + nm_cmd_line_add_string (cmd, "mppe-stateful"); + if (setting->mppe_stateful) + nm_cmd_line_add_string (cmd, "require-mppc"); + if (setting->crtscts) + nm_cmd_line_add_string (cmd, "crtscts"); + if (setting->usepeerdns) + nm_cmd_line_add_string (cmd, "usepeerdns"); + + if (setting->mru) { + nm_cmd_line_add_string (cmd, "mru"); + nm_cmd_line_add_int (cmd, setting->mru); + } + + if (setting->mtu) { + nm_cmd_line_add_string (cmd, "mtu"); + nm_cmd_line_add_int (cmd, setting->mtu); + } + + if (setting->lcp_echo_failure) { + nm_cmd_line_add_string (cmd, "lcp-echo-failure"); + nm_cmd_line_add_int (cmd, setting->lcp_echo_failure); + } + + if (setting->lcp_echo_interval) { + nm_cmd_line_add_string (cmd, "lcp-echo-interval"); + nm_cmd_line_add_int (cmd, setting->lcp_echo_interval); + } + + nm_cmd_line_add_string (cmd, "plugin"); + nm_cmd_line_add_string (cmd, NM_PPPD_PLUGIN); + + return cmd; +} + +gboolean +nm_ppp_manager_start (NMPPPManager *manager, + const char *device, + NMSettingPPP *setting, + GError **err) +{ + NMPPPManagerPrivate *priv; + NMCmdLine *ppp_cmd; + char *cmd_str; + GPid pid; + int stdin_fd; + int stdout_fd; + int stderr_fd; + GSource *ppp_watch; + + g_return_val_if_fail (NM_IS_PPP_MANAGER (manager), FALSE); + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (setting != NULL, FALSE); + + ppp_cmd = create_pppd_cmd_line (setting, device, err); + if (!ppp_cmd) + return FALSE; + + /* FIXME: This should come from NMSettingIP4Config */ + nm_cmd_line_add_string (ppp_cmd, "defaultroute"); + g_ptr_array_add (ppp_cmd->array, NULL); + + priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + nm_info ("Starting pppd connection"); + + cmd_str = nm_cmd_line_to_str (ppp_cmd); + nm_debug ("Command line: %s", cmd_str); + g_free (cmd_str); + + if (!g_spawn_async_with_pipes (NULL, (char **) ppp_cmd->array->pdata, NULL, + G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &priv->pid, + &stdin_fd, &stdout_fd, &stderr_fd, err)) { + goto out; + } + + nm_debug ("ppp started with pid %d", priv->pid); + + ppp_watch = g_child_watch_source_new (pid); + g_source_set_callback (ppp_watch, (GSourceFunc) ppp_watch_cb, manager, NULL); + g_source_attach (ppp_watch, NULL); + g_source_unref (ppp_watch); + + start_dbus_watcher (manager); + + out: + if (ppp_cmd) + nm_cmd_line_destroy (ppp_cmd); + + return priv->pid > 0; +} + +void +nm_ppp_manager_stop (NMPPPManager *manager) +{ + NMPPPManagerPrivate *priv; + + g_return_if_fail (NM_IS_PPP_MANAGER (manager)); + + priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + if (priv->ppp_timeout_handler) { + g_source_remove (priv->ppp_timeout_handler); + priv->ppp_timeout_handler = 0; + } + + if (priv->signal_handler) { + nm_dbus_manager_remove_signal_handler (priv->dbus_manager, priv->signal_handler); + priv->signal_handler = 0; + } + + if (priv->dbus_manager) { + g_signal_handler_disconnect (priv->dbus_manager, priv->name_owner_changed_handler); + g_object_unref (priv->dbus_manager); + priv->dbus_manager = NULL; + } + + if (priv->pid) { + kill (priv->pid, SIGTERM); + priv->pid = 0; + } +} diff --git a/src/ppp-manager/nm-ppp-manager.h b/src/ppp-manager/nm-ppp-manager.h new file mode 100644 index 000000000..8b4c332fd --- /dev/null +++ b/src/ppp-manager/nm-ppp-manager.h @@ -0,0 +1,46 @@ +#ifndef NM_PPP_MANAGER_H +#define NM_PPP_MANAGER_H + +#include +#include + +#include "nm-ppp-status.h" +#include "nm-setting.h" +#include "nm-ip4-config.h" +#include "nm-pppd-plugin.h" + +#define NM_TYPE_PPP_MANAGER (nm_ppp_manager_get_type ()) +#define NM_PPP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PPP_MANAGER, NMPPPManager)) +#define NM_PPP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_PPP_MANAGER, NMPPPManagerClass)) +#define NM_IS_PPP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_PPP_MANAGER)) +#define NM_IS_PPP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_PPP_MANAGER)) +#define NM_PPP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_PPP_MANAGER, NMPPPManagerClass)) + +typedef struct { + GObject parent; +} NMPPPManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*state_changed) (NMPPPManager *manager, NMPPPStatus status); + void (*ip4_config) (NMPPPManager *manager, const char *iface, NMIP4Config *config); +} NMPPPManagerClass; + +GType nm_ppp_manager_get_type (void); + +NMPPPManager *nm_ppp_manager_new (void); + +gboolean nm_ppp_manager_start (NMPPPManager *manager, + const char *device, + NMSettingPPP *setting, + GError **err); + +void nm_ppp_manager_stop (NMPPPManager *manager); + + +#define NM_PPP_MANAGER_ERROR nm_ppp_manager_error_quark() +GQuark nm_ppp_manager_error_quark (void); + +#endif /* NM_PPP_MANAGER_H */ diff --git a/src/ppp-manager/nm-ppp-marshal-main.c b/src/ppp-manager/nm-ppp-marshal-main.c new file mode 100644 index 000000000..14bfd9860 --- /dev/null +++ b/src/ppp-manager/nm-ppp-marshal-main.c @@ -0,0 +1,3 @@ +#include "nm-ppp-marshal.h" +#include "nm-ppp-marshal.c" + diff --git a/src/ppp-manager/nm-ppp-marshal.list b/src/ppp-manager/nm-ppp-marshal.list new file mode 100644 index 000000000..7e665999e --- /dev/null +++ b/src/ppp-manager/nm-ppp-marshal.list @@ -0,0 +1 @@ +VOID:STRING,OBJECT diff --git a/src/ppp-manager/nm-ppp-status.h b/src/ppp-manager/nm-ppp-status.h new file mode 100644 index 000000000..9e46e542c --- /dev/null +++ b/src/ppp-manager/nm-ppp-status.h @@ -0,0 +1,22 @@ +#ifndef NM_PPP_STATUS_H +#define NM_PPP_STATUS_H + +typedef enum { + NM_PPP_STATUS_UNKNOWN, + + NM_PPP_STATUS_DEAD, + NM_PPP_STATUS_INITIALIZE, + NM_PPP_STATUS_SERIALCONN, + NM_PPP_STATUS_DORMANT, + NM_PPP_STATUS_ESTABLISH, + NM_PPP_STATUS_AUTHENTICATE, + NM_PPP_STATUS_CALLBACK, + NM_PPP_STATUS_NETWORK, + NM_PPP_STATUS_RUNNING, + NM_PPP_STATUS_TERMINATE, + NM_PPP_STATUS_DISCONNECT, + NM_PPP_STATUS_HOLDOFF, + NM_PPP_STATUS_MASTER +} NMPPPStatus; + +#endif /* NM_PPP_STATUS_H */ diff --git a/src/ppp-manager/nm-pppd-plugin.c b/src/ppp-manager/nm-pppd-plugin.c new file mode 100644 index 000000000..637d2889a --- /dev/null +++ b/src/ppp-manager/nm-pppd-plugin.c @@ -0,0 +1,307 @@ +/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nm-pppd-plugin.h" +#include "nm-ppp-status.h" +#include "dbus-dict-helpers.h" + +char pppd_version[] = VERSION; + +static void +nm_phasechange (void *data, int arg) +{ + NMPPPStatus status = NM_PPP_STATUS_UNKNOWN; + char *phase; + + switch (arg) { + case PHASE_DEAD: + status = NM_PPP_STATUS_DEAD; + phase = "dead"; + break; + case PHASE_INITIALIZE: + status = NM_PPP_STATUS_INITIALIZE; + phase = "initialize"; + break; + case PHASE_SERIALCONN: + status = NM_PPP_STATUS_SERIALCONN; + phase = "serial connection"; + break; + case PHASE_DORMANT: + status = NM_PPP_STATUS_DORMANT; + phase = "dormant"; + break; + case PHASE_ESTABLISH: + status = NM_PPP_STATUS_ESTABLISH; + phase = "establish"; + break; + case PHASE_AUTHENTICATE: + status = NM_PPP_STATUS_AUTHENTICATE; + phase = "authenticate"; + break; + case PHASE_CALLBACK: + status = NM_PPP_STATUS_CALLBACK; + phase = "callback"; + break; + case PHASE_NETWORK: + status = NM_PPP_STATUS_NETWORK; + phase = "network"; + break; + case PHASE_RUNNING: + status = NM_PPP_STATUS_RUNNING; + phase = "running"; + break; + case PHASE_TERMINATE: + status = NM_PPP_STATUS_TERMINATE; + phase = "terminate"; + break; + case PHASE_DISCONNECT: + status = NM_PPP_STATUS_DISCONNECT; + phase = "disconnect"; + break; + case PHASE_HOLDOFF: + status = NM_PPP_STATUS_HOLDOFF; + phase = "holdoff"; + break; + case PHASE_MASTER: + status = NM_PPP_STATUS_MASTER; + phase = "master"; + break; + + default: + phase = "unknown"; + break; + } + + g_message ("pppd reported new phase: %s", phase); + + if (status != NM_PPP_STATUS_UNKNOWN) { + DBusConnection *connection = (DBusConnection *) data; + DBusMessage *message; + + message = dbus_message_new_signal (NM_DBUS_PATH_PPP, + NM_DBUS_INTERFACE_PPP, + "Status"); + if (!message) { + g_warning ("Couldn't allocate the dbus message"); + return; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_UINT32, &status, + DBUS_TYPE_INVALID)) { + g_warning ("could not append message args"); + goto out; + } + + if (!dbus_connection_send (connection, message, NULL)) { + g_warning ("could not send dbus message"); + goto out; + } + + out: + dbus_message_unref (message); + } +} + +static const gchar * +ip4_address_as_string (guint32 ip) +{ + struct in_addr tmp_addr; + gchar *ip_string; + + tmp_addr.s_addr = ip; + ip_string = inet_ntoa (tmp_addr); + + return ip_string; +} + +static void +nm_ip_up (void *data, int arg) +{ + DBusConnection *connection = (DBusConnection *) data; + DBusMessage *signal; + DBusMessageIter iter; + DBusMessageIter iter_dict; + guint32 ip4_address; + guint32 ip4_gateway; + guint32 ip4_dns_1; + guint32 ip4_dns_2; + guint32 ip4_wins_1; + guint32 ip4_wins_2; + guint32 ip4_netmask = 0xFFFFFFFF; /* Default mask of 255.255.255.255 */ + + if (!ifname) { + g_warning ("Didn't receive a tunnel device name"); + return; + } + + ip4_address = ipcp_gotoptions[ifunit].ouraddr; + if (!ip4_address) { + g_warning ("Didn't receive an internal IP from pppd"); + return; + } + + ip4_gateway = ipcp_gotoptions[ifunit].hisaddr; + + ip4_dns_1 = ipcp_gotoptions[ifunit].dnsaddr[0]; + ip4_dns_2 = ipcp_gotoptions[ifunit].dnsaddr[1]; + ip4_wins_1 = ipcp_gotoptions[ifunit].winsaddr[0]; + ip4_wins_2 = ipcp_gotoptions[ifunit].winsaddr[1]; + + g_message ("Got ip configuration"); + g_message ("address: %s", ip4_address_as_string (ip4_address)); + g_message ("gateway: %s", ip4_address_as_string (ip4_gateway)); + g_message ("netmask: %s", ip4_address_as_string (ip4_netmask)); + g_message ("DNS1: %s", ip4_address_as_string (ip4_dns_1)); + g_message ("DNS2: %s", ip4_address_as_string (ip4_dns_2)); + g_message ("WINS1: %s", ip4_address_as_string (ip4_wins_1)); + g_message ("WINS2: %s", ip4_address_as_string (ip4_wins_2)); + + signal = dbus_message_new_signal (NM_DBUS_PATH_PPP, + NM_DBUS_INTERFACE_PPP, + "IP4Config"); + if (!signal) + goto out; + + dbus_message_iter_init_append (signal, &iter); + if (!nmu_dbus_dict_open_write (&iter, &iter_dict)) { + g_warning ("dict open write failed!"); + goto out; + } + + if (!nmu_dbus_dict_append_string (&iter_dict, "interface", ifname)) { + g_warning ("couldn't append interface to dict"); + goto out; + } + + if (!nmu_dbus_dict_append_uint32 (&iter_dict, "addres", ip4_address)) { + g_warning ("couldn't append address to dict"); + goto out; + } + + if (!nmu_dbus_dict_append_uint32 (&iter_dict, "netmask", ip4_netmask)) { + g_warning ("couldn't append netmask to dict"); + goto out; + } + + if (!nmu_dbus_dict_append_uint32 (&iter_dict, "gateway", ip4_gateway)) { + g_warning ("couldn't append gateway to dict"); + goto out; + } + + if (ip4_dns_1 || ip4_dns_2) { + guint32 ip4_dns[2]; + guint32 ip4_dns_len = 0; + + if (ip4_dns_1) + ip4_dns[ip4_dns_len++] = ip4_dns_1; + if (ip4_dns_2) + ip4_dns[ip4_dns_len++] = ip4_dns_2; + + if (!nmu_dbus_dict_append_uint32_array (&iter_dict, + "dns_server", + ip4_dns, + ip4_dns_len)) { + g_warning ("couldn't append dns_servers to dict"); + goto out; + } + } + + if (ip4_wins_1 || ip4_wins_2) { + guint32 ip4_wins[2]; + guint32 ip4_wins_len = 0; + + if (ip4_wins_1) + ip4_wins[ip4_wins_len++] = ip4_wins_1; + if (ip4_wins_2) + ip4_wins[ip4_wins_len++] = ip4_wins_2; + + if (!nmu_dbus_dict_append_uint32_array (&iter_dict, + "wins_server", + ip4_wins, + ip4_wins_len)) { + g_warning ("couldn't append wins_servers to dict"); + goto out; + } + } + + if (!nmu_dbus_dict_close_write (&iter, &iter_dict)) { + g_warning ("dict close write failed!"); + goto out; + } + + if (!dbus_connection_send (connection, signal, NULL)) { + g_warning ("could not send dbus message"); + goto out; + } + + out: + if (signal) + dbus_message_unref (signal); +} + +static void +nm_exit_notify (void *data, int arg) +{ + DBusConnection *connection = (DBusConnection *) data; + + g_message ("exiting"); + + if (connection) + dbus_connection_unref (connection); +} + +static DBusConnection * +nm_dbus_prepare_connection (void) +{ + DBusConnection *connection; + DBusError err; + + dbus_error_init (&err); + connection = dbus_bus_get (DBUS_BUS_SYSTEM, &err); + if (!connection || dbus_error_is_set (&err)) { + g_warning ("Could not get the system bus. Make sure the message bus daemon is running."); + goto out; + } + + dbus_connection_set_exit_on_disconnect (connection, FALSE); + + dbus_error_init (&err); + dbus_bus_request_name (connection, NM_DBUS_SERVICE_PPP, 0, &err); + if (dbus_error_is_set (&err)) { + g_warning ("Could not acquire the dbus service. dbus_bus_request_name() says: '%s'.", err.message); + goto out; + } + + out: + if (dbus_error_is_set (&err)) { + dbus_error_free (&err); + connection = NULL; + } + + return connection; +} + + +int +plugin_init (void) +{ + DBusConnection *connection; + + connection = nm_dbus_prepare_connection (); + if (connection) { + add_notifier (&phasechange, nm_phasechange, connection); + add_notifier (&ip_up_notifier, nm_ip_up, connection); + add_notifier (&exitnotify, nm_exit_notify, connection); + } + + return connection ? 0 : -1; +} diff --git a/src/ppp-manager/nm-pppd-plugin.h b/src/ppp-manager/nm-pppd-plugin.h new file mode 100644 index 000000000..5c34906f9 --- /dev/null +++ b/src/ppp-manager/nm-pppd-plugin.h @@ -0,0 +1,3 @@ +#define NM_DBUS_SERVICE_PPP "org.freedesktop.NetworkManager.PPP" +#define NM_DBUS_PATH_PPP "/org/freedesktop/NetworkManager/PPP" +#define NM_DBUS_INTERFACE_PPP "org.freedesktop.NetworkManager.PPP"