e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen This file is part of systemd.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Copyright (C) 2014 Axis Communications AB. All rights reserved.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Copyright (C) 2015 Tom 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 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 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/* Constants from the RFC */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
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#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 /* External */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->receive_message = sd_event_source_unref(ll->receive_message);
4afd3348c7506dd1d36305b7bcb9feb8952b9d6bLennart Poettering _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *ll = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ll->receive_message = sd_event_source_unref(ll->receive_message);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_STOP);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
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 next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
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 r = sd_event_source_set_priority(timer, ll->event_priority);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_source_set_description(timer, "ipv4acd-timer");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* see the BPF */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* the TPA matched instead of the SPA, this is not a conflict */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return false;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
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 r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Send a probe */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen union in_addr_union addr = { .in.s_addr = ll->address };
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = in_addr_to_string(AF_INET, &addr, &address);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Send announcement packet */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void ipv4acd_on_conflict(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen union in_addr_union addr = { .in.s_addr = ll->address };
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = in_addr_to_string(AF_INET, &addr, &address);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int ipv4acd_on_packet(sd_event_source *s, int fd,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = read(fd, &packet, sizeof(struct ether_arp));
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < (int) sizeof(struct ether_arp))
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Defend address */
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 log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* BPF ensures this packet indicates a conflict */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenint sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
04c0136989b7eb896bfb0fb176e11233d69e1453Lennart Poetteringint sd_ipv4acd_is_running(sd_ipv4acd *ll) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic bool ether_addr_is_nul(const struct ether_addr *addr) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
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 assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");