From: Cristiano Fernandes <[email protected]>

Currently connman uses ioctl to set address, netmask and broadcast separately.
This imples that when a ip address with a non default netmask is set to an
interface (192.168.1.10/20 for instance), a netlink event will be trigged
announcing that the address has been configured with default netmask
(192.168.1.10/24). Connman will then signal an IPv4 change with the wrong
netmask. When the correct netmask is set with ioctl, a removal netlink event
will be trigged that connman will signal again as an IPv4 change. Finally,
the correct address and netmask are set, another netlink event announces an
address change and connman signals IPv4 change with the correct values.

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     |    8 ++-
 include/ipconfig.h |    2 +
 src/inet.c         |  199 ++++++++++++++++++++++++++++++----------------------
 src/ipconfig.c     |    7 +-
 src/ipv4.c         |  138 ++++++------------------------------
 5 files changed, 149 insertions(+), 205 deletions(-)

diff --git a/include/inet.h b/include/inet.h
index ce0a0b2..d1b4896 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>
@@ -42,8 +44,12 @@ int connman_inet_ifdown(int index);
 struct connman_device *connman_inet_create_device(int index);
 connman_bool_t connman_inet_is_cfg80211(int index);
 
+int connman_inet_modify_address(int cmd, int flags, int index,
+                               const char *address,
+                               const char *netmask,
+                               const char *broadcast);
 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/include/ipconfig.h b/include/ipconfig.h
index 28a3d6a..ad4dbf4 100644
--- a/include/ipconfig.h
+++ b/include/ipconfig.h
@@ -74,6 +74,8 @@ struct connman_ipconfig_ops {
        void (*ip_release) (struct connman_ipconfig *ipconfig);
 };
 
+unsigned char netmask2prefixlen(const char *netmask);
+
 struct connman_ipconfig *connman_ipconfig_create(int index);
 struct connman_ipconfig *connman_ipconfig_clone(struct connman_ipconfig 
*ipconfig);
 struct connman_ipconfig *connman_ipconfig_ref(struct connman_ipconfig 
*ipconfig);
diff --git a/src/inet.c b/src/inet.c
index 3f1547a..28fc4f8 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -39,6 +39,104 @@
 
 #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,
+                               const char *address,
+                               const char *netmask,
+                               const char *broadcast)
+{
+       union {
+               struct sockaddr sa;
+               struct sockaddr_nl nl;
+       } sa;
+       union {
+               struct nlmsghdr header;
+               uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+                               NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
+                               RTA_LENGTH(sizeof(struct in6_addr))];
+       } request;
+       struct ifaddrmsg *ifaddrmsg;
+       uint32_t addr, nmask, bcast;
+       unsigned char prefixlen;
+       int sk;
+
+       DBG("");
+
+       if (address == NULL || netmask == NULL)
+               return -1;
+
+       sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       if (sk < 0)
+               return -1;
+
+       addr = inet_addr(address);
+       nmask = inet_addr(netmask);
+       prefixlen = netmask2prefixlen(netmask);
+
+       if (broadcast != NULL)
+               bcast = inet_addr(broadcast);
+       else
+               bcast = addr | ~nmask;
+
+       memset(&request, 0, sizeof(request));
+
+       request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       request.header.nlmsg_type = cmd;
+       request.header.nlmsg_flags = NLM_F_REQUEST|flags;
+       request.header.nlmsg_seq = 1;
+
+       ifaddrmsg = NLMSG_DATA(&request.header);
+       ifaddrmsg->ifa_family = AF_INET;
+
+       ifaddrmsg->ifa_prefixlen = prefixlen;
+
+       ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
+       ifaddrmsg->ifa_scope = RT_SCOPE_UNIVERSE;
+       ifaddrmsg->ifa_index = index;
+
+       if (add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
+                       &addr, sizeof(addr)) < 0)
+               return -1;
+
+       if (add_rtattr(&request.header, sizeof(request), IFA_BROADCAST,
+                       &bcast, sizeof(bcast)) < 0)
+               return -1;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.nl.nl_family = AF_NETLINK;
+
+       if (sendto(sk, &request, request.header.nlmsg_len,
+                               0, &sa.sa, sizeof(sa)) < 0)
+               return -1;
+
+       close(sk);
+
+       return 0;
+}
+
 int connman_inet_ifindex(const char *name)
 {
        struct ifreq ifr;
@@ -551,67 +649,22 @@ out:
 
 int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
 {
-       struct ifreq ifr;
-       struct sockaddr_in addr;
-       int sk, err;
+       struct in_addr netmask;
 
-       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;
-       }
+       DBG("");
 
-       DBG("ifname %s", ifr.ifr_name);
+       netmask.s_addr = htonl(~(0xfffffffflu >> ipaddress->prefixlen));
 
-       if (ipaddress->local == NULL) {
-               close(sk);
+       if ((connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE|NLM_F_ACK,
+                       index, ipaddress->local, inet_ntoa(netmask),
+                       ipaddress->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;
 }
 
@@ -647,40 +700,18 @@ out:
        return err;
 }
 
-int connman_inet_clear_address(int index)
+int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
 {
+       const char *address = NULL, *broadcast = NULL;
+       struct in_addr netmask;
 
+       address = ipaddress->local;
+       broadcast = ipaddress->broadcast;
+       netmask.s_addr = htonl(~(0xfffffffflu >> ipaddress->prefixlen));
 
-       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);
-
-       if (err < 0 && errno != EADDRNOTAVAIL) {
-               DBG("address removal failed (%s)", strerror(errno));
+       if ((connman_inet_modify_address(RTM_DELADDR, 0, index, address,
+                       inet_ntoa(netmask), broadcast)) < 0) {
+               DBG("address removal failed");
                return -1;
        }
 
diff --git a/src/ipconfig.c b/src/ipconfig.c
index 0a7eb8b..69b6d74 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 netmask2prefixlen(const char *netmask)
 {
        unsigned char bits = 0;
        in_addr_t mask = inet_network(netmask);
@@ -412,7 +412,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);
@@ -1181,7 +1181,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 7f18675..abe9063 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,11 @@ 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;
 
        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 +92,9 @@ 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;
-
-       set_ipv4(element, &ipv4);
+       if ((connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE|NLM_F_ACK,
+                       element->index, address, netmask, broadcast)) < 0)
+               DBG("address setting failed");
 
        service = __connman_element_get_service(element);
 
@@ -235,18 +125,30 @@ 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;
 
        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 +156,9 @@ static void ipv4_remove(struct connman_element *element)
                __connman_service_remove_nameserver(service, nameserver);
        }
 
-       clear_ipv4(element);
+       if ((connman_inet_modify_address(RTM_DELADDR, 0, element->index,
+                       address, netmask, broadcast) < 0))
+               DBG("address removal failed");
 }
 
 static struct connman_driver ipv4_driver = {
-- 
1.7.2.3

_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman

Reply via email to