| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2007-2009 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 <config.h> |
| #endif |
| |
| #include <errno.h> |
| #include <glib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <net/if_arp.h> |
| #include <sys/socket.h> |
| #define CONNMAN_API_SUBJECT_TO_CHANGE |
| #include <connman/dbus.h> |
| #include <connman/device.h> |
| #include <connman/inet.h> |
| #include <connman/ipconfig.h> |
| #include <connman/log.h> |
| #include <connman/plugin.h> |
| #include <connman/task.h> |
| |
| #define _DBG_PPPD(fmt, arg...) DBG(DBG_PPPD, fmt, ## arg) |
| |
| #define PPPD_INTF "org.samba.pppd" |
| #define PPPD_PATH "/org/samba/pppd" |
| |
| struct pppd_priv { |
| DBusConnection *connection; |
| struct connman_task *task; |
| struct connman_ipconfig *ipconfig; |
| }; |
| |
| static const char *find_pppd(void) |
| { |
| static const char *pppd_locations[] = { |
| "/usr/sbin/pppd", |
| "/sbin/pppd" |
| }; |
| int i; |
| |
| for (i = 0; i < sizeof(pppd_locations)/sizeof(const char *); i++) { |
| if (g_file_test(pppd_locations[i], G_FILE_TEST_IS_EXECUTABLE)) |
| return pppd_locations[i]; |
| } |
| return NULL; |
| } |
| |
| static const char *pppdargs[] = { |
| "921600", /* TODO(ers): find the baud rate */ |
| "nodetach", |
| "crtscts", /* TODO(ers): device dependent? */ |
| "lock", |
| "user", "user", /* TODO(ers) find user name */ |
| "password", "user", /* TODO(ers) find password */ |
| "fetchpeerdns", |
| "noauth", |
| "debug" /* TODO(ers) temporary */ |
| }; |
| |
| static DBusHandlerResult pppd_filter(DBusConnection *conn, |
| DBusMessage *msg, void *user_data); |
| static void pppd_died(struct connman_task *task, void *user_data); |
| |
| static const char *pppd_rule = "path=" PPPD_PATH ",interface=" PPPD_INTF; |
| |
| static int pppd_start(struct connman_ipconfig *ipconfig) |
| { |
| struct connman_device *device; |
| const char *tty; |
| int i; |
| struct connman_task *task; |
| struct pppd_priv *pppd_data; |
| const char *busname; |
| const char *pppd_binary; |
| |
| _DBG_PPPD(""); |
| pppd_binary = find_pppd(); |
| if (pppd_binary == NULL) |
| return -ENOENT; |
| |
| task = connman_task_create(pppd_binary); |
| if (task == NULL) |
| return -ENOMEM; |
| |
| device = connman_ipconfig_get_device(ipconfig); |
| if (device == NULL) { |
| connman_task_destroy(task); |
| _DBG_PPPD("Cannot get device"); |
| return -ENODEV; |
| } |
| |
| tty = connman_device_get_string(device, "Tty"); |
| if (tty == NULL) { |
| connman_task_destroy(task); |
| _DBG_PPPD("Device missing Tty Property"); |
| return -ENODEV; |
| } |
| |
| pppd_data = g_try_new0(struct pppd_priv, 1); |
| if (pppd_data == NULL) { |
| connman_task_destroy(task); |
| return -ENOMEM; |
| } |
| |
| pppd_data->connection = connman_dbus_get_connection(); |
| if (pppd_data->connection == NULL) { |
| connman_task_destroy(task); |
| g_free(pppd_data); |
| return -EIO; |
| } |
| dbus_connection_add_filter(pppd_data->connection, pppd_filter, pppd_data, NULL); |
| dbus_bus_add_match(pppd_data->connection, pppd_rule, NULL); |
| |
| busname = dbus_bus_get_unique_name(pppd_data->connection); |
| |
| connman_task_add_argument(task, tty, NULL); |
| for (i = 0; i < sizeof(pppdargs)/sizeof(const char *); i++) |
| connman_task_add_argument(task, pppdargs[i], NULL); |
| connman_task_add_argument(task, "ipparam", "%s", busname); |
| |
| connman_task_run(task, pppd_died, pppd_data, NULL, NULL, NULL); |
| pppd_data->task = task; |
| pppd_data->ipconfig = ipconfig; |
| connman_ipconfig_set_data(ipconfig, pppd_data); |
| return 0; |
| } |
| |
| static int pppd_stop(struct connman_ipconfig *ipconfig) |
| { |
| struct pppd_priv *pppd_data = connman_ipconfig_get_data(ipconfig); |
| |
| _DBG_PPPD(""); |
| /* |
| * If pppd is not running, the following is a no-op. |
| * If it is running, then the rest of the cleanup steps |
| * will be handled in pppd_died. |
| */ |
| if (pppd_data != NULL) |
| connman_task_stop(pppd_data->task); |
| return 0; |
| } |
| |
| static void pppd_died(struct connman_task *task, void *user_data) |
| { |
| struct pppd_priv *pppd_data = user_data; |
| |
| _DBG_PPPD(""); |
| dbus_bus_remove_match(pppd_data->connection, pppd_rule, NULL); |
| dbus_connection_remove_filter(pppd_data->connection, pppd_filter, pppd_data); |
| dbus_connection_unref(pppd_data->connection); |
| connman_ipconfig_set_data(pppd_data->ipconfig, NULL); |
| g_free(pppd_data); |
| connman_task_destroy(task); |
| } |
| |
| static struct connman_ipconfig_driver ppp_driver = { |
| .name = "ppp", |
| .type = CONNMAN_IPCONFIG_TYPE_PPP, |
| .priority = CONNMAN_IPCONFIG_PRIORITY_DEFAULT, |
| .request = pppd_start, |
| .release = pppd_stop |
| /* NB: nothing to do for renew */ |
| }; |
| |
| static DBusHandlerResult pppd_filter(DBusConnection *conn, |
| DBusMessage *msg, void *user_data) |
| { |
| DBusMessageIter iter, dict; |
| dbus_uint32_t pid; |
| const char *key; |
| char *dns1 = NULL; |
| char *dns2 = NULL; |
| const char *interface = NULL; |
| char *value; |
| struct connman_ipaddress ipaddr; |
| struct connman_device *device; |
| struct pppd_priv *pppd_data = user_data; |
| int index; |
| |
| if (dbus_message_is_method_call(msg, PPPD_INTF, "Notify") == FALSE) |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| dbus_message_iter_init(msg, &iter); |
| |
| dbus_message_iter_get_basic(&iter, &pid); |
| dbus_message_iter_next(&iter); |
| |
| memset(&ipaddr, 0, sizeof(ipaddr)); |
| ipaddr.af = AF_INET; |
| ipaddr.mask |= CONNMAN_IPCONFIG_AF; |
| |
| 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); |
| |
| _DBG_PPPD("%s = %s", key, value); |
| |
| |
| if (g_ascii_strcasecmp(key, "iplocal") == 0) { |
| ipaddr.local = g_strdup(value); |
| ipaddr.mask |= CONNMAN_IPCONFIG_LOCAL; |
| } else if (g_ascii_strcasecmp(key, "ipremote") == 0) { |
| ipaddr.peer = g_strdup(value); |
| ipaddr.mask |= CONNMAN_IPCONFIG_PEER; |
| } else if (g_ascii_strcasecmp(key, "dns1") == 0) |
| dns1 = g_strdup(value); |
| else if (g_ascii_strcasecmp(key, "dns2") == 0) |
| dns2 = g_strdup(value); |
| else if (g_ascii_strcasecmp(key, "interface") == 0) |
| interface = g_strdup(value); |
| else if (g_ascii_strcasecmp(key, "device") == 0) |
| ; |
| else if (g_ascii_strcasecmp(key, "tty") == 0) |
| ; |
| else if (g_ascii_strcasecmp(key, "speed") == 0) |
| ; |
| |
| dbus_message_iter_next(&dict); |
| } |
| if (interface == NULL) { |
| connman_error("pppd did not send interface name"); |
| g_free(ipaddr.local); |
| g_free(ipaddr.peer); |
| g_free(dns1); |
| g_free(dns2); |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| } |
| index = connman_inet_ifindex(interface); |
| connman_ipconfig_set_index(pppd_data->ipconfig, index); |
| device = connman_ipconfig_get_device(pppd_data->ipconfig); |
| connman_device_set_index(device, index); |
| connman_device_set_interface(device, interface, NULL); |
| ipaddr.gateway = g_strdup(ipaddr.peer); |
| ipaddr.mask |= CONNMAN_IPCONFIG_GW; |
| ipaddr.broadcast = g_strdup("0.0.0.0"); |
| ipaddr.mask |= CONNMAN_IPCONFIG_BCAST; |
| ipaddr.domain_name = g_strdup(""); /* TODO(ers) fixme */ |
| ipaddr.mask |= CONNMAN_IPCONFIG_DOMAIN; |
| ipaddr.prefixlen = 32; |
| ipaddr.mask |= CONNMAN_IPCONFIG_PREFIX; |
| ipaddr.dns_servers = g_try_new0(gchar *, 3); |
| if (dns1 != NULL) { |
| ipaddr.dns_servers[0] = dns1; |
| if (dns2 != NULL) |
| ipaddr.dns_servers[1] = dns2; |
| ipaddr.mask |= CONNMAN_IPCONFIG_DNS; |
| } |
| |
| connman_ipconfig_bind(pppd_data->ipconfig, &ipaddr); |
| g_strfreev(ipaddr.dns_servers); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| static int pppd_init(void) |
| { |
| _DBG_PPPD(""); |
| return connman_ipconfig_driver_register(&ppp_driver); |
| } |
| |
| static void pppd_exit(void) |
| { |
| _DBG_PPPD(""); |
| connman_ipconfig_driver_unregister(&ppp_driver); |
| } |
| |
| CONNMAN_PLUGIN_DEFINE(pppd, "Point-to-point protocol plugin", VERSION, |
| CONNMAN_PLUGIN_PRIORITY_DEFAULT, pppd_init, pppd_exit) |