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

Umut Tezduyar Lindskog umut at tezduyar.com
Fri Mar 21 00:46:03 PDT 2014


On Thu, Mar 20, 2014 at 7:16 PM, Tom Gundersen <teg at jklm.no> wrote:
> On Thu, Mar 20, 2014 at 6:52 PM, Tom Gundersen <teg at jklm.no> wrote:
>> On Wed, Mar 19, 2014 at 2:36 PM, Umut Tezduyar Lindskog
>> <umut.tezduyar at axis.com> wrote:
>>> ---
>>>  src/libsystemd-network/sd-ipv4ll.c |   86 +++++++++++++++++++++-------
>>>  src/network/networkd-link.c        |   12 +++-
>>>  src/network/networkd.h             |    1 +
>>>  src/shared/net-util.c              |   39 +++++++++++++
>>>  src/shared/net-util.h              |    2 +
>>>  src/systemd/sd-ipv4ll.h            |    1 +
>>>  src/udev/net/link-config.c         |   27 +--------
>>>  7 files changed, 119 insertions(+), 49 deletions(-)
>>>
>>> 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 fdc351f..7a589a5 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;
>>> +                        }
>>
>> I thought a bit more about this case, and I think we should try a bit
>> harder here to get a predictable seed.
>>
>> The situation I have in mind is in the container, where we will not
>> have any udev properties available, so
>> net_get_unique_predictable_data() will always fail. However, we may
>> still have a predictable mac address. Or it could be random of course,
>> we don't know, but at this point the alternative is random data
>> anyway, so might as well use the mac address. At least for the devices
>> we create ourselves from nspawn the address will be a predictable one.
>>
>> So I'd suggest replacing the if block above with
>>
>> if (r < 0) {
>>         siphash24((uint64_t*)&seed, ETH_ALEN,
>> &link->mac.ether_addr_octet, HASH_KEY.bytes)
>> }
>
> Actually, as the mac address is available also inside the library,
> maybe best to simply put this logic inside the library so the users
> don't have to care. I.e., leave the caller in networkd as you have it
> now, just replace the use of random_u32() when the seed is not set
> inside the library with a hash of the mac. What do you think?

I am fine with it but we also need a solution in case there is an
address collision and we need to pick up a new address. I don't think
we can get rid of random_32().

I have also been thinking and I am having yet another propose
(inspired by the discussion). We have to have a unique MAC for ipv4ll
to work anyways and without setting MAC, ipv4ll will not start. How
about as soon as MAC is set, we siphash it and then initialize
initstate_r in the library. This will drop the need of
sd_ipv4ll_set_address_seed API and I think simplify things a lot.

Thoughts?

>
>> (not tested)
>>
>> [You could then add back the requirement to sd_ipv4ll_start() that
>> set_address_seed() must have been called, and drop the call with
>> random data from within the library (as that would be dead code now).]
>>
>>>                          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 0c01719..b998b62 100644
>>> --- a/src/network/networkd.h
>>> +++ b/src/network/networkd.h
>>> @@ -196,6 +196,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..0b96032 100644
>>> --- a/src/shared/net-util.c
>>> +++ b/src/shared/net-util.c
>>> @@ -24,6 +24,9 @@
>>>  #include <arpa/inet.h>
>>>  #include <fnmatch.h>
>>>
>>> +#include "strv.h"
>>> +#include "siphash24.h"
>>> +#include "libudev-private.h"
>>>  #include "net-util.h"
>>>  #include "log.h"
>>>  #include "utf8.h"
>>> @@ -31,6 +34,42 @@
>>>  #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, *field = NULL;
>>> +        int r;
>>> +        uint8_t *v;
>>> +
>>> +        /* fetch some persistent data unique (on this machine) to this device */
>>> +        FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") {
>>> +                name = udev_device_get_property_value(device, field);
>>> +                if (name)
>>> +                        break;
>>> +        }
>>> +
>>> +        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
>>>
>>> _______________________________________________
>>> systemd-devel mailing list
>>> systemd-devel at lists.freedesktop.org
>>> http://lists.freedesktop.org/mailman/listinfo/systemd-devel
> _______________________________________________
> systemd-devel mailing list
> systemd-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/systemd-devel


More information about the systemd-devel mailing list