[systemd-devel] [PATCH] sd-ipv4ll: generate predictable addresses

Umut Tezduyar Lindskog umut.tezduyar at axis.com
Tue Mar 18 12:29:08 PDT 2014


---
 TODO                               |    1 -
 src/libsystemd-network/sd-ipv4ll.c |   86 +++++++++++++++++++++++++++---------
 src/network/networkd-link.c        |   12 ++++-
 src/network/networkd.h             |    1 +
 src/shared/net-util.c              |   42 ++++++++++++++++++
 src/shared/net-util.h              |    2 +
 src/systemd/sd-ipv4ll.h            |    1 +
 src/udev/net/link-config.c         |   27 +----------
 8 files changed, 122 insertions(+), 50 deletions(-)

diff --git a/TODO b/TODO
index 99cbb9f..d0c7132 100644
--- a/TODO
+++ b/TODO
@@ -667,7 +667,6 @@ Features:
    - add reduced [Link] support to .network files
    - add IPv4LL tests (inspire by DHCP)
    - add Scope= parsing option for [Network]
-   - change LL address generation and make it predictable like get_mac() (link-config.c)
    - have smooth transition from LL to routable address, without disconnecting clients.
 
 External:
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index c6f6e01..fbe3efd 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -76,6 +76,8 @@ struct sd_ipv4ll {
         usec_t defend_window;
         int next_wakeup_valid;
         be32_t address;
+        struct random_data *random_data;
+        char *random_data_state;
         /* External */
         be32_t claimed_address;
         struct ether_addr mac_addr;
@@ -130,30 +132,27 @@ static int ipv4ll_stop(sd_ipv4ll *ll, int event) {
         return 0;
 }
 
-static be32_t ipv4ll_pick_address(sd_ipv4ll *ll) {
+static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
         be32_t addr;
+        int r;
+        int32_t random;
 
         assert(ll);
+        assert(address);
+        assert(ll->random_data);
 
-        if (ll->address) {
-                do {
-                        uint32_t r = random_u32() & 0x0000FFFF;
-                        addr = htonl(IPV4LL_NETWORK | r);
-                } while (addr == ll->address ||
-                        (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
-                        (ntohl(addr) & 0x0000FF00) == 0x0000 ||
-                        (ntohl(addr) & 0x0000FF00) == 0xFF00);
-        } else {
-                uint32_t a = 1;
-                int i;
-
-                for (i = 0; i < ETH_ALEN; i++)
-                        a += ll->mac_addr.ether_addr_octet[i]*i;
-                a = (a % 0xFE00) + 0x0100;
-                addr = htonl(IPV4LL_NETWORK | (uint32_t) a);
-        }
+        do {
+                r = random_r(ll->random_data, &random);
+                if (r < 0)
+                        return r;
+                addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
+        } while (addr == ll->address ||
+                (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
+                (ntohl(addr) & 0x0000FF00) == 0x0000 ||
+                (ntohl(addr) & 0x0000FF00) == 0xFF00);
 
-        return addr;
+        *address = addr;
+        return 0;
 }
 
 static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) {
@@ -306,7 +305,9 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void
                         ll->claimed_address = 0;
 
                         /* Pick a new address */
-                        ll->address = ipv4ll_pick_address(ll);
+                        r = ipv4ll_pick_address(ll, &ll->address);
+                        if (r < 0)
+                                goto out;
                         ll->conflict++;
                         ll->defend_window = 0;
                         ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
@@ -435,6 +436,36 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
         return 0;
 }
 
+int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint64_t entropy) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+
+        free(ll->random_data);
+        free(ll->random_data_state);
+
+        ll->random_data = new0(struct random_data, 1);
+        ll->random_data_state = new0(char, 128);
+
+        if (!ll->random_data || !ll->random_data_state) {
+                r = -ENOMEM;
+                goto error;
+        }
+
+        r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
+        if (r < 0)
+                goto error;
+
+error:
+        if (r < 0){
+                free(ll->random_data);
+                free(ll->random_data_state);
+                ll->random_data = NULL;
+                ll->random_data_state = NULL;
+        }
+        return r;
+}
+
 int sd_ipv4ll_start (sd_ipv4ll *ll) {
         int r;
 
@@ -453,8 +484,17 @@ int sd_ipv4ll_start (sd_ipv4ll *ll) {
         ll->defend_window = 0;
         ll->claimed_address = 0;
 
-        if (ll->address == 0)
-                ll->address = ipv4ll_pick_address(ll);
+        if (!ll->random_data) {
+                r = sd_ipv4ll_set_address_seed(ll, random_u64());
+                if (r < 0)
+                        goto out;
+        }
+
+        if (ll->address == 0) {
+                r = ipv4ll_pick_address(ll, &ll->address);
+                if (r < 0)
+                        goto out;
+        }
 
         ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
 
@@ -493,6 +533,8 @@ void sd_ipv4ll_free (sd_ipv4ll *ll) {
         sd_ipv4ll_stop(ll);
         sd_ipv4ll_detach_event(ll);
 
+        free(ll->random_data);
+        free(ll->random_data_state);
         free(ll);
 }
 
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 275ad97..04900e5 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -72,6 +72,8 @@ int link_new(Manager *manager, struct udev_device *device, Link **ret) {
         if (r < 0)
                 return r;
 
+        link->udev_device = udev_device_ref(device);
+
         *ret = link;
         link = NULL;
 
@@ -94,6 +96,8 @@ void link_free(Link *link) {
         free(link->ifname);
         free(link->state_file);
 
+        udev_device_unref(link->udev_device);
+
         free(link);
 }
 
@@ -893,10 +897,16 @@ static int link_acquire_conf(Link *link) {
 
         if (link->network->ipv4ll) {
                 if (!link->ipv4ll) {
+                        uint64_t seed = 0;
                         r = sd_ipv4ll_new(&link->ipv4ll);
                         if (r < 0)
                                 return r;
-
+                        r = net_get_unique_predictable_data(link->udev_device, &seed);
+                        if (r >= 0) {
+                                r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
+                                if (r < 0)
+                                        return r;
+                        }
                         r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
                         if (r < 0)
                                 return r;
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 311350c..239ef1c 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -198,6 +198,7 @@ struct Link {
         char *ifname;
         char *state_file;
         struct ether_addr mac;
+        struct udev_device *udev_device;
 
         unsigned flags;
 
diff --git a/src/shared/net-util.c b/src/shared/net-util.c
index 50cfa2c..7950af4 100644
--- a/src/shared/net-util.c
+++ b/src/shared/net-util.c
@@ -24,6 +24,8 @@
 #include <arpa/inet.h>
 #include <fnmatch.h>
 
+#include "siphash24.h"
+#include "libudev-private.h"
 #include "net-util.h"
 #include "log.h"
 #include "utf8.h"
@@ -31,6 +33,46 @@
 #include "conf-parser.h"
 #include "condition.h"
 
+#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
+
+int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result) {
+        size_t l, sz = 0;
+        const char *name;
+        int r;
+        uint8_t *v;
+
+        /* fetch some persistent data unique (on this machine) to this device */
+        name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
+        if (!name) {
+                name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
+                if (!name) {
+                        name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
+                        if (!name) {
+                                name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
+                                if (!name)
+                                        return -ENOENT;
+                        }
+                }
+        }
+
+        l = strlen(name);
+        sz = sizeof(sd_id128_t) + l;
+        v = alloca(sz);
+
+        /* fetch some persistent data unique to this machine */
+        r = sd_id128_get_machine((sd_id128_t*) v);
+        if (r < 0)
+                 return r;
+        memcpy(v + sizeof(sd_id128_t), name, l);
+
+        /* Let's hash the machine ID plus the device name. We
+        * use a fixed, but originally randomly created hash
+        * key here. */
+        siphash24(result, v, sz, HASH_KEY.bytes);
+
+        return 0;
+}
+
 bool net_match_config(const struct ether_addr *match_mac,
                       const char *match_path,
                       const char *match_driver,
diff --git a/src/shared/net-util.h b/src/shared/net-util.h
index 99479e1..01a6b72 100644
--- a/src/shared/net-util.h
+++ b/src/shared/net-util.h
@@ -62,3 +62,5 @@ int config_parse_ifalias(const char *unit, const char *filename, unsigned line,
                          int ltype, const char *rvalue, void *data, void *userdata);
 
 int net_parse_inaddr(const char *address, unsigned char *family, void *dst);
+
+int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result);
diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h
index 0207006..2397d43 100644
--- a/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/sd-ipv4ll.h
@@ -42,6 +42,7 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
 int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
 int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
+int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint64_t entropy);
 int sd_ipv4ll_start (sd_ipv4ll *ll);
 int sd_ipv4ll_stop (sd_ipv4ll *ll);
 void sd_ipv4ll_free (sd_ipv4ll *ll);
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index d3f1aff..7049181 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -303,36 +303,11 @@ static int get_mac(struct udev_device *device, bool want_random, struct ether_ad
         if (want_random)
                 random_bytes(mac->ether_addr_octet, ETH_ALEN);
         else {
-                const char *name;
                 uint8_t result[8];
-                size_t l, sz;
-                uint8_t *v;
-
-                /* fetch some persistent data unique (on this machine) to this device */
-                name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
-                if (!name) {
-                        name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
-                        if (!name) {
-                                name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
-                                if (!name)
-                                        return -ENOENT;
-                        }
-                }
 
-                l = strlen(name);
-                sz = sizeof(sd_id128_t) + l;
-                v = alloca(sz);
-
-                /* fetch some persistent data unique to this machine */
-                r = sd_id128_get_machine((sd_id128_t*) v);
+                r = net_get_unique_predictable_data(device, (uint64_t*)result);
                 if (r < 0)
                         return r;
-                memcpy(v + sizeof(sd_id128_t), name, l);
-
-                /* Let's hash the machine ID plus the device name. We
-                 * use a fixed, but originally randomly created hash
-                 * key here. */
-                siphash24(result, v, sz, HASH_KEY.bytes);
 
                 assert_cc(ETH_ALEN <= sizeof(result));
                 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
-- 
1.7.10.4



More information about the systemd-devel mailing list