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

Tom Gundersen teg at jklm.no
Fri Mar 21 12:25:17 PDT 2014


On Fri, Mar 21, 2014 at 7:23 PM, Umut Tezduyar Lindskog
<umut.tezduyar at axis.com> wrote:
> ---
>  TODO                               |    1 -
>  src/libsystemd-network/sd-ipv4ll.c |   93 +++++++++++++++++++++++++++---------
>  src/network/networkd-link.c        |   12 +++++
>  src/network/networkd.h             |    1 +
>  src/shared/net-util.c              |   37 ++++++++++++++
>  src/shared/net-util.h              |    2 +
>  src/shared/siphash24.h             |    2 +
>  src/systemd/sd-ipv4ll.h            |    1 +
>  src/udev/net/link-config.c         |   27 +----------
>  9 files changed, 127 insertions(+), 49 deletions(-)
>
> diff --git a/TODO b/TODO
> index 50d67f7..b894e23 100644
> --- a/TODO
> +++ b/TODO
> @@ -660,7 +660,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.
>
>  * sd-network:
> diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
> index 689dce9..155c315 100644
> --- a/src/libsystemd-network/sd-ipv4ll.c
> +++ b/src/libsystemd-network/sd-ipv4ll.c
> @@ -24,6 +24,7 @@
>  #include <arpa/inet.h>
>
>  #include "util.h"
> +#include "siphash24.h"
>  #include "list.h"
>
>  #include "ipv4ll-internal.h"
> @@ -76,6 +77,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;
> @@ -128,30 +131,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) {
> @@ -304,7 +304,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);
> @@ -433,6 +435,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;
>
> @@ -451,8 +483,23 @@ 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) {
> +                uint64_t seed;
> +
> +                /* Fallback to mac */
> +                siphash24((uint8_t*) &seed, &ll->mac_addr.ether_addr_octet,
> +                          ETH_ALEN, HASH_KEY.bytes);
> +
> +                r = sd_ipv4ll_set_address_seed(ll, seed);
> +                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);
>
> @@ -491,6 +538,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 a7ba466..3131671 100644
> --- a/src/network/networkd-link.c
> +++ b/src/network/networkd-link.c
> @@ -63,6 +63,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;
>
> @@ -85,6 +87,8 @@ void link_free(Link *link) {
>          free(link->ifname);
>          free(link->state_file);
>
> +        udev_device_unref(link->udev_device);
> +
>          free(link);
>  }
>
> @@ -1258,10 +1262,18 @@ int link_add(Manager *m, struct udev_device *device, Link **ret) {
>                  return r;
>
>          if (link->network->ipv4ll) {
> +                uint64_t seed;
>                  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..a788a78 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,40 @@
>  #include "conf-parser.h"
>  #include "condition.h"
>
> +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/shared/siphash24.h b/src/shared/siphash24.h
> index 62e1168..bf9d17d 100644
> --- a/src/shared/siphash24.h
> +++ b/src/shared/siphash24.h
> @@ -3,4 +3,6 @@
>  #include <inttypes.h>
>  #include <sys/types.h>
>
> +#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
> +
>  void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]);
> 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 62439c0..41384b8 100644
> --- a/src/udev/net/link-config.c
> +++ b/src/udev/net/link-config.c
> @@ -302,36 +302,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);
> --

Applied with some minor changes, please check that it is still ok :)

Thanks for your work on this!

Cheers,

Tom


More information about the systemd-devel mailing list