summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO1
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c96
-rw-r--r--src/network/networkd-link.c12
-rw-r--r--src/network/networkd.h1
-rw-r--r--src/shared/net-util.c39
-rw-r--r--src/shared/net-util.h2
-rw-r--r--src/systemd/sd-ipv4ll.h1
-rw-r--r--src/udev/net/link-config.c29
8 files changed, 130 insertions, 51 deletions
diff --git a/TODO b/TODO
index 50d67f7273..b894e235ed 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 ad8b4e3b43..7431af729e 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);
@@ -448,6 +450,39 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
return 0;
}
+int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) {
+ unsigned int entropy = *seed;
+ 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;
+}
+
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
int sd_ipv4ll_start (sd_ipv4ll *ll) {
int r;
@@ -466,8 +501,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) {
+ uint8_t seed[8];
+
+ /* Fallback to mac */
+ siphash24(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);
@@ -506,6 +556,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 14cc8715ce..4044f0b5a5 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);
}
@@ -1283,10 +1287,18 @@ int link_add(Manager *m, struct udev_device *device, Link **ret) {
return r;
if (link->network->ipv4ll) {
+ uint8_t seed[8];
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 311350c1e2..239ef1cd38 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 50cfa2c3f3..1c223117b8 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, uint8_t result[8]) {
+ 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 99479e1f5c..4a8d3f9fdf 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, uint8_t result[8]);
diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h
index 0207006ff7..6273c8917a 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, uint8_t seed[8]);
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 62439c0c71..472a4150c3 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -294,44 +294,17 @@ static bool mac_is_permanent(struct udev_device *device) {
return type == 0;
}
-#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
-
static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
int r;
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, 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);