[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