From e1ec0cf683d35746dd4e94abfab6ad5aa2de10fe Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 24 Dec 2007 03:04:10 +0100 Subject: [PATCH] Add experimental DHCP plugin --- include/dhcp.h | 12 +++ plugins/Makefile.am | 17 +++- plugins/dhclient.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dhcp.c | 25 +++++ 4 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 plugins/dhclient.c diff --git a/include/dhcp.h b/include/dhcp.h index 337746b..f817e0a 100644 --- a/include/dhcp.h +++ b/include/dhcp.h @@ -28,6 +28,14 @@ extern "C" { #include +enum connman_dhcp_state { + CONNMAN_DHCP_STATE_UNKNOWN = 0, + CONNMAN_DHCP_STATE_INIT = 1, + CONNMAN_DHCP_STATE_BOUND = 2, + CONNMAN_DHCP_STATE_RENEW = 3, + CONNMAN_DHCP_STATE_FAILED = 4, +}; + struct connman_dhcp_driver { const char *name; int (*request) (struct connman_iface *iface); @@ -37,6 +45,10 @@ struct connman_dhcp_driver { extern int connman_dhcp_register(struct connman_dhcp_driver *driver); extern void connman_dhcp_unregister(struct connman_dhcp_driver *driver); +extern int connman_dhcp_update(struct connman_iface *iface, + enum connman_dhcp_state state, + struct connman_ipv4 *ipv4); + #ifdef __cplusplus } #endif diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 2d7d5df..a0a387d 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -1,14 +1,29 @@ plugindir = $(libdir)/connman/plugins -plugin_LTLIBRARIES = libconnman-80203.la libconnman-80211.la +plugin_LTLIBRARIES = libconnman-80203.la libconnman-80211.la \ + libconnman-dhclient.la libconnman_80203_la_SOURCES = 80203.c net.h net.c libconnman_80211_la_SOURCES = 80211.c net.h net.c +libconnman_dhclient_la_SOURCES = dhclient.c net.h net.c +libconnman_dhclient_la_LIBADD = @GDBUS_LIBS@ + AM_LDFLAGS = -module -avoid-version -export-symbols-regex connman_plugin_desc +statedir = $(localstatedir)/run/connman + +if MAINTAINER_MODE +scriptdir = $(abs_top_srcdir)/scripts +else +scriptdir = $(libdir)/connman/scripts +endif + +AM_CFLAGS = @GDBUS_CFLAGS@ \ + -DSTATEDIR=\""$(statedir)"\" -DSCRIPTDIR=\""$(scriptdir)"\" + INCLUDES = -I$(top_builddir)/include MAINTAINERCLEANFILES = Makefile.in diff --git a/plugins/dhclient.c b/plugins/dhclient.c new file mode 100644 index 0000000..dbf6e6d --- /dev/null +++ b/plugins/dhclient.c @@ -0,0 +1,273 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "net.h" + +static const char *busname; + +struct dhclient_task { + GPid pid; + char *ifname; + struct connman_iface *iface; +}; + +static GSList *tasks = NULL; + +static struct dhclient_task *find_task(GPid pid) +{ + GSList *list; + + for (list = tasks; list; list = list->next) { + struct dhclient_task *task = list->data; + + if (task->pid == pid) + return task; + } + + return NULL; +} + +static int dhclient_request(struct connman_iface *iface) +{ + struct dhclient_task *task; + char *ifname, *argv[16], address[128], pidfile[PATH_MAX]; + char leases[PATH_MAX], config[PATH_MAX], script[PATH_MAX]; + + ifname = __net_ifname(iface->sysfs); + if (ifname == NULL) + return -1; + + task = g_try_new0(struct dhclient_task, 1); + if (task == NULL) + return -1; + + task->ifname = ifname; + task->iface = iface; + + printf("[DHCP] request for %s\n", ifname); + + snprintf(address, sizeof(address) - 1, "BUSNAME=%s", busname); + snprintf(pidfile, sizeof(pidfile) - 1, + "%s/dhclient.%s.pid", STATEDIR, ifname); + snprintf(leases, sizeof(leases) - 1, + "%s/dhclient.%s.leases", STATEDIR, ifname); + snprintf(config, sizeof(config) - 1, "%s/dhclient.conf", SCRIPTDIR); + snprintf(script, sizeof(script) - 1, "%s/dhclient-script", SCRIPTDIR); + + argv[0] = "/sbin/dhclient"; + argv[1] = "-d"; + argv[2] = "-q"; + argv[3] = "-n"; + argv[4] = "-e"; + argv[5] = address; + argv[6] = "-pf"; + argv[7] = pidfile; + argv[8] = "-lf"; + argv[9] = leases; + argv[10] = "-cf"; + argv[11] = config; + argv[12] = "-sf"; + argv[13] = script; + argv[14] = ifname; + argv[15] = NULL; + + if (g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &task->pid, NULL) == FALSE) { + printf("Failed to spawn dhclient\n"); + return -1; + } + + tasks = g_slist_append(tasks, task); + + printf("[DHCP] executed with pid %d\n", task->pid); + + return 0; +} + +static int dhclient_release(struct connman_iface *iface) +{ + char *ifname; + + ifname = __net_ifname(iface->sysfs); + if (ifname == NULL) + return -1; + + printf("[DHCP] release for %s\n", ifname); + + __net_free(ifname); + + return 0; +} + +static struct connman_dhcp_driver dhclient_driver = { + .name = "dhclient", + .request = dhclient_request, + .release = dhclient_release, +}; + +static DBusMessage *notify_method(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessageIter iter, dict; + dbus_uint32_t pid; + struct dhclient_task *task; + struct connman_ipv4 ipv4; + const char *text, *key, *value; + + memset(&ipv4, 0, sizeof(ipv4)); + + dbus_message_iter_init(msg, &iter); + + dbus_message_iter_get_basic(&iter, &pid); + dbus_message_iter_next(&iter); + + dbus_message_iter_get_basic(&iter, &text); + dbus_message_iter_next(&iter); + + printf("[DHCP] change %d to %s\n", pid, text); + + task = find_task(pid); + if (task == NULL) + return NULL; + + dbus_message_iter_recurse(&iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry; + + dbus_message_iter_recurse(&dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + dbus_message_iter_next(&entry); + dbus_message_iter_get_basic(&entry, &value); + + printf("[DHCP] %s = %s\n", key, value); + + if (strcmp(key, "new_ip_address") == 0) + inet_aton(value, &ipv4.address); + + if (strcmp(key, "new_subnet_mask") == 0) + inet_aton(value, &ipv4.netmask); + + if (strcmp(key, "new_routers") == 0) + inet_aton(value, &ipv4.gateway); + + if (strcmp(key, "new_network_number") == 0) + inet_aton(value, &ipv4.network); + + if (strcmp(key, "new_broadcast_address") == 0) + inet_aton(value, &ipv4.broadcast); + + if (strcmp(key, "new_domain_name_servers") == 0) + inet_aton(value, &ipv4.nameserver); + + dbus_message_iter_next(&dict); + } + + if (strcmp(text, "PREINIT") == 0) + connman_dhcp_update(task->iface, + CONNMAN_DHCP_STATE_INIT, &ipv4); + else if (strcmp(text, "BOUND") == 0 || strcmp(text, "REBOOT") == 0) + connman_dhcp_update(task->iface, + CONNMAN_DHCP_STATE_BOUND, &ipv4); + else if (strcmp(text, "RENEW") == 0 || strcmp(text, "REBIND") == 0) + connman_dhcp_update(task->iface, + CONNMAN_DHCP_STATE_RENEW, &ipv4); + else + connman_dhcp_update(task->iface, + CONNMAN_DHCP_STATE_FAILED, NULL); + + return NULL; +} + +static GDBusMethodTable dhclient_methods[] = { + { "notify", "usa{ss}", "", notify_method, G_DBUS_METHOD_FLAG_NOREPLY }, + { }, +}; + +static DBusConnection *connection; + +static int plugin_init(void) +{ + connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL); + + busname = dbus_bus_get_unique_name(connection); + + g_dbus_register_object(connection, "/org/isc/dhclient", NULL, NULL); + + g_dbus_register_interface(connection, "/org/isc/dhclient", + "org.isc.dhclient", + dhclient_methods, NULL, NULL); + + connman_dhcp_register(&dhclient_driver); + + return 0; +} + +static void plugin_exit(void) +{ + GSList *list; + + for (list = tasks; list; list = list->next) { + struct dhclient_task *task = list->data; + char pathname[PATH_MAX]; + + printf("[DHCP] killing process %d\n", task->pid); + + kill(task->pid, SIGTERM); + + snprintf(pathname, sizeof(pathname) - 1, + "%s/dhclient.%s.pid", STATEDIR, task->ifname); + unlink(pathname); + + snprintf(pathname, sizeof(pathname) - 1, + "%s/dhclient.%s.leases", STATEDIR, task->ifname); + unlink(pathname); + + __net_free(task->ifname); + + g_free(task); + } + + g_slist_free(tasks); + + connman_dhcp_unregister(&dhclient_driver); + + g_dbus_cleanup_connection(connection); +} + +CONNMAN_PLUGIN_DEFINE("dhclient", "ISC DHCP client plugin", VERSION, + plugin_init, plugin_exit) diff --git a/src/dhcp.c b/src/dhcp.c index 5a6e5c9..8af6dab 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -23,6 +23,8 @@ #include #endif +#include + #include #include "connman.h" @@ -45,6 +47,29 @@ void connman_dhcp_unregister(struct connman_dhcp_driver *driver) drivers = g_slist_remove(drivers, driver); } +int connman_dhcp_update(struct connman_iface *iface, + enum connman_dhcp_state state, + struct connman_ipv4 *ipv4) +{ + DBG("iface %p state %d", iface, state); + + if (state == CONNMAN_DHCP_STATE_BOUND) { + DBG("address %s", inet_ntoa(ipv4->address)); + DBG("netmask %s", inet_ntoa(ipv4->netmask)); + DBG("gateway %s", inet_ntoa(ipv4->gateway)); + DBG("network %s", inet_ntoa(ipv4->network)); + DBG("broadcast %s", inet_ntoa(ipv4->broadcast)); + DBG("nameserver %s", inet_ntoa(ipv4->nameserver)); + + if (iface->driver->set_ipv4) { + iface->driver->set_ipv4(iface, ipv4); + iface->ipv4 = *ipv4; + } + } + + return 0; +} + int __connman_dhcp_request(struct connman_iface *iface) { struct connman_dhcp_driver *driver = g_slist_nth_data(drivers, 0); -- 2.7.4