*
* Connection Manager
*
- * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
+ * 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
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#define CONNMAN_API_SUBJECT_TO_CHANGE
#include <connman/plugin.h>
#include <connman/driver.h>
+#include <connman/resolver.h>
+#include <connman/rtnl.h>
#include <connman/log.h>
+#include "inet.h"
+
enum connman_ipv4_method {
CONNMAN_IPV4_METHOD_UNKNOWN = 0,
CONNMAN_IPV4_METHOD_OFF = 1,
enum connman_ipv4_method method;
struct in_addr address;
struct in_addr netmask;
- struct in_addr gateway;
- struct in_addr network;
struct in_addr broadcast;
- struct in_addr nameserver;
};
+struct gateway_data {
+ int index;
+ char *gateway;
+};
+
+static GSList *gateway_list = NULL;
+
+static struct gateway_data *find_gateway(int index, const char *gateway)
+{
+ GSList *list;
+
+ if (gateway == NULL)
+ return NULL;
+
+ for (list = gateway_list; list; list = list->next) {
+ struct gateway_data *data = list->data;
+
+ if (data->gateway == NULL)
+ continue;
+
+ if (data->index == index &&
+ g_str_equal(data->gateway, gateway) == TRUE)
+ return data;
+ }
+
+ return NULL;
+}
+
static int set_ipv4(struct connman_element *element,
- struct connman_ipv4 *ipv4)
+ struct connman_ipv4 *ipv4, const char *nameserver)
{
struct ifreq ifr;
- struct rtentry rt;
struct sockaddr_in *addr;
int sk, err;
return -1;
memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = element->netdev.index;
+ ifr.ifr_ifindex = element->index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
close(sk);
if (err < 0)
DBG("broadcast setting failed (%s)", strerror(errno));
+ close(sk);
+
+ connman_resolver_append(ifr.ifr_name, NULL, nameserver);
+
+ return 0;
+}
+
+static int clear_ipv4(struct connman_element *element)
+{
+ struct ifreq ifr;
+ struct sockaddr_in *addr;
+ int sk, err;
+
+ DBG("element %p", element);
+
+ sk = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sk < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = element->index;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ DBG("ifname %s", ifr.ifr_name);
+
+ connman_resolver_remove_all(ifr.ifr_name);
+
+ addr = (struct sockaddr_in *) &ifr.ifr_addr;
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = INADDR_ANY;
+
+ //err = ioctl(sk, SIOCDIFADDR, &ifr);
+ err = ioctl(sk, SIOCSIFADDR, &ifr);
+
+ close(sk);
+
+ if (err < 0 && errno != EADDRNOTAVAIL) {
+ DBG("address removal failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_route(struct connman_element *element, const char *gateway)
+{
+ struct ifreq ifr;
+ struct rtentry rt;
+ struct sockaddr_in *addr;
+ int sk, err;
+
+ DBG("element %p", element);
+
+ sk = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sk < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = element->index;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ DBG("ifname %s", ifr.ifr_name);
+
memset(&rt, 0, sizeof(rt));
rt.rt_flags = RTF_UP | RTF_GATEWAY;
addr = (struct sockaddr_in *) &rt.rt_gateway;
addr->sin_family = AF_INET;
- addr->sin_addr = ipv4->gateway;
+ addr->sin_addr.s_addr = inet_addr(gateway);
addr = (struct sockaddr_in *) &rt.rt_genmask;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = INADDR_ANY;
err = ioctl(sk, SIOCADDRT, &rt);
+ if (err < 0)
+ DBG("default route setting failed (%s)", strerror(errno));
close(sk);
- if (err < 0) {
- DBG("default route failed (%s)", strerror(errno));
- return -1;
- }
-
- return 0;
+ return err;
}
-static int clear_ipv4(struct connman_element *element)
+static int del_route(struct connman_element *element, const char *gateway)
{
struct ifreq ifr;
+ struct rtentry rt;
struct sockaddr_in *addr;
int sk, err;
return -1;
memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = element->netdev.index;
+ ifr.ifr_ifindex = element->index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
close(sk);
DBG("ifname %s", ifr.ifr_name);
- addr = (struct sockaddr_in *) &ifr.ifr_addr;
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+
+ addr = (struct sockaddr_in *) &rt.rt_dst;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = INADDR_ANY;
- //err = ioctl(sk, SIOCDIFADDR, &ifr);
- err = ioctl(sk, SIOCSIFADDR, &ifr);
+ addr = (struct sockaddr_in *) &rt.rt_gateway;
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = inet_addr(gateway);
+
+ addr = (struct sockaddr_in *) &rt.rt_genmask;
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = INADDR_ANY;
+
+ err = ioctl(sk, SIOCDELRT, &rt);
+ if (err < 0)
+ DBG("default route removal failed (%s)", strerror(errno));
close(sk);
- if (err < 0 && errno != EADDRNOTAVAIL) {
- DBG("address removal failed (%s)", strerror(errno));
- return -1;
+ return err;
+}
+
+static int conn_probe(struct connman_element *element)
+{
+ const char *gateway = NULL;
+
+ DBG("element %p name %s", element, element->name);
+
+ if (element->parent == NULL)
+ return -ENODEV;
+
+ if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
+ return -ENODEV;
+
+ connman_element_get_value(element,
+ CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+
+ DBG("gateway %s", gateway);
+
+ if (gateway == NULL)
+ return 0;
+
+ if (g_slist_length(gateway_list) > 0) {
+ DBG("default already present");
+ return 0;
}
+ set_route(element, gateway);
+
+ connman_element_set_enabled(element, TRUE);
+
+ return 0;
+}
+
+static void conn_remove(struct connman_element *element)
+{
+ DBG("element %p name %s", element, element->name);
+}
+
+static int conn_enable(struct connman_element *element)
+{
+ const char *gateway = NULL;
+
+ DBG("element %p name %s", element, element->name);
+
+ connman_element_get_value(element,
+ CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+
+ DBG("gateway %s", gateway);
+
+ if (gateway == NULL)
+ return -EINVAL;
+
+ set_route(element, gateway);
+
+ return 0;
+}
+
+static int conn_disable(struct connman_element *element)
+{
+ const char *gateway = NULL;
+
+ DBG("element %p name %s", element, element->name);
+
+ connman_element_get_value(element,
+ CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+
+ DBG("gateway %s", gateway);
+
+ if (gateway == NULL)
+ return -EINVAL;
+
+ del_route(element, gateway);
+
return 0;
}
+static struct connman_driver conn_driver = {
+ .name = "ipv4-connection",
+ .type = CONNMAN_ELEMENT_TYPE_CONNECTION,
+ .probe = conn_probe,
+ .remove = conn_remove,
+ .enable = conn_enable,
+ .disable = conn_disable,
+};
+
static int ipv4_probe(struct connman_element *element)
{
- struct connman_element *resolver;
+ struct connman_element *connection;
struct connman_ipv4 ipv4;
- const char *address = NULL, *netmask = NULL, *gateway = NULL;
+ const char *address = NULL, *netmask = NULL, *broadcast = NULL;
+ const char *nameserver = NULL;
DBG("element %p name %s", element, element->name);
connman_element_get_value(element,
- CONNMAN_PROPERTY_TYPE_IPV4_ADDRESS, &address);
+ CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &address);
connman_element_get_value(element,
- CONNMAN_PROPERTY_TYPE_IPV4_NETMASK, &netmask);
+ CONNMAN_PROPERTY_ID_IPV4_NETMASK, &netmask);
connman_element_get_value(element,
- CONNMAN_PROPERTY_TYPE_IPV4_GATEWAY, &gateway);
+ CONNMAN_PROPERTY_ID_IPV4_BROADCAST, &broadcast);
+
+ connman_element_get_value(element,
+ CONNMAN_PROPERTY_ID_IPV4_NAMESERVER, &nameserver);
DBG("address %s", address);
DBG("netmask %s", netmask);
- DBG("gateway %s", gateway);
+ DBG("broadcast %s", broadcast);
+
+ if (address == NULL || netmask == NULL)
+ return -EINVAL;
memset(&ipv4, 0, sizeof(ipv4));
ipv4.address.s_addr = inet_addr(address);
ipv4.netmask.s_addr = inet_addr(netmask);
- ipv4.gateway.s_addr = inet_addr(gateway);
+ ipv4.broadcast.s_addr = inet_addr(broadcast);
- set_ipv4(element, &ipv4);
+ set_ipv4(element, &ipv4, nameserver);
- resolver = connman_element_create();
+ connection = connman_element_create(NULL);
- resolver->type = CONNMAN_ELEMENT_TYPE_RESOLVER;
- resolver->netdev.name = g_strdup(element->netdev.name);
+ connection->type = CONNMAN_ELEMENT_TYPE_CONNECTION;
+ connection->index = element->index;
+ connection->devname = inet_index2name(element->index);
- connman_element_set_data(element, resolver);
-
- connman_element_register(resolver, element);
+ if (connman_element_register(connection, element) < 0)
+ connman_element_unref(connection);
return 0;
}
static void ipv4_remove(struct connman_element *element)
{
- struct connman_element *resolver = connman_element_get_data(element);
-
DBG("element %p name %s", element, element->name);
- connman_element_set_data(element, NULL);
-
- connman_element_unregister(resolver);
-
- connman_element_unref(resolver);
-
clear_ipv4(element);
}
static struct connman_driver ipv4_driver = {
- .name = "ipv4",
+ .name = "ipv4-address",
.type = CONNMAN_ELEMENT_TYPE_IPV4,
.probe = ipv4_probe,
.remove = ipv4_remove,
};
+static void ipv4_newgateway(int index, const char *gateway)
+{
+ struct gateway_data *data;
+
+ DBG("index %d gateway %s", index, gateway);
+
+ data = find_gateway(index, gateway);
+ if (data != NULL)
+ return;
+
+ data = g_try_new0(struct gateway_data, 1);
+ if (data == NULL)
+ return;
+
+ data->index = index;
+ data->gateway = g_strdup(gateway);
+
+ gateway_list = g_slist_append(gateway_list, data);
+}
+
+static void ipv4_delgateway(int index, const char *gateway)
+{
+ struct gateway_data *data;
+
+ DBG("index %d gateway %s", index, gateway);
+
+ data = find_gateway(index, gateway);
+ if (data == NULL)
+ return;
+
+ gateway_list = g_slist_remove(gateway_list, data);
+
+ g_free(data->gateway);
+ g_free(data);
+}
+
+static struct connman_rtnl ipv4_rtnl = {
+ .name = "ipv4-rtnl",
+ .newgateway = ipv4_newgateway,
+ .delgateway = ipv4_delgateway,
+};
+
static int ipv4_init(void)
{
- return connman_driver_register(&ipv4_driver);
+ int err;
+
+ err = connman_rtnl_register(&ipv4_rtnl);
+ if (err < 0)
+ return err;
+
+ connman_rtnl_send_getroute();
+
+ err = connman_driver_register(&conn_driver);
+ if (err < 0) {
+ connman_rtnl_unregister(&ipv4_rtnl);
+ return err;
+ }
+
+ err = connman_driver_register(&ipv4_driver);
+ if (err < 0) {
+ connman_driver_unregister(&conn_driver);
+ connman_rtnl_unregister(&ipv4_rtnl);
+ }
+
+ return err;
}
static void ipv4_exit(void)
{
+ GSList *list;
+
+ connman_driver_unregister(&conn_driver);
connman_driver_unregister(&ipv4_driver);
+
+ connman_rtnl_unregister(&ipv4_rtnl);
+
+ for (list = gateway_list; list; list = list->next) {
+ struct gateway_data *data = list->data;
+
+ DBG("index %d gateway %s", data->index, data->gateway);
+
+ g_free(data->gateway);
+ g_free(data);
+ list->data = NULL;
+ }
+
+ g_slist_free(gateway_list);
+ gateway_list = NULL;
}
-CONNMAN_PLUGIN_DEFINE("ipv4", "IPv4 configuration plugin", VERSION,
+CONNMAN_PLUGIN_DEFINE(ipv4, "IPv4 configuration plugin", VERSION,
ipv4_init, ipv4_exit)