#include <arpa/inet.h>
#include "util.h"
+#include "siphash24.h"
#include "list.h"
#include "ipv4ll-internal.h"
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;
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) {
return 0;
}
-static void ipv4ll_set_next_wakeup (sd_ipv4ll *ll, int sec, int random_sec) {
+static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
usec_t next_timeout = 0;
usec_t time_now = 0;
if (random_sec)
next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
- if (sd_event_get_now_monotonic(ll->event, &time_now) < 0)
+ if (sd_event_now(ll->event, CLOCK_MONOTONIC, &time_now) < 0)
time_now = now(CLOCK_MONOTONIC);
ll->next_wakeup = time_now + next_timeout;
if (ipv4ll_arp_conflict(ll, in_packet)) {
- r = sd_event_get_now_monotonic(ll->event, &time_now);
+ r = sd_event_now(ll->event, CLOCK_MONOTONIC, &time_now);
if (r < 0)
goto out;
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);
if (ll->next_wakeup_valid) {
ll->timer = sd_event_source_unref(ll->timer);
- r = sd_event_add_monotonic(ll->event, &ll->timer,
- ll->next_wakeup, 0, ipv4ll_timer, ll);
+ r = sd_event_add_time(ll->event, &ll->timer, CLOCK_MONOTONIC,
+ ll->next_wakeup, 0, ipv4ll_timer, ll);
if (r < 0)
goto out;
}
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
+ bool need_restart = false;
+
assert_return(ll, -EINVAL);
- assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);
+ assert_return(addr, -EINVAL);
+
+ if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
+ return 0;
+
+ if (ll->state != IPV4LL_STATE_INIT) {
+ log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
+ "client, restarting");
+ sd_ipv4ll_stop(ll);
+ need_restart = true;
+ }
- memcpy(&ll->mac_addr.ether_addr_octet, addr, ETH_ALEN);
+ memcpy(&ll->mac_addr, addr, ETH_ALEN);
+
+ if (need_restart)
+ sd_ipv4ll_start(ll);
return 0;
}
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;
+}
+
+bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
+ assert_return(ll, -EINVAL);
+
+ return ll->state != IPV4LL_STATE_INIT;
+}
+
+#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;
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);
if (r < 0)
goto out;
- r = sd_event_add_monotonic(ll->event, &ll->timer, now(CLOCK_MONOTONIC), 0,
- ipv4ll_timer, ll);
+ r = sd_event_add_time(ll->event,
+ &ll->timer,
+ CLOCK_MONOTONIC,
+ now(CLOCK_MONOTONIC), 0,
+ ipv4ll_timer, ll);
if (r < 0)
goto out;
sd_ipv4ll_stop(ll);
sd_ipv4ll_detach_event(ll);
+ free(ll->random_data);
+ free(ll->random_data_state);
free(ll);
}