summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCristiano Fernandes <cristiano.fernandes@hp.com>2010-10-12 00:34:39 +0200
committerSamuel Ortiz <sameo@linux.intel.com>2010-10-12 00:34:39 +0200
commitb0c90253ddbcdfbab4c0ea30a69f59bf71dcaa88 (patch)
tree17bbc83e4b002528705fabb306b6cb84ff03b55f
parentea5b1fb70fa718bd68b63de75f12881fab9f9c21 (diff)
downloadconnman-b0c90253ddbcdfbab4c0ea30a69f59bf71dcaa88.tar.gz
connman-b0c90253ddbcdfbab4c0ea30a69f59bf71dcaa88.tar.bz2
connman-b0c90253ddbcdfbab4c0ea30a69f59bf71dcaa88.zip
Using netlink to set and clear ipv4 addresses
Using netlink to set and clear ipv4 configuration avoids connman from sending multiple IPv4 signals through DBus with the wrong configuration, since the configuration are set all at once triggering only one netlink event.
-rw-r--r--include/inet.h4
-rw-r--r--src/connman.h6
-rw-r--r--src/inet.c204
-rw-r--r--src/ipconfig.c10
-rw-r--r--src/ipv4.c143
5 files changed, 163 insertions, 204 deletions
diff --git a/include/inet.h b/include/inet.h
index ce0a0b25..d7af8261 100644
--- a/include/inet.h
+++ b/include/inet.h
@@ -23,6 +23,8 @@
#define __CONNMAN_INET_H
#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
#include <connman/device.h>
#include <connman/ipconfig.h>
@@ -43,7 +45,7 @@ struct connman_device *connman_inet_create_device(int index);
connman_bool_t connman_inet_is_cfg80211(int index);
int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress);
-int connman_inet_clear_address(int index);
+int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress);
int connman_inet_add_host_route(int index, const char *host, const char *gateway);
int connman_inet_del_host_route(int index, const char *host);
int connman_inet_set_gateway_address(int index, const char *gateway);
diff --git a/src/connman.h b/src/connman.h
index 3a546fe9..9cc05951 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -105,6 +105,11 @@ void __connman_task_cleanup(void);
#include <connman/inet.h>
+int __connman_inet_modify_address(int cmd, int flags, int index, int family,
+ const char *address,
+ unsigned char prefixlen,
+ const char *broadcast);
+
#include <connman/wifi.h>
#include <connman/rfkill.h>
@@ -248,6 +253,7 @@ int __connman_ipconfig_set_gateway(struct connman_ipconfig *ipconfig,
struct connman_element *parent);
int __connman_ipconfig_set_address(struct connman_ipconfig *ipconfig);
int __connman_ipconfig_clear_address(struct connman_ipconfig *ipconfig);
+unsigned char __connman_ipconfig_netmask_prefix_len(const char *netmask);
int __connman_ipconfig_set_proxy_autoconfig(struct connman_ipconfig *ipconfig,
const char *url);
diff --git a/src/inet.c b/src/inet.c
index 1b0cc45d..7501ad58 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -39,6 +39,106 @@
#include "connman.h"
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((uint8_t*) (nmsg)) + \
+ NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type,
+ const void *data, size_t data_length)
+{
+ size_t length;
+ struct rtattr *rta;
+
+ length = RTA_LENGTH(data_length);
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length)
+ return -E2BIG;
+
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = length;
+ memcpy(RTA_DATA(rta), data, data_length);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length);
+
+ return 0;
+}
+
+int __connman_inet_modify_address(int cmd, int flags,
+ int index, int family,
+ const char *address,
+ unsigned char prefixlen,
+ const char *broadcast)
+{
+ uint8_t request[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+ NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
+ RTA_LENGTH(sizeof(struct in6_addr))];
+
+ struct nlmsghdr *header;
+ struct sockaddr_nl nl_addr;
+ struct ifaddrmsg *ifaddrmsg;
+ struct in_addr ipv4_addr, ipv4_bcast;
+ int sk, err;
+
+ DBG("");
+
+ if (address == NULL)
+ return -1;
+
+ memset(&request, 0, sizeof(request));
+
+ header = (struct nlmsghdr *)request;
+ header->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ header->nlmsg_type = cmd;
+ header->nlmsg_flags = NLM_F_REQUEST | flags;
+ header->nlmsg_seq = 1;
+
+ ifaddrmsg = NLMSG_DATA(header);
+ ifaddrmsg->ifa_family = family;
+ ifaddrmsg->ifa_prefixlen = prefixlen;
+ ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
+ ifaddrmsg->ifa_scope = RT_SCOPE_UNIVERSE;
+ ifaddrmsg->ifa_index = index;
+
+ if (family == AF_INET) {
+ if (inet_pton(AF_INET, address, &ipv4_addr) < 1)
+ return -1;
+
+ if (broadcast != NULL)
+ inet_pton(AF_INET, broadcast, &ipv4_bcast);
+ else
+ ipv4_bcast.s_addr = ipv4_addr.s_addr |
+ htonl(0xfffffffflu >> prefixlen);
+
+ if ((err = add_rtattr(header, sizeof(request), IFA_LOCAL,
+ &ipv4_addr, sizeof(ipv4_addr))) < 0)
+ return err;
+
+ if ((err = add_rtattr(header, sizeof(request), IFA_BROADCAST,
+ &ipv4_bcast, sizeof(ipv4_bcast))) < 0)
+ return err;
+ } else {
+ return -1;
+ }
+
+ sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (sk < 0)
+ return -1;
+
+ memset(&nl_addr, 0, sizeof(nl_addr));
+ nl_addr.nl_family = AF_NETLINK;
+
+ if ((err = sendto(sk, request, header->nlmsg_len, 0,
+ (struct sockaddr *) &nl_addr, sizeof(nl_addr))) < 0)
+ goto done;
+
+ err = 0;
+
+done:
+ close(sk);
+
+ return err;
+}
+
int connman_inet_ifindex(const char *name)
{
struct ifreq ifr;
@@ -453,67 +553,25 @@ out:
int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
{
- struct ifreq ifr;
- struct sockaddr_in addr;
- int sk, err;
+ unsigned char prefix_len;
+ const char *address, *broadcast;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
- if (sk < 0)
+ if (ipaddress->local == NULL)
return -1;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
-
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- close(sk);
- return -1;
- }
+ prefix_len = ipaddress->prefixlen;
+ address = ipaddress->local;
+ broadcast = ipaddress->broadcast;
- DBG("ifname %s", ifr.ifr_name);
+ DBG("index %d address %s prefix_len %d", index, address, prefix_len);
- if (ipaddress->local == NULL) {
- close(sk);
+ if ((__connman_inet_modify_address(RTM_NEWADDR,
+ NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
+ address, prefix_len, broadcast)) < 0) {
+ DBG("address setting failed");
return -1;
}
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(ipaddress->local);
- memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr));
-
- err = ioctl(sk, SIOCSIFADDR, &ifr);
-
- if (err < 0)
- DBG("address setting failed (%s)", strerror(errno));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(~(0xfffffffflu >> ipaddress->prefixlen));
- memcpy(&ifr.ifr_netmask, &addr, sizeof(ifr.ifr_netmask));
-
- err = ioctl(sk, SIOCSIFNETMASK, &ifr);
-
- if (err < 0)
- DBG("netmask setting failed (%s)", strerror(errno));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
-
- if (ipaddress->broadcast != NULL)
- addr.sin_addr.s_addr = inet_addr(ipaddress->broadcast);
- else
- addr.sin_addr.s_addr = inet_addr(ipaddress->local) |
- htonl(0xfffffffflu >> ipaddress->prefixlen);
-
- memcpy(&ifr.ifr_broadaddr, &addr, sizeof(ifr.ifr_broadaddr));
-
- err = ioctl(sk, SIOCSIFBRDADDR, &ifr);
-
- if (err < 0)
- DBG("broadcast setting failed (%s)", strerror(errno));
-
- close(sk);
-
return 0;
}
@@ -549,40 +607,20 @@ out:
return err;
}
-int connman_inet_clear_address(int index)
+int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
{
+ unsigned char prefix_len;
+ const char *address, *broadcast;
+ prefix_len = ipaddress->prefixlen;
+ address = ipaddress->local;
+ broadcast = ipaddress->broadcast;
- struct ifreq ifr;
- struct sockaddr_in addr;
- int sk, err;
-
- sk = socket(PF_INET, SOCK_DGRAM, 0);
- if (sk < 0)
- return -1;
-
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
-
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- close(sk);
- return -1;
- }
-
- DBG("ifname %s", ifr.ifr_name);
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr));
-
- //err = ioctl(sk, SIOCDIFADDR, &ifr);
- err = ioctl(sk, SIOCSIFADDR, &ifr);
-
- close(sk);
+ DBG("index %d address %s prefix_len %d", index, address, prefix_len);
- if (err < 0 && errno != EADDRNOTAVAIL) {
- DBG("address removal failed (%s)", strerror(errno));
+ if ((__connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
+ address, prefix_len, broadcast)) < 0) {
+ DBG("address removal failed");
return -1;
}
diff --git a/src/ipconfig.c b/src/ipconfig.c
index 2b60e2df..0b4a84d9 100644
--- a/src/ipconfig.c
+++ b/src/ipconfig.c
@@ -116,7 +116,7 @@ void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
g_free(ipaddress);
}
-static unsigned char netmask2prefixlen(const char *netmask)
+unsigned char __connman_ipconfig_netmask_prefix_len(const char *netmask)
{
unsigned char bits = 0;
in_addr_t mask = inet_network(netmask);
@@ -178,7 +178,8 @@ void connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
return;
if (netmask != NULL)
- ipaddress->prefixlen = netmask2prefixlen(netmask);
+ ipaddress->prefixlen =
+ __connman_ipconfig_netmask_prefix_len(netmask);
else
ipaddress->prefixlen = 32;
@@ -412,7 +413,7 @@ static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice)
connman_ipconfig_unref(ipdevice->driver_config);
ipdevice->driver_config = NULL;
- connman_inet_clear_address(ipdevice->index);
+ connman_inet_clear_address(ipdevice->index, ipdevice->config->address);
connman_inet_clear_ipv6_address(ipdevice->index,
ipdevice->driver_config->address->local,
ipdevice->driver_config->address->prefixlen);
@@ -1211,7 +1212,8 @@ int __connman_ipconfig_clear_address(struct connman_ipconfig *ipconfig)
break;
case CONNMAN_IPCONFIG_METHOD_MANUAL:
if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
- return connman_inet_clear_address(ipconfig->index);
+ return connman_inet_clear_address(ipconfig->index,
+ ipconfig->address);
else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
return connman_inet_clear_ipv6_address(
ipconfig->index,
diff --git a/src/ipv4.c b/src/ipv4.c
index 7f18675b..7ca834de 100644
--- a/src/ipv4.c
+++ b/src/ipv4.c
@@ -30,113 +30,12 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
#include "connman.h"
-struct connman_ipv4 {
- enum connman_ipconfig_method method;
- struct in_addr address;
- struct in_addr netmask;
- struct in_addr broadcast;
-};
-
-static int set_ipv4(struct connman_element *element, struct connman_ipv4 *ipv4)
-{
- struct ifreq ifr;
- struct sockaddr_in addr;
- int sk, err;
-
- DBG("element %p ipv4 %p", element, ipv4);
-
- 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(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr = ipv4->address;
- memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr));
-
- err = ioctl(sk, SIOCSIFADDR, &ifr);
-
- if (err < 0)
- DBG("address setting failed (%s)", strerror(errno));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr = ipv4->netmask;
- memcpy(&ifr.ifr_netmask, &addr, sizeof(ifr.ifr_netmask));
-
- err = ioctl(sk, SIOCSIFNETMASK, &ifr);
-
- if (err < 0)
- DBG("netmask setting failed (%s)", strerror(errno));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr = ipv4->broadcast;
- memcpy(&ifr.ifr_broadaddr, &addr, sizeof(ifr.ifr_broadaddr));
-
- err = ioctl(sk, SIOCSIFBRDADDR, &ifr);
-
- if (err < 0)
- DBG("broadcast setting failed (%s)", strerror(errno));
-
- close(sk);
-
- 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);
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr));
-
- //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 char *index2name(int index)
{
struct ifreq ifr;
@@ -167,13 +66,12 @@ static int ipv4_probe(struct connman_element *element)
struct connman_service *service;
struct connman_ipconfig *ipconfig;
struct connman_element *connection;
- struct connman_ipv4 ipv4;
const char *address = NULL, *netmask = NULL, *broadcast = NULL;
const char *nameserver = NULL, *pac = NULL;
char *timeserver = NULL;
+ unsigned char prefixlen;
DBG("element %p name %s", element, element->name);
-
connman_element_get_value(element,
CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &address);
connman_element_get_value(element,
@@ -195,16 +93,12 @@ static int ipv4_probe(struct connman_element *element)
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);
- if (broadcast)
- ipv4.broadcast.s_addr = inet_addr(broadcast);
- else
- ipv4.broadcast.s_addr = ipv4.address.s_addr |
- ~ipv4.netmask.s_addr;
+ prefixlen = __connman_ipconfig_netmask_prefix_len(netmask);
- set_ipv4(element, &ipv4);
+ if ((__connman_inet_modify_address(RTM_NEWADDR,
+ NLM_F_REPLACE | NLM_F_ACK, element->index,
+ AF_INET, address, prefixlen, broadcast)) < 0)
+ DBG("address setting failed");
service = __connman_element_get_service(element);
@@ -235,18 +129,31 @@ static int ipv4_probe(struct connman_element *element)
static void ipv4_remove(struct connman_element *element)
{
+ const char *address = NULL, *netmask = NULL, *broadcast = NULL;
const char *nameserver = NULL;
char *timeserver = NULL;
+ unsigned char prefixlen;
DBG("element %p name %s", element, element->name);
connman_element_get_value(element,
+ CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &address);
+ connman_element_get_value(element,
+ CONNMAN_PROPERTY_ID_IPV4_NETMASK, &netmask);
+ connman_element_get_value(element,
+ CONNMAN_PROPERTY_ID_IPV4_BROADCAST, &broadcast);
+
+ connman_element_get_value(element,
CONNMAN_PROPERTY_ID_IPV4_NAMESERVER, &nameserver);
connman_element_get_value(element,
CONNMAN_PROPERTY_ID_IPV4_TIMESERVER, &timeserver);
connman_timeserver_remove(timeserver);
+ DBG("address %s", address);
+ DBG("netmask %s", netmask);
+ DBG("broadcast %s", broadcast);
+
if (nameserver != NULL) {
struct connman_service *service;
@@ -254,7 +161,11 @@ static void ipv4_remove(struct connman_element *element)
__connman_service_remove_nameserver(service, nameserver);
}
- clear_ipv4(element);
+ prefixlen = __connman_ipconfig_netmask_prefix_len(netmask);
+
+ if ((__connman_inet_modify_address(RTM_DELADDR, 0, element->index,
+ AF_INET, address, prefixlen, broadcast) < 0))
+ DBG("address removal failed");
}
static struct connman_driver ipv4_driver = {