]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-ipv4acd: introduce new library split out from sd-ipv4ll
authorTom Gundersen <teg@jklm.no>
Fri, 21 Aug 2015 10:50:31 +0000 (12:50 +0200)
committerTom Gundersen <teg@jklm.no>
Fri, 18 Sep 2015 13:14:43 +0000 (15:14 +0200)
This splits the Address Conflict Detection out of the Link Local
library so that we can reuse it for DHCP and static addresses in
the future.

Implements RFC5227.

Makefile.am
src/libsystemd-network/sd-ipv4acd.c [new file with mode: 0644]
src/libsystemd-network/sd-ipv4ll.c
src/libsystemd-network/test-ipv4ll.c
src/network/networkd-ipv4ll.c
src/systemd/sd-ipv4acd.h [new file with mode: 0644]
src/systemd/sd-ipv4ll.h

index 3930647aa0bd0c0753c90b899bf49a13cdaef821..7b73ba24e9f2348b6c359b5297994298cbe2c1e2 100644 (file)
@@ -3198,6 +3198,7 @@ libsystemd_network_la_SOURCES = \
        src/systemd/sd-dhcp-server.h \
        src/systemd/sd-dhcp-lease.h \
        src/systemd/sd-ipv4ll.h \
+       src/systemd/sd-ipv4acd.h \
        src/systemd/sd-icmp6-nd.h \
        src/systemd/sd-dhcp6-client.h \
        src/systemd/sd-dhcp6-lease.h \
@@ -3214,6 +3215,7 @@ libsystemd_network_la_SOURCES = \
        src/libsystemd-network/dhcp-lease-internal.h \
        src/libsystemd-network/sd-dhcp-lease.c \
        src/libsystemd-network/sd-ipv4ll.c \
+       src/libsystemd-network/sd-ipv4acd.c \
        src/libsystemd-network/arp-util.h \
        src/libsystemd-network/arp-util.c \
        src/libsystemd-network/sd-pppoe.c \
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
new file mode 100644 (file)
index 0000000..ee5d13c
--- /dev/null
@@ -0,0 +1,529 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Axis Communications AB. All rights reserved.
+  Copyright (C) 2015 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "event-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "refcnt.h"
+#include "random-util.h"
+#include "siphash24.h"
+#include "util.h"
+
+#include "arp-util.h"
+#include "sd-ipv4acd.h"
+
+/* Constants from the RFC */
+#define PROBE_WAIT 1
+#define PROBE_NUM 3
+#define PROBE_MIN 1
+#define PROBE_MAX 2
+#define ANNOUNCE_WAIT 2
+#define ANNOUNCE_NUM 2
+#define ANNOUNCE_INTERVAL 2
+#define MAX_CONFLICTS 10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL 10
+
+#define IPV4ACD_NETWORK 0xA9FE0000L
+#define IPV4ACD_NETMASK 0xFFFF0000L
+
+#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug(ll, ...)   log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_ipv4acd_info(ll, ...)    log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_ipv4acd_notice(ll, ...)  log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_ipv4acd_error(ll, ...)   log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug_errno(ll, error, ...)   log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_ipv4acd_info_errno(ll, error, ...)    log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__)
+#define log_ipv4acd_notice_errno(ll, error, ...)  log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_ipv4acd_error_errno(ll, error, ...)   log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__)
+
+typedef enum IPv4ACDState {
+        IPV4ACD_STATE_INIT,
+        IPV4ACD_STATE_WAITING_PROBE,
+        IPV4ACD_STATE_PROBING,
+        IPV4ACD_STATE_WAITING_ANNOUNCE,
+        IPV4ACD_STATE_ANNOUNCING,
+        IPV4ACD_STATE_RUNNING,
+        _IPV4ACD_STATE_MAX,
+        _IPV4ACD_STATE_INVALID = -1
+} IPv4ACDState;
+
+struct sd_ipv4acd {
+        RefCount n_ref;
+
+        IPv4ACDState state;
+        int index;
+        int fd;
+        int iteration;
+        int conflict;
+        sd_event_source *receive_message;
+        sd_event_source *timer;
+        usec_t defend_window;
+        be32_t address;
+        /* External */
+        struct ether_addr mac_addr;
+        sd_event *event;
+        int event_priority;
+        sd_ipv4acd_cb_t cb;
+        void* userdata;
+};
+
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
+        if (ll)
+                assert_se(REFCNT_INC(ll->n_ref) >= 2);
+
+        return ll;
+}
+
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
+        if (!ll || REFCNT_DEC(ll->n_ref) > 0)
+                return NULL;
+
+        ll->receive_message = sd_event_source_unref(ll->receive_message);
+        ll->fd = safe_close(ll->fd);
+
+        ll->timer = sd_event_source_unref(ll->timer);
+
+        sd_ipv4acd_detach_event(ll);
+
+        free(ll);
+
+        return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4acd*, sd_ipv4acd_unref);
+#define _cleanup_ipv4acd_unref_ _cleanup_(sd_ipv4acd_unrefp)
+
+int sd_ipv4acd_new(sd_ipv4acd **ret) {
+        _cleanup_ipv4acd_unref_ sd_ipv4acd *ll = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        ll = new0(sd_ipv4acd, 1);
+        if (!ll)
+                return -ENOMEM;
+
+        ll->n_ref = REFCNT_INIT;
+        ll->state = IPV4ACD_STATE_INIT;
+        ll->index = -1;
+        ll->fd = -1;
+
+        *ret = ll;
+        ll = NULL;
+
+        return 0;
+}
+
+static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
+
+        assert(ll);
+        assert(st < _IPV4ACD_STATE_MAX);
+
+        if (st == ll->state && !reset_counter)
+                ll->iteration++;
+        else {
+                ll->state = st;
+                ll->iteration = 0;
+        }
+}
+
+static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
+        assert(ll);
+
+        if (ll->cb)
+                ll->cb(ll, event, ll->userdata);
+}
+
+static void ipv4acd_stop(sd_ipv4acd *ll) {
+        assert(ll);
+
+        ll->receive_message = sd_event_source_unref(ll->receive_message);
+        ll->fd = safe_close(ll->fd);
+
+        ll->timer = sd_event_source_unref(ll->timer);
+
+        log_ipv4acd_debug(ll, "STOPPED");
+
+        ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
+}
+
+int sd_ipv4acd_stop(sd_ipv4acd *ll) {
+        assert_return(ll, -EINVAL);
+
+        ipv4acd_stop(ll);
+
+        ipv4acd_client_notify(ll, IPV4ACD_EVENT_STOP);
+
+        return 0;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
+
+static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
+        _cleanup_event_source_unref_ sd_event_source *timer = NULL;
+        usec_t next_timeout;
+        usec_t time_now;
+        int r;
+
+        assert(sec >= 0);
+        assert(random_sec >= 0);
+        assert(ll);
+
+        next_timeout = sec * USEC_PER_SEC;
+
+        if (random_sec)
+                next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
+
+        assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+        r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
+                              time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(timer, ll->event_priority);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_description(timer, "ipv4acd-timer");
+        if (r < 0)
+                return r;
+
+        ll->timer = sd_event_source_unref(ll->timer);
+        ll->timer = timer;
+        timer = NULL;
+
+        return 0;
+}
+
+static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
+        assert(ll);
+        assert(arp);
+
+        /* see the BPF */
+        if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
+                return true;
+
+        /* the TPA matched instead of the SPA, this is not a conflict */
+        return false;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+        sd_ipv4acd *ll = userdata;
+        int r = 0;
+
+        assert(ll);
+
+        switch (ll->state) {
+        case IPV4ACD_STATE_INIT:
+
+                ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
+
+                if (ll->conflict >= MAX_CONFLICTS) {
+                        log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
+                        r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
+                        if (r < 0)
+                                goto out;
+
+                        ll->conflict = 0;
+                } else {
+                        r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
+                        if (r < 0)
+                                goto out;
+                }
+
+                break;
+        case IPV4ACD_STATE_WAITING_PROBE:
+        case IPV4ACD_STATE_PROBING:
+                /* Send a probe */
+                r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                if (r < 0) {
+                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
+                        goto out;
+                } else {
+                        _cleanup_free_ char *address = NULL;
+                        union in_addr_union addr = { .in.s_addr = ll->address };
+
+                        r = in_addr_to_string(AF_INET, &addr, &address);
+                        if (r >= 0)
+                                log_ipv4acd_debug(ll, "Probing %s", address);
+                }
+
+                if (ll->iteration < PROBE_NUM - 2) {
+                        ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
+
+                        r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
+                        if (r < 0)
+                                goto out;
+                } else {
+                        ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
+
+                        r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
+                        if (r < 0)
+                                goto out;
+                }
+
+                break;
+
+        case IPV4ACD_STATE_ANNOUNCING:
+                if (ll->iteration >= ANNOUNCE_NUM - 1) {
+                        ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
+
+                        break;
+                }
+        case IPV4ACD_STATE_WAITING_ANNOUNCE:
+                /* Send announcement packet */
+                r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                if (r < 0) {
+                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+                        goto out;
+                } else
+                        log_ipv4acd_debug(ll, "ANNOUNCE");
+
+                ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
+
+                r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
+                if (r < 0)
+                        goto out;
+
+                if (ll->iteration == 0) {
+                        ll->conflict = 0;
+                        ipv4acd_client_notify(ll, IPV4ACD_EVENT_BIND);
+                }
+
+                break;
+        default:
+                assert_not_reached("Invalid state.");
+        }
+
+out:
+        if (r < 0)
+                sd_ipv4acd_stop(ll);
+
+        return 1;
+}
+
+static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
+        _cleanup_free_ char *address = NULL;
+        union in_addr_union addr = { .in.s_addr = ll->address };
+        int r;
+
+        assert(ll);
+
+        ll->conflict++;
+
+        r = in_addr_to_string(AF_INET, &addr, &address);
+        if (r >= 0)
+                log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
+
+        ipv4acd_stop(ll);
+
+        ipv4acd_client_notify(ll, IPV4ACD_EVENT_CONFLICT);
+}
+
+static int ipv4acd_on_packet(sd_event_source *s, int fd,
+                            uint32_t revents, void *userdata) {
+        sd_ipv4acd *ll = userdata;
+        struct ether_arp packet;
+        int r;
+
+        assert(ll);
+        assert(fd >= 0);
+
+        r = read(fd, &packet, sizeof(struct ether_arp));
+        if (r < (int) sizeof(struct ether_arp))
+                goto out;
+
+        switch (ll->state) {
+        case IPV4ACD_STATE_ANNOUNCING:
+        case IPV4ACD_STATE_RUNNING:
+                if (ipv4acd_arp_conflict(ll, &packet)) {
+                        usec_t ts;
+
+                        assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+                        /* Defend address */
+                        if (ts > ll->defend_window) {
+                                ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
+                                r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                                if (r < 0) {
+                                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+                                        goto out;
+                                } else
+                                        log_ipv4acd_debug(ll, "DEFEND");
+
+                        } else
+                                ipv4acd_on_conflict(ll);
+                }
+
+                break;
+        case IPV4ACD_STATE_WAITING_PROBE:
+        case IPV4ACD_STATE_PROBING:
+        case IPV4ACD_STATE_WAITING_ANNOUNCE:
+                /* BPF ensures this packet indicates a conflict */
+                ipv4acd_on_conflict(ll);
+
+                break;
+        default:
+                assert_not_reached("Invalid state.");
+        }
+
+out:
+        if (r < 0)
+                sd_ipv4acd_stop(ll);
+
+        return 1;
+}
+
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
+        assert_return(ll, -EINVAL);
+        assert_return(interface_index > 0, -EINVAL);
+        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+        ll->index = interface_index;
+
+        return 0;
+}
+
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
+        assert_return(ll, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+        memcpy(&ll->mac_addr, addr, ETH_ALEN);
+
+        return 0;
+}
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
+        assert_return(ll, -EINVAL);
+
+        ll->event = sd_event_unref(ll->event);
+
+        return 0;
+}
+
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+        assert_return(!ll->event, -EBUSY);
+
+        if (event)
+                ll->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&ll->event);
+                if (r < 0)
+                        return r;
+        }
+
+        ll->event_priority = priority;
+
+        return 0;
+}
+
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
+        assert_return(ll, -EINVAL);
+
+        ll->cb = cb;
+        ll->userdata = userdata;
+
+        return 0;
+}
+
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
+        assert_return(ll, -EINVAL);
+        assert_return(address, -EINVAL);
+        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+        ll->address = address->s_addr;
+
+        return 0;
+}
+
+bool sd_ipv4acd_is_running(sd_ipv4acd *ll) {
+        assert_return(ll, false);
+
+        return ll->state != IPV4ACD_STATE_INIT;
+}
+
+static bool ether_addr_is_nul(const struct ether_addr *addr) {
+        const struct ether_addr nul_addr = {};
+
+        assert(addr);
+
+        return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
+}
+
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
+int sd_ipv4acd_start(sd_ipv4acd *ll) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+        assert_return(ll->event, -EINVAL);
+        assert_return(ll->index > 0, -EINVAL);
+        assert_return(ll->address != 0, -EINVAL);
+        assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
+        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+        ll->defend_window = 0;
+
+        r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
+        if (r < 0)
+                goto out;
+
+        ll->fd = safe_close(ll->fd);
+        ll->fd = r;
+
+        r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
+                            EPOLLIN, ipv4acd_on_packet, ll);
+        if (r < 0)
+                goto out;
+
+        r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
+        if (r < 0)
+                goto out;
+
+        r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");
+        if (r < 0)
+                goto out;
+
+        r = ipv4acd_set_next_wakeup(ll, 0, 0);
+        if (r < 0)
+                goto out;
+out:
+        if (r < 0) {
+                ipv4acd_stop(ll);
+                return r;
+        }
+
+        return 0;
+}
index 03a9b3f4f4b8fe03fe445266ac096105e368d1ca..f0230b919c431b0d43c810a5a75cf35c8cc4d26b 100644 (file)
 #include <stdio.h>
 #include <arpa/inet.h>
 
-#include "util.h"
-#include "siphash24.h"
+#include "event-util.h"
 #include "list.h"
 #include "random-util.h"
-#include "event-util.h"
+#include "refcnt.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "util.h"
 
-#include "arp-util.h"
+#include "sd-ipv4acd.h"
 #include "sd-ipv4ll.h"
 
-/* Constants from the RFC */
-#define PROBE_WAIT 1
-#define PROBE_NUM 3
-#define PROBE_MIN 1
-#define PROBE_MAX 2
-#define ANNOUNCE_WAIT 2
-#define ANNOUNCE_NUM 2
-#define ANNOUNCE_INTERVAL 2
-#define MAX_CONFLICTS 10
-#define RATE_LIMIT_INTERVAL 60
-#define DEFEND_INTERVAL 10
-
 #define IPV4LL_NETWORK 0xA9FE0000L
 #define IPV4LL_NETMASK 0xFFFF0000L
 
-#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__)
-
 #define IPV4LL_DONT_DESTROY(ll) \
         _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
 
-typedef enum IPv4LLState {
-        IPV4LL_STATE_INIT,
-        IPV4LL_STATE_WAITING_PROBE,
-        IPV4LL_STATE_PROBING,
-        IPV4LL_STATE_WAITING_ANNOUNCE,
-        IPV4LL_STATE_ANNOUNCING,
-        IPV4LL_STATE_RUNNING,
-        _IPV4LL_STATE_MAX,
-        _IPV4LL_STATE_INVALID = -1
-} IPv4LLState;
-
 struct sd_ipv4ll {
         unsigned n_ref;
 
-        IPv4LLState state;
-        int index;
-        int fd;
-        int iteration;
-        int conflict;
-        sd_event_source *receive_message;
-        sd_event_source *timer;
-        usec_t defend_window;
-        be32_t address;
+        sd_ipv4acd *acd;
+        be32_t address; /* the address pushed to ACD */
         struct random_data *random_data;
         char *random_data_state;
+
         /* External */
         be32_t claimed_address;
-        struct ether_addr mac_addr;
-        sd_event *event;
-        int event_priority;
         sd_ipv4ll_cb_t cb;
         void* userdata;
 };
@@ -107,12 +75,7 @@ sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
         if (ll->n_ref > 0)
                 return NULL;
 
-        ll->receive_message = sd_event_source_unref(ll->receive_message);
-        ll->fd = safe_close(ll->fd);
-
-        ll->timer = sd_event_source_unref(ll->timer);
-
-        sd_ipv4ll_detach_event(ll);
+        sd_ipv4acd_unref(ll->acd);
 
         free(ll->random_data);
         free(ll->random_data_state);
@@ -124,8 +87,11 @@ sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
 #define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
 
+static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
+
 int sd_ipv4ll_new(sd_ipv4ll **ret) {
         _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
+        int r;
 
         assert_return(ret, -EINVAL);
 
@@ -133,10 +99,15 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
         if (!ll)
                 return -ENOMEM;
 
+        r = sd_ipv4acd_new(&ll->acd);
+        if (r < 0)
+                return r;
+
+        r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
+        if (r < 0)
+                return r;
+
         ll->n_ref = 1;
-        ll->state = IPV4LL_STATE_INIT;
-        ll->index = -1;
-        ll->fd = -1;
 
         *ret = ll;
         ll = NULL;
@@ -144,347 +115,62 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
         return 0;
 }
 
-static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, bool reset_counter) {
-
-        assert(ll);
-        assert(st < _IPV4LL_STATE_MAX);
-
-        if (st == ll->state && !reset_counter) {
-                ll->iteration++;
-        } else {
-                ll->state = st;
-                ll->iteration = 0;
-        }
-}
-
-static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
-        assert(ll);
-
-        if (ll->cb)
-                ll->cb(ll, event, ll->userdata);
-}
-
-static void ipv4ll_stop(sd_ipv4ll *ll) {
-        IPV4LL_DONT_DESTROY(ll);
-
-        assert(ll);
-
-        ll->receive_message = sd_event_source_unref(ll->receive_message);
-        ll->fd = safe_close(ll->fd);
-
-        ll->timer = sd_event_source_unref(ll->timer);
-
-        log_ipv4ll(ll, "STOPPED");
-
-        ll->claimed_address = 0;
-        ipv4ll_set_state (ll, IPV4LL_STATE_INIT, true);
-}
-
 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
-        IPV4LL_DONT_DESTROY(ll);
-
-        assert_return(ll, -EINVAL);
-
-        ipv4ll_stop(ll);
-
-        ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP);
-
-        return 0;
-}
-
-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);
-
-        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);
-
-        *address = addr;
-        return 0;
-}
-
-static int ipv4ll_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
-
-static int ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
-        _cleanup_event_source_unref_ sd_event_source *timer = NULL;
-        usec_t next_timeout;
-        usec_t time_now;
         int r;
 
-        assert(sec >= 0);
-        assert(random_sec >= 0);
-        assert(ll);
-
-        next_timeout = sec * USEC_PER_SEC;
-
-        if (random_sec)
-                next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
-
-        assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
-
-        r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
-                              time_now + next_timeout, 0, ipv4ll_on_timeout, ll);
-        if (r < 0)
-                return r;
-
-        r = sd_event_source_set_priority(timer, ll->event_priority);
-        if (r < 0)
-                return r;
+        assert_return(ll, -EINVAL);
 
-        r = sd_event_source_set_description(timer, "ipv4ll-timer");
+        r = sd_ipv4acd_stop(ll->acd);
         if (r < 0)
                 return r;
 
-        ll->timer = sd_event_source_unref(ll->timer);
-        ll->timer = timer;
-        timer = NULL;
-
         return 0;
 }
 
-static bool ipv4ll_arp_conflict(sd_ipv4ll *ll, struct ether_arp *arp) {
-        assert(ll);
-        assert(arp);
-
-        /* see the BPF */
-        if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
-                return true;
+int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
+        assert_return(ll, -EINVAL);
 
-        /* the TPA matched instead of the SPA, this is not a conflict */
-        return false;
+        return sd_ipv4acd_set_index(ll->acd, interface_index);
 }
 
-static int ipv4ll_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_ipv4ll *ll = userdata;
-        int r = 0;
-
-        assert(ll);
-
-        switch (ll->state) {
-        case IPV4LL_STATE_INIT:
-
-                log_ipv4ll(ll, "PROBE");
-
-                ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, true);
-
-                if (ll->conflict >= MAX_CONFLICTS) {
-                        log_ipv4ll(ll, "MAX_CONFLICTS");
-                        r = ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
-                        if (r < 0)
-                                return r;
-                } else {
-                        r = ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
-                        if (r < 0)
-                                return r;
-                }
-
-                break;
-        case IPV4LL_STATE_WAITING_PROBE:
-        case IPV4LL_STATE_PROBING:
-                /* Send a probe */
-                r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
-                if (r < 0) {
-                        log_ipv4ll(ll, "Failed to send ARP probe.");
-                        goto out;
-                }
-
-                if (ll->iteration < PROBE_NUM - 2) {
-                        ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, false);
-
-                        r = ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
-                        if (r < 0)
-                                goto out;
-                } else {
-                        ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, true);
-
-                        r = ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
-                        if (r < 0)
-                                goto out;
-                }
-
-                break;
-
-        case IPV4LL_STATE_ANNOUNCING:
-                if (ll->iteration >= ANNOUNCE_NUM - 1) {
-                        ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, false);
-
-                        break;
-                }
-        case IPV4LL_STATE_WAITING_ANNOUNCE:
-                /* Send announcement packet */
-                r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
-                if (r < 0) {
-                        log_ipv4ll(ll, "Failed to send ARP announcement.");
-                        goto out;
-                }
-
-                ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, false);
-
-                r = ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
-                if (r < 0)
-                        goto out;
-
-                if (ll->iteration == 0) {
-                        log_ipv4ll(ll, "ANNOUNCE");
-                        ll->claimed_address = ll->address;
-                        ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
-                        ll->conflict = 0;
-                }
-
-                break;
-        default:
-                assert_not_reached("Invalid state.");
-        }
-
-out:
-        if (r < 0 && ll)
-                sd_ipv4ll_stop(ll);
-
-        return 1;
-}
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
 
-static int ipv4ll_on_conflict(sd_ipv4ll *ll) {
-        IPV4LL_DONT_DESTROY(ll);
+int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
         int r;
 
-        assert(ll);
-
-        log_ipv4ll(ll, "CONFLICT");
-
-        ll->conflict++;
-
-        ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
-
-        sd_ipv4ll_stop(ll);
-
-        /* Pick a new address */
-        r = ipv4ll_pick_address(ll, &ll->address);
-        if (r < 0)
-                return r;
-
-        r = sd_ipv4ll_start(ll);
-        if (r < 0)
-                return r;
+        assert_return(ll, -EINVAL);
 
-        return 0;
-}
+        if (!ll->random_data) {
+                uint8_t seed[8];
 
-static int ipv4ll_on_packet(sd_event_source *s, int fd,
-                            uint32_t revents, void *userdata) {
-        sd_ipv4ll *ll = userdata;
-        struct ether_arp packet;
-        int r;
+                /* If no random data is set, generate some from the MAC */
+                siphash24(seed, &addr->ether_addr_octet,
+                          ETH_ALEN, HASH_KEY.bytes);
 
-        assert(ll);
-        assert(fd >= 0);
-
-        r = read(fd, &packet, sizeof(struct ether_arp));
-        if (r < (int) sizeof(struct ether_arp))
-                goto out;
-
-        switch (ll->state) {
-        case IPV4LL_STATE_ANNOUNCING:
-        case IPV4LL_STATE_RUNNING:
-                if (ipv4ll_arp_conflict(ll, &packet)) {
-                        usec_t ts;
-
-                        assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
-                        /* Defend address */
-                        if (ts > ll->defend_window) {
-                                ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
-                                r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
-                                if (r < 0) {
-                                        log_ipv4ll(ll, "Failed to send ARP announcement.");
-                                        goto out;
-                                }
-
-                        } else {
-                                r = ipv4ll_on_conflict(ll);
-                                if (r < 0)
-                                        goto out;
-                        }
-                }
+                assert_cc(sizeof(unsigned) <= 8);
 
-                break;
-        case IPV4LL_STATE_WAITING_PROBE:
-        case IPV4LL_STATE_PROBING:
-        case IPV4LL_STATE_WAITING_ANNOUNCE:
-                /* BPF ensures this packet indicates a conflict */
-                r = ipv4ll_on_conflict(ll);
+                r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed);
                 if (r < 0)
-                        goto out;
-
-                break;
-        default:
-                assert_not_reached("Invalid state.");
+                        return r;
         }
 
-out:
-        if (r < 0 && ll)
-                sd_ipv4ll_stop(ll);
-
-        return 1;
-}
-
-int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
-        assert_return(ll, -EINVAL);
-        assert_return(interface_index > 0, -EINVAL);
-        assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);
-
-        ll->index = interface_index;
-
-        return 0;
-}
-
-int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
-        assert_return(ll, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);
-
-        if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
-                return 0;
-
-        memcpy(&ll->mac_addr, addr, ETH_ALEN);
-
-        return 0;
+        return sd_ipv4acd_set_mac(ll->acd, addr);
 }
 
 int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
         assert_return(ll, -EINVAL);
 
-        ll->event = sd_event_unref(ll->event);
-
-        return 0;
+        return sd_ipv4acd_detach_event(ll->acd);
 }
 
 int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
         int r;
 
         assert_return(ll, -EINVAL);
-        assert_return(!ll->event, -EBUSY);
-
-        if (event)
-                ll->event = sd_event_ref(event);
-        else {
-                r = sd_event_default(&ll->event);
-                if (r < 0)
-                        return r;
-        }
 
-        ll->event_priority = priority;
+        r = sd_ipv4acd_attach_event(ll->acd, event, priority);
+        if (r < 0)
+                return r;
 
         return 0;
 }
@@ -506,111 +192,147 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
                 return -ENOENT;
 
         address->s_addr = ll->claimed_address;
+
         return 0;
 }
 
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint8_t seed[8]) {
-        unsigned int entropy;
+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;
 
         assert_return(ll, -EINVAL);
         assert_return(seed, -EINVAL);
 
-        entropy = *seed;
+        random_data = new0(struct random_data, 1);
+        if (!random_data)
+                return -ENOMEM;
 
-        free(ll->random_data);
-        free(ll->random_data_state);
+        random_data_state = new0(char, 128);
+        if (!random_data_state)
+                return -ENOMEM;
 
-        ll->random_data = new0(struct random_data, 1);
-        ll->random_data_state = new0(char, 128);
+        r = initstate_r(seed, random_data_state, 128, random_data);
+        if (r < 0)
+                return r;
 
-        if (!ll->random_data || !ll->random_data_state) {
-                r = -ENOMEM;
-                goto error;
-        }
+        free(ll->random_data);
+        ll->random_data = random_data;
+        random_data = NULL;
 
-        r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
-        if (r < 0)
-                goto error;
+        free(ll->random_data_state);
+        ll->random_data_state = random_data_state;
+        random_data_state = NULL;
 
-error:
-        if (r < 0){
-                free(ll->random_data);
-                free(ll->random_data_state);
-                ll->random_data = NULL;
-                ll->random_data_state = NULL;
-        }
-        return r;
+        return 0;
 }
 
 bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
         assert_return(ll, false);
 
-        return ll->state != IPV4LL_STATE_INIT;
+        return sd_ipv4acd_is_running(ll->acd);
 }
 
-#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) {
+static int ipv4ll_pick_address(sd_ipv4ll *ll) {
+        struct in_addr in_addr;
+        be32_t addr;
         int r;
+        int32_t random;
 
-        assert_return(ll, -EINVAL);
-        assert_return(ll->event, -EINVAL);
-        assert_return(ll->index > 0, -EINVAL);
-        assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);
+        assert(ll);
+        assert(ll->random_data);
 
-        ll->state = IPV4LL_STATE_INIT;
+        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);
 
-        ll->conflict = 0;
-        ll->defend_window = 0;
-        ll->claimed_address = 0;
+        in_addr.s_addr = addr;
 
-        if (!ll->random_data) {
-                uint8_t seed[8];
+        r = sd_ipv4acd_set_address(ll->acd, &in_addr);
+        if (r < 0)
+                return r;
 
-                /* Fallback to mac */
-                siphash24(seed, &ll->mac_addr.ether_addr_octet,
-                          ETH_ALEN, HASH_KEY.bytes);
+        ll->address = addr;
 
-                r = sd_ipv4ll_set_address_seed(ll, seed);
-                if (r < 0)
-                        goto out;
-        }
+        return 0;
+}
+
+int sd_ipv4ll_start(sd_ipv4ll *ll) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+        assert_return(ll->random_data, -EINVAL);
 
         if (ll->address == 0) {
-                r = ipv4ll_pick_address(ll, &ll->address);
+                r = ipv4ll_pick_address(ll);
                 if (r < 0)
-                        goto out;
+                        return r;
         }
 
-        r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
+        r = sd_ipv4acd_start(ll->acd);
         if (r < 0)
-                goto out;
+                return r;
 
-        safe_close(ll->fd);
-        ll->fd = r;
+        return 0;
+}
 
-        ipv4ll_set_state(ll, IPV4LL_STATE_INIT, true);
+static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
+        assert(ll);
 
-        r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
-                            EPOLLIN, ipv4ll_on_packet, ll);
-        if (r < 0)
-                goto out;
+        if (ll->cb)
+                ll->cb(ll, event, ll->userdata);
+}
 
-        r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
-        if (r < 0)
-                goto out;
+void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
+        sd_ipv4ll *ll = userdata;
+        IPV4LL_DONT_DESTROY(ll);
+        int r;
 
-        r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message");
-        if (r < 0)
-                goto out;
+        assert(acd);
+        assert(ll);
 
-        r = ipv4ll_set_next_wakeup(ll, 0, 0);
-        if (r < 0)
-                goto out;
-out:
-        if (r < 0)
-                ipv4ll_stop(ll);
+        switch (event) {
+        case IPV4ACD_EVENT_STOP:
+                ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP);
 
-        return 0;
+                ll->claimed_address = 0;
+
+                break;
+        case IPV4ACD_EVENT_BIND:
+                ll->claimed_address = ll->address;
+                ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
+
+                break;
+        case IPV4ACD_EVENT_CONFLICT:
+                /* if an address was already bound we must call up to the
+                   user to handle this, otherwise we just try again */
+                if (ll->claimed_address != 0) {
+                        ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
+
+                        ll->claimed_address = 0;
+                } else {
+                        r = ipv4ll_pick_address(ll);
+                        if (r < 0)
+                                goto error;
+
+                        r = sd_ipv4acd_start(ll->acd);
+                        if (r < 0)
+                                goto error;
+                }
+
+                break;
+        default:
+                assert_not_reached("Invalid IPv4ACD event.");
+        }
+
+        return;
+
+error:
+        ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP);
 }
index 55ec2f3972208281db89bc52ab0b0cdcef26eff1..f0e26bda07278138a777687e97a4847a5d25834b 100644 (file)
@@ -118,9 +118,8 @@ static void test_public_api_setters(sd_event *e) {
         assert_se(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL);
         assert_se(sd_ipv4ll_set_callback(ll, NULL, NULL) == 0);
 
-        assert_se(sd_ipv4ll_set_address_seed(NULL, NULL) == -EINVAL);
-        assert_se(sd_ipv4ll_set_address_seed(ll, NULL) == -EINVAL);
-        assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0);
+        assert_se(sd_ipv4ll_set_address_seed(NULL, *(unsigned *) seed) == -EINVAL);
+        assert_se(sd_ipv4ll_set_address_seed(ll, *(unsigned *) seed) == 0);
 
         assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL);
         assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL);
@@ -168,6 +167,8 @@ static void test_basic_request(sd_event *e) {
         sd_event_run(e, (uint64_t) -1);
         assert_se(sd_ipv4ll_start(ll) == -EBUSY);
 
+        assert_se(sd_ipv4ll_is_running(ll));
+
         /* PROBE */
         sd_event_run(e, (uint64_t) -1);
         assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp));
@@ -196,6 +197,10 @@ static void test_basic_request(sd_event *e) {
 int main(int argc, char *argv[]) {
         _cleanup_event_unref_ sd_event *e = NULL;
 
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
         assert_se(sd_event_new(&e) >= 0);
 
         test_public_api_setters(e);
index 43aaa749ff635552b427abaf289caf4f7b9ced13..1c34f55b4b68d411227fdd06b4220a922a0d50d5 100644 (file)
@@ -215,7 +215,9 @@ int ipv4ll_configure(Link *link) {
         if (link->udev_device) {
                 r = net_get_unique_predictable_data(link->udev_device, seed);
                 if (r >= 0) {
-                        r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
+                        assert_cc(sizeof(unsigned) <= 8);
+
+                        r = sd_ipv4ll_set_address_seed(link->ipv4ll, *(unsigned *)seed);
                         if (r < 0)
                                 return r;
                 }
diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h
new file mode 100644 (file)
index 0000000..8844ae8
--- /dev/null
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdipv4acdfoo
+#define foosdipv4acdfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Axis Communications AB. All rights reserved.
+  Copyright (C) 2015 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+enum {
+        IPV4ACD_EVENT_STOP           = 0,
+        IPV4ACD_EVENT_BIND           = 1,
+        IPV4ACD_EVENT_CONFLICT       = 2,
+};
+
+typedef struct sd_ipv4acd sd_ipv4acd;
+typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata);
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority);
+int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata);
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
+bool sd_ipv4acd_is_running(sd_ipv4acd *ll);
+int sd_ipv4acd_start(sd_ipv4acd *ll);
+int sd_ipv4acd_stop(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll);
+int sd_ipv4acd_new (sd_ipv4acd **ret);
+
+#endif
index d01715815489d54d138b8dcbade690683b93af29..9581e99d31fa034c23dcb62e4e09ef0e9b11bbe9 100644 (file)
@@ -43,7 +43,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_set_address_seed(sd_ipv4ll *ll, unsigned seed);
 bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
 int sd_ipv4ll_start(sd_ipv4ll *ll);
 int sd_ipv4ll_stop(sd_ipv4ll *ll);