Using netlink to set and clear ipv4 addresses
authorCristiano Fernandes <cristiano.fernandes@hp.com>
Mon, 11 Oct 2010 22:34:39 +0000 (00:34 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 11 Oct 2010 22:34:39 +0000 (00:34 +0200)
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.

include/inet.h
src/connman.h
src/inet.c
src/ipconfig.c
src/ipv4.c

index ce0a0b2..d7af826 100644 (file)
@@ -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);
index 3a546fe..9cc0595 100644 (file)
@@ -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);
index 1b0cc45..7501ad5 100644 (file)
 
 #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;
        }
 
index 2b60e2d..0b4a84d 100644 (file)
@@ -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,
index 7f18675..7ca834d 100644 (file)
 #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 = {