--- 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); -- 1.7.10.4 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel