[systemd-commits] 5 commits - Makefile.am network/80-container-ve.network src/libsystemd src/network src/shared src/test

Lennart Poettering lennart at kemper.freedesktop.org
Wed Jun 18 09:28:56 PDT 2014


 Makefile.am                         |    3 
 network/80-container-ve.network     |    2 
 src/libsystemd/sd-rtnl/test-rtnl.c  |    1 
 src/network/networkd-address-pool.c |  166 +++++++++++++++++++++++++++++++
 src/network/networkd-address.c      |   66 ++++++++++++
 src/network/networkd-link.c         |  105 ++++++++++++--------
 src/network/networkd-manager.c      |   55 ++++++++++
 src/network/networkd.h              |   42 +++++---
 src/shared/socket-util.c            |  187 ++++++++++++++++++++++++++++++++++++
 src/shared/socket-util.h            |   13 ++
 src/test/test-socket-util.c         |   82 +++++++++++++++
 11 files changed, 669 insertions(+), 53 deletions(-)

New commits:
commit 7826397d925d92e813039fa0d2614ee7d2ecc808
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 18 18:27:55 2014 +0200

    network: automatically assign each container veth link a /28 IP address range

diff --git a/network/80-container-ve.network b/network/80-container-ve.network
index f0c02b0..cb04c7c 100644
--- a/network/80-container-ve.network
+++ b/network/80-container-ve.network
@@ -10,4 +10,6 @@ Name=ve-*
 Driver=veth
 
 [Network]
+Address=0.0.0.0/28
 IPv4LL=yes
+DHCPServer=yes

commit 4f4349389760480759612b2b1d0fb02c1743dff3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 18 18:26:03 2014 +0200

    networkd: configure dhcp server range only after successfully setting an IP address on the interface
    
    This way we can make use of the addresses of the IP pool.

diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index e7753dc..5b2dabc 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -251,6 +251,35 @@ static void link_enter_failed(Link *link) {
         link_save(link);
 }
 
+static Address* link_find_dhcp_server_address(Link *link) {
+        Address *address;
+
+        assert(link);
+        assert(link->network);
+
+        /* The the first statically configured address if there is any */
+        LIST_FOREACH(addresses, address, link->network->static_addresses) {
+
+                if (address->family != AF_INET)
+                        continue;
+
+                if (in_addr_null(address->family, &address->in_addr))
+                        continue;
+
+                return address;
+        }
+
+        /* If that didn't work, find a suitable address we got from the pool */
+        LIST_FOREACH(addresses, address, link->pool_addresses) {
+                if (address->family != AF_INET)
+                        continue;
+
+                return address;
+        }
+
+        return NULL;
+}
+
 static int link_enter_configured(Link *link) {
         int r;
 
@@ -258,10 +287,42 @@ static int link_enter_configured(Link *link) {
         assert(link->network);
         assert(link->state == LINK_STATE_SETTING_ROUTES);
 
-
         if (link->network->dhcp_server) {
+                struct in_addr pool_start;
+                Address *address;
+
+                address = link_find_dhcp_server_address(link);
+                if (!address) {
+                        log_warning_link(link, "Failed to find suitable address for DHCPv4 server instance.");
+                        link_enter_failed(link);
+                        return 0;
+                }
+
                 log_debug_link(link, "offering DHCPv4 leases");
 
+                r = sd_dhcp_server_set_address(link->dhcp_server, &address->in_addr.in);
+                if (r < 0)
+                        return r;
+
+                /* offer 32 addresses starting from the address following the server address */
+                pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1);
+                r = sd_dhcp_server_set_lease_pool(link->dhcp_server,
+                                                  &pool_start, 32);
+                if (r < 0)
+                        return r;
+
+                /* TODO:
+                r = sd_dhcp_server_set_router(link->dhcp_server,
+                                              &main_address->in_addr.in);
+                if (r < 0)
+                        return r;
+
+                r = sd_dhcp_server_set_prefixlen(link->dhcp_server,
+                                                 main_address->prefixlen);
+                if (r < 0)
+                        return r;
+                */
+
                 r = sd_dhcp_server_start(link->dhcp_server);
                 if (r < 0) {
                         log_warning_link(link, "could not start DHCPv4 server "
@@ -1723,8 +1784,6 @@ static int link_configure(Link *link) {
         }
 
         if (link->network->dhcp_server) {
-                Address *address;
-
                 r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex);
                 if (r < 0)
                         return r;
@@ -1732,41 +1791,6 @@ static int link_configure(Link *link) {
                 r = sd_dhcp_server_attach_event(link->dhcp_server, NULL, 0);
                 if (r < 0)
                         return r;
-
-                LIST_FOREACH(addresses, address,
-                             link->network->static_addresses) {
-                        struct in_addr pool_start;
-
-                        if (address->family != AF_INET)
-                                continue;
-
-                        /* currently this is picked essentially at random */
-                        r = sd_dhcp_server_set_address(link->dhcp_server,
-                                                       &address->in_addr.in);
-                        if (r < 0)
-                                return r;
-
-                        /* offer 32 addresses starting from the address following the server address */
-                        pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1);
-                        r = sd_dhcp_server_set_lease_pool(link->dhcp_server,
-                                                          &pool_start, 32);
-                        if (r < 0)
-                                return r;
-
-                        break;
-                }
-
-                /* TODO:
-                r = sd_dhcp_server_set_router(link->dhcp_server,
-                                              &main_address->in_addr.in);
-                if (r < 0)
-                        return r;
-
-                r = sd_dhcp_server_set_prefixlen(link->dhcp_server,
-                                                 main_address->prefixlen);
-                if (r < 0)
-                        return r;
-                */
         }
 
         if (link_has_carrier(link->flags, link->kernel_operstate)) {

commit 11bf3cced13c885ca215c108cb0bdb7a148520d6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 18 18:22:14 2014 +0200

    networkd: add address pool support
    
    When an address is configured to be all zeroes, networkd will now
    automatically find a locally unused network of the right size from a
    list of pre-configured pools. Currently those pools are 10.0.0.0/8,
    172.16.0.0/12, 192.168.0.0/16 and fc00::/7, i.e. the network ranges for
    private networks. They are compiled in, but should be configurable
    eventually.
    
    This allows applying the same configuration to a large number of
    interfaces with each time a different IP range block, and management of
    these IP ranges is fully automatic.
    
    When allocating an address range from the pool it is made sure the range
    is not used otherwise.

diff --git a/Makefile.am b/Makefile.am
index dea0633..82145c6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4384,7 +4384,8 @@ libsystemd_networkd_core_la_SOURCES = \
 	src/network/networkd-network.c \
 	src/network/networkd-address.c \
 	src/network/networkd-route.c \
-	src/network/networkd-manager.c
+	src/network/networkd-manager.c \
+	src/network/networkd-address-pool.c
 
 nodist_libsystemd_networkd_core_la_SOURCES = \
 	src/network/networkd-network-gperf.c \
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
new file mode 100644
index 0000000..a5079ad
--- /dev/null
+++ b/src/network/networkd-address-pool.c
@@ -0,0 +1,166 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <arpa/inet.h>
+
+#include "networkd.h"
+
+int address_pool_new(
+                Manager *m,
+                AddressPool **ret,
+                unsigned family,
+                const union in_addr_union *u,
+                unsigned prefixlen) {
+
+        AddressPool *p;
+
+        assert(m);
+        assert(ret);
+        assert(u);
+
+        p = new0(AddressPool, 1);
+        if (!p)
+                return -ENOMEM;
+
+        p->manager = m;
+        p->family = family;
+        p->prefixlen = prefixlen;
+        p->in_addr = *u;
+
+        LIST_PREPEND(address_pools, m->address_pools, p);
+
+        *ret = p;
+        return 0;
+}
+
+int address_pool_new_from_string(
+                Manager *m,
+                AddressPool **ret,
+                unsigned family,
+                const char *p,
+                unsigned prefixlen) {
+
+        union in_addr_union u;
+        int r;
+
+        assert(m);
+        assert(ret);
+        assert(p);
+
+        r = in_addr_from_string(family, p, &u);
+        if (r < 0)
+                return r;
+
+        return address_pool_new(m, ret, family, &u, prefixlen);
+}
+
+void address_pool_free(AddressPool *p) {
+
+        if (!p)
+                return;
+
+        if (p->manager)
+                LIST_REMOVE(address_pools, p->manager->address_pools, p);
+
+        free(p);
+}
+
+static bool address_pool_prefix_is_taken(
+                AddressPool *p,
+                const union in_addr_union *u,
+                unsigned prefixlen) {
+
+        Iterator i;
+        Link *l;
+        Network *n;
+
+        assert(p);
+        assert(u);
+
+        HASHMAP_FOREACH(l, p->manager->links, i) {
+                Address *a;
+
+                /* Don't clash with assigned addresses */
+                LIST_FOREACH(addresses, a, l->addresses) {
+                        if (a->family != p->family)
+                                continue;
+
+                        if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
+                                return true;
+                }
+
+                /* Don't clash with addresses already pulled from the pool, but not assigned yet */
+                LIST_FOREACH(addresses, a, l->pool_addresses) {
+                        if (a->family != p->family)
+                                continue;
+
+                        if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
+                                return true;
+                }
+        }
+
+        /* And don't clash with configured but un-assigned addresses either */
+        LIST_FOREACH(networks, n, p->manager->networks) {
+                Address *a;
+
+                LIST_FOREACH(addresses, a, n->static_addresses) {
+                        if (a->family != p->family)
+                                continue;
+
+                        if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
+                                return true;
+                }
+        }
+
+        return false;
+}
+
+int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found) {
+        union in_addr_union u;
+
+        assert(p);
+        assert(prefixlen > 0);
+        assert(found);
+
+        if (p->prefixlen > prefixlen)
+                return 0;
+
+        u = p->in_addr;
+        for (;;) {
+                if (!address_pool_prefix_is_taken(p, &u, prefixlen)) {
+                        _cleanup_free_ char *s = NULL;
+
+                        in_addr_to_string(p->family, &u, &s);
+                        log_debug("Found range %s/%u", strna(s), prefixlen);
+
+                        *found = u;
+                        return 1;
+                }
+
+                if (!in_addr_prefix_next(p->family, &u, prefixlen))
+                        return 0;
+
+                if (!in_addr_prefix_intersect(p->family, &p->in_addr, p->prefixlen, &u, prefixlen))
+                        return 0;
+        }
+
+        return 0;
+}
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index afd36a0..ce01500 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -228,6 +228,68 @@ int address_update(Address *address, Link *link,
         return 0;
 }
 
+static int address_acquire(Link *link, Address *original, Address **ret) {
+        union in_addr_union in_addr = {};
+        struct in_addr broadcast = {};
+        Address *na = NULL;
+        int r;
+
+        assert(link);
+        assert(original);
+        assert(ret);
+
+        /* Something useful was configured? just use it */
+        if (in_addr_null(original->family, &original->in_addr) <= 0)
+                return 0;
+
+        /* The address is configured to be 0.0.0.0 or [::] by the user?
+         * Then let's acquire something more useful from the pool. */
+        r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
+        if (r < 0) {
+                log_error_link(link, "Failed to acquire address from pool: %s", strerror(-r));
+                return r;
+        }
+        if (r == 0) {
+                log_error_link(link, "Couldn't find free address for interface, all taken.");
+                return -EBUSY;
+        }
+
+        if (original->family == AF_INET) {
+                /* Pick first address in range for ourselves ...*/
+                in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
+
+                /* .. and use last as broadcast address */
+                broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen);
+        } else if (original->family == AF_INET6)
+                in_addr.in6.s6_addr[15] |= 1;
+
+        r = address_new_dynamic(&na);
+        if (r < 0)
+                return r;
+
+        na->family = original->family;
+        na->prefixlen = original->prefixlen;
+        na->scope = original->scope;
+        na->cinfo = original->cinfo;
+
+        if (original->label) {
+                na->label = strdup(original->label);
+
+                if (!na->label) {
+                        free(na);
+                        return -ENOMEM;
+                }
+        }
+
+        na->broadcast = broadcast;
+        na->in_addr = in_addr;
+
+        LIST_PREPEND(addresses, link->pool_addresses, na);
+
+        *ret = na;
+        return 0;
+}
+
 int address_configure(Address *address, Link *link,
                       sd_rtnl_message_handler_t callback) {
         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
@@ -240,6 +302,10 @@ int address_configure(Address *address, Link *link,
         assert(link->manager);
         assert(link->manager->rtnl);
 
+        r = address_acquire(link, address, &address);
+        if (r < 0)
+                return r;
+
         r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
                                      link->ifindex, address->family);
         if (r < 0) {
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index d81a3bf..e7753dc 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -112,6 +112,11 @@ static void link_free(Link *link) {
                 address_free(address);
         }
 
+        while ((address = link->pool_addresses)) {
+                LIST_REMOVE(addresses, link->pool_addresses, address);
+                address_free(address);
+        }
+
         sd_dhcp_client_unref(link->dhcp_client);
         sd_dhcp_lease_unref(link->dhcp_lease);
 
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 2a0d534..5cc8872 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -75,6 +75,33 @@ static int setup_signals(Manager *m) {
         return 0;
 }
 
+static int setup_default_address_pool(Manager *m) {
+        AddressPool *p;
+        int r;
+
+        assert(m);
+
+        /* Add in the well-known private address ranges. */
+
+        r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7);
+        if (r < 0)
+                return r;
+
+        r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16);
+        if (r < 0)
+                return r;
+
+        r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12);
+        if (r < 0)
+                return r;
+
+        r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int manager_new(Manager **ret) {
         _cleanup_manager_free_ Manager *m = NULL;
         int r;
@@ -129,6 +156,10 @@ int manager_new(Manager **ret) {
 
         LIST_HEAD_INIT(m->networks);
 
+        r = setup_default_address_pool(m);
+        if (r < 0)
+                return r;
+
         *ret = m;
         m = NULL;
 
@@ -139,6 +170,7 @@ void manager_free(Manager *m) {
         Network *network;
         NetDev *netdev;
         Link *link;
+        AddressPool *pool;
 
         if (!m)
                 return;
@@ -164,6 +196,9 @@ void manager_free(Manager *m) {
                 netdev_unref(netdev);
         hashmap_free(m->netdevs);
 
+        while ((pool = m->address_pools))
+                address_pool_free(pool);
+
         sd_rtnl_unref(m->rtnl);
 
         free(m);
@@ -460,3 +495,23 @@ finish:
 
         return r;
 }
+
+int manager_address_pool_acquire(Manager *m, unsigned family, unsigned prefixlen, union in_addr_union *found) {
+        AddressPool *p;
+        int r;
+
+        assert(m);
+        assert(prefixlen > 0);
+        assert(found);
+
+        LIST_FOREACH(address_pools, p, m->address_pools) {
+                if (p->family != family)
+                        continue;
+
+                r = address_pool_acquire(p, prefixlen, found);
+                if (r != 0)
+                        return r;
+        }
+
+        return 0;
+}
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 308be83..7069a11 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -47,6 +47,7 @@ typedef struct Link Link;
 typedef struct Address Address;
 typedef struct Route Route;
 typedef struct Manager Manager;
+typedef struct AddressPool AddressPool;
 
 typedef struct netdev_enslave_callback netdev_enslave_callback;
 
@@ -259,9 +260,22 @@ struct Link {
         uint16_t original_mtu;
         sd_ipv4ll *ipv4ll;
 
+        LIST_HEAD(Address, pool_addresses);
+
         sd_dhcp_server *dhcp_server;
 };
 
+struct AddressPool {
+        Manager *manager;
+
+        unsigned family;
+        unsigned prefixlen;
+
+        union in_addr_union in_addr;
+
+        LIST_FIELDS(AddressPool, address_pools);
+};
+
 struct Manager {
         sd_rtnl *rtnl;
         sd_event *event;
@@ -277,6 +291,7 @@ struct Manager {
         Hashmap *links;
         Hashmap *netdevs;
         LIST_HEAD(Network, networks);
+        LIST_HEAD(AddressPool, address_pools);
 
         usec_t network_dirs_ts_usec;
 };
@@ -299,6 +314,8 @@ int manager_bus_listen(Manager *m);
 
 int manager_save(Manager *m);
 
+int manager_address_pool_acquire(Manager *m, unsigned family, unsigned prefixlen, union in_addr_union *found);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 #define _cleanup_manager_free_ _cleanup_(manager_freep)
 
@@ -449,6 +466,14 @@ LinkOperationalState link_operstate_from_string(const char *s) _pure_;
 DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
 #define _cleanup_link_unref_ _cleanup_(link_unrefp)
 
+/* Address Pool */
+
+int address_pool_new(Manager *m, AddressPool **ret, unsigned family, const union in_addr_union *u, unsigned prefixlen);
+int address_pool_new_from_string(Manager *m, AddressPool **ret, unsigned family, const char *p, unsigned prefixlen);
+void address_pool_free(AddressPool *p);
+
+int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found);
+
 /* Macros which append INTERFACE= to the message */
 
 #define log_full_link(level, link, fmt, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "INTERFACE=", link->ifname, "%-*s: " fmt, IFNAMSIZ, link->ifname, ##__VA_ARGS__)

commit 059f6c42b744a18d0deec0c79a9e0730ec6c1c76
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 18 18:17:39 2014 +0200

    networkd: add a number of calls to manipulate in_addr_union structs

diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
index 92564e3..f8c6795 100644
--- a/src/shared/socket-util.c
+++ b/src/shared/socket-util.c
@@ -640,6 +640,193 @@ int socket_address_unlink(SocketAddress *a) {
         return 1;
 }
 
+int in_addr_null(unsigned family, union in_addr_union *u) {
+        assert(u);
+
+        if (family == AF_INET)
+                return u->in.s_addr == 0;
+
+        if (family == AF_INET6)
+                return
+                        u->in6.s6_addr32[0] == 0 &&
+                        u->in6.s6_addr32[1] == 0 &&
+                        u->in6.s6_addr32[2] == 0 &&
+                        u->in6.s6_addr32[3] == 0;
+
+        return -EAFNOSUPPORT;
+}
+
+
+int in_addr_equal(unsigned family, union in_addr_union *a, union in_addr_union *b) {
+        assert(a);
+        assert(b);
+
+        if (family == AF_INET)
+                return a->in.s_addr == b->in.s_addr;
+
+        if (family == AF_INET6)
+                return
+                        a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
+                        a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
+                        a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
+                        a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_prefix_intersect(
+                unsigned family,
+                const union in_addr_union *a,
+                unsigned aprefixlen,
+                const union in_addr_union *b,
+                unsigned bprefixlen) {
+
+        unsigned m;
+
+        assert(a);
+        assert(b);
+
+        /* Checks whether there are any addresses that are in both
+         * networks */
+
+        m = MIN(aprefixlen, bprefixlen);
+
+        if (family == AF_INET) {
+                uint32_t x, nm;
+
+                x = be32toh(a->in.s_addr ^ b->in.s_addr);
+                nm = 0xFFFFFFFFUL << (32 - m);
+
+                return (x & nm) == 0;
+        }
+
+        if (family == AF_INET6) {
+                unsigned i;
+
+                if (m > 128)
+                        m = 128;
+
+                for (i = 0; i < 16; i++) {
+                        uint8_t x, nm;
+
+                        x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
+
+                        if (m < 8)
+                                nm = 0xFF << (8 - m);
+                        else
+                                nm = 0xFF;
+
+                        if ((x & nm) != 0)
+                                return 0;
+
+                        if (m > 8)
+                                m -= 8;
+                        else
+                                m = 0;
+                }
+
+                return 1;
+        }
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_prefix_next(unsigned family, union in_addr_union *u, unsigned prefixlen) {
+        assert(u);
+
+        /* Increases the network part of an address by one. Returns
+         * positive it that succeeds, or 0 if this overflows. */
+
+        if (prefixlen <= 0)
+                return 0;
+
+        if (family == AF_INET) {
+                uint32_t c, n;
+
+                if (prefixlen > 32)
+                        prefixlen = 32;
+
+                c = be32toh(u->in.s_addr);
+                n = c + (1UL << (32 - prefixlen));
+                if (n < c)
+                        return 0;
+                n &= 0xFFFFFFFFUL << (32 - prefixlen);
+
+                u->in.s_addr = htobe32(n);
+                return 1;
+        }
+
+        if (family == AF_INET6) {
+                struct in6_addr add = {}, result;
+                uint8_t overflow = 0;
+                unsigned i;
+
+                if (prefixlen > 128)
+                        prefixlen = 128;
+
+                /* First calculate what we have to add */
+                add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
+
+                for (i = 16; i > 0; i--) {
+                        unsigned j = i - 1;
+
+                        result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
+                        overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
+                }
+
+                if (overflow)
+                        return 0;
+
+                u->in6 = result;
+                return 1;
+        }
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_to_string(unsigned family, const union in_addr_union *u, char **ret) {
+        char *x;
+        size_t l;
+
+        assert(u);
+        assert(ret);
+
+        if (family == AF_INET)
+                l = INET_ADDRSTRLEN;
+        else if (family == AF_INET6)
+                l = INET6_ADDRSTRLEN;
+        else
+                return -EAFNOSUPPORT;
+
+        x = new(char, l);
+        if (!x)
+                return -ENOMEM;
+
+        errno = 0;
+        if (!inet_ntop(family, u, x, l)) {
+                free(x);
+                return errno ? -errno : -EINVAL;
+        }
+
+        *ret = x;
+        return 0;
+}
+
+int in_addr_from_string(unsigned family, const char *s, union in_addr_union *ret) {
+
+        assert(s);
+        assert(ret);
+
+        if (!IN_SET(family, AF_INET, AF_INET6))
+                return -EAFNOSUPPORT;
+
+        errno = 0;
+        if (inet_pton(family, s, ret) <= 0)
+                return errno ? -errno : -EINVAL;
+
+        return 0;
+}
+
 static const char* const netlink_family_table[] = {
         [NETLINK_ROUTE] = "route",
         [NETLINK_FIREWALL] = "firewall",
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
index d125fca..25c4a7e 100644
--- a/src/shared/socket-util.h
+++ b/src/shared/socket-util.h
@@ -111,3 +111,10 @@ SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *
 
 int netlink_family_to_string_alloc(int b, char **s);
 int netlink_family_from_string(const char *s) _pure_;
+
+int in_addr_null(unsigned family, union in_addr_union *u);
+int in_addr_equal(unsigned family, union in_addr_union *a, union in_addr_union *b);
+int in_addr_prefix_intersect(unsigned family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
+int in_addr_prefix_next(unsigned family, union in_addr_union *u, unsigned prefixlen);
+int in_addr_to_string(unsigned family, const union in_addr_union *u, char **ret);
+int in_addr_from_string(unsigned family, const char *s, union in_addr_union *ret);
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 716efd8..9f42dbf 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -20,6 +20,7 @@
 #include "socket-util.h"
 #include "util.h"
 #include "macro.h"
+#include "log.h"
 
 static void test_socket_address_parse(void) {
         SocketAddress a;
@@ -137,9 +138,90 @@ static void test_socket_address_get_path(void) {
         assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
 }
 
+static void test_in_addr_prefix_intersect_one(unsigned f, const char *a, unsigned apl, const char *b, unsigned bpl, int result) {
+        union in_addr_union ua, ub;
+
+        assert_se(in_addr_from_string(f, a, &ua) >= 0);
+        assert_se(in_addr_from_string(f, b, &ub) >= 0);
+
+        assert_se(in_addr_prefix_intersect(f, &ua, apl, &ub, bpl) == result);
+}
+
+static void test_in_addr_prefix_intersect(void) {
+
+        test_in_addr_prefix_intersect_one(AF_INET, "255.255.255.255", 32, "255.255.255.254", 32, 0);
+        test_in_addr_prefix_intersect_one(AF_INET, "255.255.255.255", 0, "255.255.255.255", 32, 1);
+        test_in_addr_prefix_intersect_one(AF_INET, "0.0.0.0", 0, "47.11.8.15", 32, 1);
+
+        test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.1", 24, 1);
+        test_in_addr_prefix_intersect_one(AF_INET, "2.2.2.2", 24, "1.1.1.1", 24, 0);
+
+        test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.127", 25, 1);
+        test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.127", 26, 1);
+        test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 25, "1.1.1.127", 25, 1);
+        test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 25, "1.1.1.255", 25, 0);
+
+        test_in_addr_prefix_intersect_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", 128, 0);
+        test_in_addr_prefix_intersect_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 1);
+        test_in_addr_prefix_intersect_one(AF_INET6, "::", 0, "beef:beef:beef:beef:beef:beef:beef:beef", 128, 1);
+
+        test_in_addr_prefix_intersect_one(AF_INET6, "1::2", 64, "1::2", 64, 1);
+        test_in_addr_prefix_intersect_one(AF_INET6, "2::2", 64, "1::2", 64, 0);
+
+        test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 120, "1::007f", 121, 1);
+        test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 120, "1::007f", 122, 1);
+        test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 121, "1::007f", 121, 1);
+        test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 121, "1::00ff", 121, 0);
+}
+
+static void test_in_addr_prefix_next_one(unsigned f, const char *before, unsigned pl, const char *after) {
+        union in_addr_union ubefore, uafter, t;
+
+        assert_se(in_addr_from_string(f, before, &ubefore) >= 0);
+
+        t = ubefore;
+        assert_se((in_addr_prefix_next(f, &t, pl) > 0) == !!after);
+
+        if (after) {
+                assert_se(in_addr_from_string(f, after, &uafter) >= 0);
+                assert_se(in_addr_equal(f, &t, &uafter) > 0);
+        }
+}
+
+static void test_in_addr_prefix_next(void) {
+
+        test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 24, "192.168.1.0");
+        test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 16, "192.169.0.0");
+        test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 20, "192.168.16.0");
+
+        test_in_addr_prefix_next_one(AF_INET, "0.0.0.0", 32, "0.0.0.1");
+        test_in_addr_prefix_next_one(AF_INET, "255.255.255.255", 32, NULL);
+        test_in_addr_prefix_next_one(AF_INET, "255.255.255.0", 24, NULL);
+
+        test_in_addr_prefix_next_one(AF_INET6, "4400::", 128, "4400::0001");
+        test_in_addr_prefix_next_one(AF_INET6, "4400::", 120, "4400::0100");
+        test_in_addr_prefix_next_one(AF_INET6, "4400::", 127, "4400::0002");
+        test_in_addr_prefix_next_one(AF_INET6, "4400::", 8, "4500::");
+        test_in_addr_prefix_next_one(AF_INET6, "4400::", 7, "4600::");
+
+        test_in_addr_prefix_next_one(AF_INET6, "::", 128, "::1");
+
+        test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, NULL);
+        test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120, NULL);
+
+}
+
 int main(int argc, char *argv[]) {
+
+        log_set_max_level(LOG_DEBUG);
+
         test_socket_address_parse();
         test_socket_address_parse_netlink();
         test_socket_address_equal();
         test_socket_address_get_path();
+
+        test_in_addr_prefix_intersect();
+        test_in_addr_prefix_next();
+
+        return 0;
 }

commit 5d3de3fe9cc452f1bfe3c2dcafecbd7f904da4dc
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 18 12:34:02 2014 +0200

    socket-util: introduce in_addr_union similar to sockaddr_union and make use of it everywhere

diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c
index 78f5cca..e6ba08e 100644
--- a/src/libsystemd/sd-rtnl/test-rtnl.c
+++ b/src/libsystemd/sd-rtnl/test-rtnl.c
@@ -20,6 +20,7 @@
 ***/
 
 #include <netinet/ether.h>
+#include <net/if.h>
 
 #include "util.h"
 #include "macro.h"
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 3b081d5..308be83 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -36,6 +36,7 @@
 #include "list.h"
 #include "set.h"
 #include "condition-util.h"
+#include "socket-util.h"
 
 #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
 #define VXLAN_VID_MAX (1u << 24) - 1
@@ -184,10 +185,7 @@ struct Address {
         struct in_addr broadcast;
         struct ifa_cacheinfo cinfo;
 
-        union {
-                struct in_addr in;
-                struct in6_addr in6;
-        } in_addr;
+        union in_addr_union in_addr;
 
         LIST_FIELDS(Address, addresses);
 };
@@ -201,15 +199,8 @@ struct Route {
         unsigned char scope;
         uint32_t metrics;
 
-        union {
-                struct in_addr in;
-                struct in6_addr in6;
-        } in_addr;
-
-        union {
-                struct in_addr in;
-                struct in6_addr in6;
-        } dst_addr;
+        union in_addr_union in_addr;
+        union in_addr_union dst_addr;
 
         LIST_FIELDS(Route, routes);
 };
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
index f938f86..d125fca 100644
--- a/src/shared/socket-util.h
+++ b/src/shared/socket-util.h
@@ -24,7 +24,6 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <sys/un.h>
-#include <net/if.h>
 #include <asm/types.h>
 #include <linux/netlink.h>
 #include <linux/if_packet.h>
@@ -42,6 +41,11 @@ union sockaddr_union {
         struct sockaddr_ll ll;
 };
 
+union in_addr_union {
+        struct in_addr in;
+        struct in6_addr in6;
+};
+
 typedef struct SocketAddress {
         union sockaddr_union sockaddr;
 



More information about the systemd-commits mailing list