summaryrefslogtreecommitdiff
path: root/src/libsystemd-network/sd-ipv4ll.c
diff options
context:
space:
mode:
authorUmut Tezduyar Lindskog <umut.tezduyar@axis.com>2014-03-21 19:23:35 +0100
committerTom Gundersen <teg@jklm.no>2014-03-21 20:24:10 +0100
commitb5db00e52ee2e20578839e4e4488f7b9af9abc38 (patch)
treeb17e48e7d09372e8d7e9b4e86356711d169a16cf /src/libsystemd-network/sd-ipv4ll.c
parent18bb8adb06002a5963a3373fa30c12cfa89b9724 (diff)
sd-ipv4ll/networkd: generate predictable addresses
Increase the chance of using the same link local address between reboots. The pseudo random sequence of addresses we attempt is now seeded with data that is very likely to stay the same between reboots, but at the same time be unique to the specific machine/nic. First we try to use the ID_NET_NAME_* data from the udev db combined with the machin-id, which is guaranteed to be unique and persistent, if available. If that is not possible (e.g., in containers where we don't have access to the udev db) we fallback to using the MAC address of the interface, which is guaranteed to be unique, and likely to be persistent. [tomegun: three minor changes: - don't expose HASH_KEY in the siphash24 header - get rid of some compile-warnings (and some casts at the same time), by using uint8_t[8] rather than uint64_t in the api - added commit message]
Diffstat (limited to 'src/libsystemd-network/sd-ipv4ll.c')
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c96
1 files changed, 74 insertions, 22 deletions
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);
}