diff options
Diffstat (limited to 'src/libsystemd-network/sd-ipv4ll.c')
-rw-r--r-- | src/libsystemd-network/sd-ipv4ll.c | 180 |
1 files changed, 81 insertions, 99 deletions
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 2a06418c53..5603a533a5 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -28,16 +28,17 @@ #include "sd-ipv4ll.h" #include "alloc-util.h" +#include "ether-addr-util.h" #include "in-addr-util.h" #include "list.h" #include "random-util.h" -#include "refcnt.h" #include "siphash24.h" #include "sparse-endian.h" +#include "string-util.h" #include "util.h" -#define IPV4LL_NETWORK 0xA9FE0000L -#define IPV4LL_NETMASK 0xFFFF0000L +#define IPV4LL_NETWORK UINT32_C(0xA9FE0000) +#define IPV4LL_NETMASK UINT32_C(0xFFFF0000) #define IPV4LL_DONT_DESTROY(ll) \ _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll) @@ -46,16 +47,28 @@ struct sd_ipv4ll { unsigned n_ref; sd_ipv4acd *acd; + be32_t address; /* the address pushed to ACD */ - struct random_data *random_data; - char *random_data_state; + struct ether_addr mac; + + struct { + le64_t value; + le64_t generation; + } seed; + bool seed_set; /* External */ be32_t claimed_address; - sd_ipv4ll_callback_t cb; + + sd_ipv4ll_callback_t callback; void* userdata; }; +#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__) +#define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__) + +static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); + sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { if (!ll) return NULL; @@ -77,16 +90,11 @@ sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { return NULL; sd_ipv4acd_unref(ll->acd); - - free(ll->random_data); - free(ll->random_data_state); free(ll); return NULL; } -static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); - int sd_ipv4ll_new(sd_ipv4ll **ret) { _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL; int r; @@ -114,44 +122,32 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) { } int sd_ipv4ll_stop(sd_ipv4ll *ll) { - int r; - assert_return(ll, -EINVAL); - r = sd_ipv4acd_stop(ll->acd); - if (r < 0) - return r; - - return 0; + return sd_ipv4acd_stop(ll->acd); } -int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) { +int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) { assert_return(ll, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - return sd_ipv4acd_set_index(ll->acd, interface_index); + return sd_ipv4acd_set_ifindex(ll->acd, ifindex); } -#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_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { int r; assert_return(ll, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - if (!ll->random_data) { - uint64_t seed; - - /* If no random data is set, generate some from the MAC */ - seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes); - - assert_cc(sizeof(unsigned) <= 8); - - r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed)); - if (r < 0) - return r; - } + r = sd_ipv4acd_set_mac(ll->acd, addr); + if (r < 0) + return r; - return sd_ipv4acd_set_mac(ll->acd, addr); + ll->mac = *addr; + return 0; } int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { @@ -161,21 +157,15 @@ int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { } int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) { - int r; - assert_return(ll, -EINVAL); - r = sd_ipv4acd_attach_event(ll->acd, event, priority); - if (r < 0) - return r; - - return 0; + return sd_ipv4acd_attach_event(ll->acd, event, priority); } int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) { assert_return(ll, -EINVAL); - ll->cb = cb; + ll->callback = cb; ll->userdata = userdata; return 0; @@ -193,32 +183,12 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) { return 0; } -int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) { - _cleanup_free_ struct random_data *random_data = NULL; - _cleanup_free_ char *random_data_state = NULL; - int r; - +int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) { assert_return(ll, -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - random_data = new0(struct random_data, 1); - if (!random_data) - return -ENOMEM; - - random_data_state = new0(char, 128); - if (!random_data_state) - return -ENOMEM; - - r = initstate_r(seed, random_data_state, 128, random_data); - if (r < 0) - return r; - - free(ll->random_data); - ll->random_data = random_data; - random_data = NULL; - - free(ll->random_data_state); - ll->random_data_state = random_data_state; - random_data_state = NULL; + ll->seed.value = htole64(seed); + ll->seed_set = true; return 0; } @@ -230,20 +200,12 @@ int sd_ipv4ll_is_running(sd_ipv4ll *ll) { } static bool ipv4ll_address_is_valid(const struct in_addr *address) { - uint32_t addr; - assert(address); if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address)) return false; - addr = be32toh(address->s_addr); - - if ((addr & 0x0000FF00) == 0x0000 || - (addr & 0x0000FF00) == 0xFF00) - return false; - - return true; + return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U); } int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) { @@ -262,48 +224,67 @@ int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) { return 0; } +#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b) + static int ipv4ll_pick_address(sd_ipv4ll *ll) { - struct in_addr in_addr; + _cleanup_free_ char *address = NULL; be32_t addr; - int r; - int32_t random; assert(ll); - assert(ll->random_data); 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) & 0x0000FF00) == 0x0000 || - (ntohl(addr) & 0x0000FF00) == 0xFF00); + uint64_t h; - in_addr.s_addr = addr; + h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes); - r = sd_ipv4ll_set_address(ll, &in_addr); - if (r < 0) - return r; + /* Increase the generation counter by one */ + ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1); - return 0; + addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK); + } while (addr == ll->address || + IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U)); + + (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address); + log_ipv4ll(ll, "Picked new IP address %s.", strna(address)); + + return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr }); } +#define MAC_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; + bool picked_address = false; assert_return(ll, -EINVAL); - assert_return(ll->random_data, -EINVAL); + assert_return(!ether_addr_is_null(&ll->mac), -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + /* If no random seed is set, generate some from the MAC address */ + if (!ll->seed_set) + ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes)); + + /* Restart the generation counter. */ + ll->seed.generation = 0; if (ll->address == 0) { r = ipv4ll_pick_address(ll); if (r < 0) return r; + + picked_address = true; } r = sd_ipv4acd_start(ll->acd); - if (r < 0) + if (r < 0) { + + /* We couldn't start? If so, let's forget the picked address again, the user might make a change and + * retry, and we want the new data to take effect when picking an address. */ + if (picked_address) + ll->address = 0; + return r; + } return 0; } @@ -311,8 +292,8 @@ int sd_ipv4ll_start(sd_ipv4ll *ll) { static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) { assert(ll); - if (ll->cb) - ll->cb(ll, event, ll->userdata); + if (ll->callback) + ll->callback(ll, event, ll->userdata); } void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { @@ -324,17 +305,17 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { assert(ll); switch (event) { + case SD_IPV4ACD_EVENT_STOP: ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); - ll->claimed_address = 0; - break; + case SD_IPV4ACD_EVENT_BIND: ll->claimed_address = ll->address; ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND); - break; + case SD_IPV4ACD_EVENT_CONFLICT: /* if an address was already bound we must call up to the user to handle this, otherwise we just try again */ @@ -353,6 +334,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { } break; + default: assert_not_reached("Invalid IPv4ACD event."); } |