blob: 856e1dbf67398de8eb05320c4a632edbcf90b9f4 [file] [log] [blame]
/*
*
* 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)