e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen/***
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen This file is part of systemd.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Copyright (C) 2014 Axis Communications AB. All rights reserved.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Copyright (C) 2015 Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen systemd is free software; you can redistribute it and/or modify it
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen under the terms of the GNU Lesser General Public License as published by
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen (at your option) any later version.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen systemd is distributed in the hope that it will be useful, but
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Lesser General Public License for more details.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen You should have received a copy of the GNU Lesser General Public License
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen***/
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include <arpa/inet.h>
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include <errno.h>
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include <stdio.h>
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include <stdlib.h>
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include <string.h>
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "sd-ipv4acd.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include "alloc-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "arp-util.h"
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include "fd-util.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "in-addr-util.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "list.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "random-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "refcnt.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "siphash24.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "util.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen/* Constants from the RFC */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define PROBE_WAIT 1
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define PROBE_NUM 3
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define PROBE_MIN 1
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define PROBE_MAX 2
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define ANNOUNCE_WAIT 2
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define ANNOUNCE_NUM 2
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define ANNOUNCE_INTERVAL 2
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define MAX_CONFLICTS 10
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define RATE_LIMIT_INTERVAL 60
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define DEFEND_INTERVAL 10
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define IPV4ACD_NETWORK 0xA9FE0000L
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define IPV4ACD_NETMASK 0xFFFF0000L
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_debug(ll, ...) log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_info(ll, ...) log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_notice(ll, ...) log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_error(ll, ...) log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_debug_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_info_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_notice_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_error_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersentypedef enum IPv4ACDState {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen IPV4ACD_STATE_INIT,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen IPV4ACD_STATE_WAITING_PROBE,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen IPV4ACD_STATE_PROBING,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen IPV4ACD_STATE_WAITING_ANNOUNCE,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen IPV4ACD_STATE_ANNOUNCING,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen IPV4ACD_STATE_RUNNING,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen _IPV4ACD_STATE_MAX,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen _IPV4ACD_STATE_INVALID = -1
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen} IPv4ACDState;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstruct sd_ipv4acd {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen RefCount n_ref;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen IPv4ACDState state;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int index;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int fd;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int iteration;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int conflict;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_event_source *receive_message;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_event_source *timer;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen usec_t defend_window;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen be32_t address;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* External */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen struct ether_addr mac_addr;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_event *event;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int event_priority;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_ipv4acd_cb_t cb;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen void* userdata;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen};
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersensd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (ll)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(REFCNT_INC(ll->n_ref) >= 2);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return ll;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersensd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!ll || REFCNT_DEC(ll->n_ref) > 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->receive_message = sd_event_source_unref(ll->receive_message);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->fd = safe_close(ll->fd);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->timer = sd_event_source_unref(ll->timer);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_ipv4acd_detach_event(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen free(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_new(sd_ipv4acd **ret) {
4afd3348c7506dd1d36305b7bcb9feb8952b9d6bLennart Poettering _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *ll = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ret, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll = new0(sd_ipv4acd, 1);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!ll)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return -ENOMEM;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->n_ref = REFCNT_INIT;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->state = IPV4ACD_STATE_INIT;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->index = -1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->fd = -1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen *ret = ll;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(st < _IPV4ACD_STATE_MAX);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (st == ll->state && !reset_counter)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->iteration++;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen else {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->state = st;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->iteration = 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (ll->cb)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->cb(ll, event, ll->userdata);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void ipv4acd_stop(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->receive_message = sd_event_source_unref(ll->receive_message);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->fd = safe_close(ll->fd);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->timer = sd_event_source_unref(ll->timer);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_debug(ll, "STOPPED");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_stop(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_stop(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_STOP);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
4afd3348c7506dd1d36305b7bcb9feb8952b9d6bLennart Poettering _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen usec_t next_timeout;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen usec_t time_now;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(sec >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(random_sec >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen next_timeout = sec * USEC_PER_SEC;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (random_sec)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_source_set_priority(timer, ll->event_priority);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_source_set_description(timer, "ipv4acd-timer");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->timer = sd_event_source_unref(ll->timer);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->timer = timer;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen timer = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(arp);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* see the BPF */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return true;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* the TPA matched instead of the SPA, this is not a conflict */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return false;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_ipv4acd *ll = userdata;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int r = 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen switch (ll->state) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_INIT:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (ll->conflict >= MAX_CONFLICTS) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->conflict = 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen } else {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen break;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_WAITING_PROBE:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_PROBING:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Send a probe */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen } else {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen _cleanup_free_ char *address = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen union in_addr_union addr = { .in.s_addr = ll->address };
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = in_addr_to_string(AF_INET, &addr, &address);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r >= 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_debug(ll, "Probing %s", address);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (ll->iteration < PROBE_NUM - 2) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen } else {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen break;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_ANNOUNCING:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (ll->iteration >= ANNOUNCE_NUM - 1) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen break;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_WAITING_ANNOUNCE:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Send announcement packet */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen } else
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_debug(ll, "ANNOUNCE");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (ll->iteration == 0) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->conflict = 0;
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen break;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen default:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_not_reached("Invalid state.");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenout:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_ipv4acd_stop(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void ipv4acd_on_conflict(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen _cleanup_free_ char *address = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen union in_addr_union addr = { .in.s_addr = ll->address };
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->conflict++;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = in_addr_to_string(AF_INET, &addr, &address);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r >= 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_stop(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int ipv4acd_on_packet(sd_event_source *s, int fd,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen uint32_t revents, void *userdata) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_ipv4acd *ll = userdata;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen struct ether_arp packet;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(fd >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = read(fd, &packet, sizeof(struct ether_arp));
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < (int) sizeof(struct ether_arp))
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen switch (ll->state) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_ANNOUNCING:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_RUNNING:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (ipv4acd_arp_conflict(ll, &packet)) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen usec_t ts;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Defend address */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (ts > ll->defend_window) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen } else
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_debug(ll, "DEFEND");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen } else
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_on_conflict(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen break;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_WAITING_PROBE:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_PROBING:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen case IPV4ACD_STATE_WAITING_ANNOUNCE:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* BPF ensures this packet indicates a conflict */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_on_conflict(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen break;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen default:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_not_reached("Invalid state.");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenout:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen sd_ipv4acd_stop(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(interface_index > 0, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->index = interface_index;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(addr, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen memcpy(&ll->mac_addr, addr, ETH_ALEN);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->event = sd_event_unref(ll->event);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(!ll->event, -EBUSY);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (event)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->event = sd_event_ref(event);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen else {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_default(&ll->event);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->event_priority = priority;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->cb = cb;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->userdata = userdata;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(address, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->address = address->s_addr;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
04c0136989b7eb896bfb0fb176e11233d69e1453Lennart Poetteringint sd_ipv4acd_is_running(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, false);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return ll->state != IPV4ACD_STATE_INIT;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic bool ether_addr_is_nul(const struct ether_addr *addr) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen const struct ether_addr nul_addr = {};
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(addr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_start(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->event, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->index > 0, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->address != 0, -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->defend_window = 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->fd = safe_close(ll->fd);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->fd = r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen EPOLLIN, ipv4acd_on_packet, ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, 0, 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen goto out;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenout:
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_stop(ll);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}