diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index 3925810768..e660ac2c6f 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -37,6 +37,16 @@ int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) return memcmp(a->bytes, b->bytes, a->length); } +static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) { + assert(p); + assert(state); + + siphash24_compress(&p->length, sizeof(p->length), state); + siphash24_compress(p->bytes, p->length, state); +} + +DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare); + char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { assert(addr); assert(buffer); diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h index cf8afff356..63447910c0 100644 --- a/src/basic/ether-addr-util.h +++ b/src/basic/ether-addr-util.h @@ -36,6 +36,8 @@ static inline bool hw_addr_is_null(const struct hw_addr_data *addr) { return hw_addr_equal(addr, &HW_ADDR_NULL); } +extern const struct hash_ops hw_addr_hash_ops; + #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index 327fb2ff3e..d8d94ab5cb 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -8,11 +8,13 @@ #include #include "arp-util.h" +#include "ether-addr-util.h" #include "fd-util.h" +#include "in-addr-util.h" #include "unaligned.h" #include "util.h" -int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { +int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) { struct sock_filter filter[] = { BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ @@ -46,13 +48,13 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ @@ -61,15 +63,25 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ }; struct sock_fprog fprog = { - .len = ELEMENTSOF(filter), - .filter = (struct sock_filter*) filter + .len = ELEMENTSOF(filter), + .filter = (struct sock_filter*) filter, }; + + assert(fd >= 0); + + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) + return -errno; + + return 0; +} + +int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) { union sockaddr_union link = { - .ll.sll_family = AF_PACKET, + .ll.sll_family = AF_PACKET, .ll.sll_protocol = htobe16(ETH_P_ARP), - .ll.sll_ifindex = ifindex, - .ll.sll_halen = ETH_ALEN, - .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, }; _cleanup_close_ int s = -1; int r; @@ -80,59 +92,57 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ if (s < 0) return -errno; - r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); + r = arp_update_filter(s, a, eth_mac); if (r < 0) - return -errno; + return r; - r = bind(s, &link.sa, sizeof(link.ll)); - if (r < 0) + if (bind(s, &link.sa, sizeof(link.ll)) < 0) return -errno; return TAKE_FD(s); } -static int arp_send_packet(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha, - bool announce) { +int arp_send_packet( + int fd, + int ifindex, + const struct in_addr *pa, + const struct ether_addr *ha, + bool announce) { + union sockaddr_union link = { - .ll.sll_family = AF_PACKET, + .ll.sll_family = AF_PACKET, .ll.sll_protocol = htobe16(ETH_P_ARP), - .ll.sll_ifindex = ifindex, - .ll.sll_halen = ETH_ALEN, - .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, }; struct ether_arp arp = { - .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */ - .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */ - .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ - .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */ - .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ + .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */ + .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */ + .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ + .ea_hdr.ar_pln = sizeof(struct in_addr), /* PLEN */ + .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ }; - int r; + ssize_t n; assert(fd >= 0); - assert(pa != 0); + assert(ifindex > 0); + assert(pa); + assert(in4_addr_is_set(pa)); assert(ha); + assert(!ether_addr_is_null(ha)); memcpy(&arp.arp_sha, ha, ETH_ALEN); - memcpy(&arp.arp_tpa, &pa, sizeof(pa)); + memcpy(&arp.arp_tpa, pa, sizeof(struct in_addr)); if (announce) - memcpy(&arp.arp_spa, &pa, sizeof(pa)); + memcpy(&arp.arp_spa, pa, sizeof(struct in_addr)); - r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); - if (r < 0) + n = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); + if (n < 0) return -errno; + if (n != sizeof(struct ether_arp)) + return -EIO; return 0; } - -int arp_send_probe(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha) { - return arp_send_packet(fd, ifindex, pa, ha, false); -} - -int arp_send_announcement(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha) { - return arp_send_packet(fd, ifindex, pa, ha, true); -} diff --git a/src/libsystemd-network/arp-util.h b/src/libsystemd-network/arp-util.h index 2dac8cfbaa..e8615a158d 100644 --- a/src/libsystemd-network/arp-util.h +++ b/src/libsystemd-network/arp-util.h @@ -6,13 +6,31 @@ ***/ #include +#include #include "socket-util.h" #include "sparse-endian.h" -int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac); +int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac); +int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac); -int arp_send_probe(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha); -int arp_send_announcement(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha); +int arp_send_packet( + int fd, + int ifindex, + const struct in_addr *pa, + const struct ether_addr *ha, + bool announce); +static inline int arp_send_probe( + int fd, + int ifindex, + const struct in_addr *pa, + const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, false); +} +static inline int arp_send_announcement( + int fd, + int ifindex, + const struct in_addr *pa, + const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, true); +} diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index b4af60741a..9a77a33317 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -18,6 +18,7 @@ #include "fd-util.h" #include "in-addr-util.h" #include "log-link.h" +#include "memory-util.h" #include "network-common.h" #include "random-util.h" #include "siphash24.h" @@ -64,7 +65,7 @@ struct sd_ipv4acd { sd_event_source *timer_event_source; usec_t defend_window; - be32_t address; + struct in_addr address; /* External */ struct ether_addr mac_addr; @@ -72,7 +73,9 @@ struct sd_ipv4acd { sd_event *event; int event_priority; sd_ipv4acd_callback_t callback; - void* userdata; + void *userdata; + sd_ipv4acd_check_mac_callback_t check_mac_callback; + void *check_mac_userdata; }; #define log_ipv4acd_errno(acd, error, fmt, ...) \ @@ -208,18 +211,6 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u acd->event_priority, "ipv4acd-timer", true); } -static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) { - assert(acd); - assert(arp); - - /* see the BPF */ - if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->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 *acd = userdata; int r = 0; @@ -229,38 +220,34 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) switch (acd->state) { case IPV4ACD_STATE_STARTED: + acd->defend_window = 0; + ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true); if (acd->n_conflict >= MAX_CONFLICTS) { char ts[FORMAT_TIMESPAN_MAX]; - log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0)); + log_ipv4acd(acd, "Max conflicts reached, delaying by %s", + format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0)); r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC); - if (r < 0) - goto fail; - } else { + } else r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC); - if (r < 0) - goto fail; - } + if (r < 0) + goto fail; break; case IPV4ACD_STATE_WAITING_PROBE: case IPV4ACD_STATE_PROBING: /* Send a probe */ - r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); + r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr); if (r < 0) { log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m"); goto fail; - } else { - _cleanup_free_ char *address = NULL; - union in_addr_union addr = { .in.s_addr = acd->address }; - - (void) in_addr_to_string(AF_INET, &addr, &address); - log_ipv4acd(acd, "Probing %s", strna(address)); } + log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address)); + if (acd->n_iteration < PROBE_NUM - 2) { ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false); @@ -286,12 +273,13 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) _fallthrough_; case IPV4ACD_STATE_WAITING_ANNOUNCE: /* Send announcement packet */ - r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); + r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr); if (r < 0) { log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); goto fail; - } else - log_ipv4acd(acd, "ANNOUNCE"); + } + + log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address)); ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false); @@ -317,16 +305,45 @@ fail: return 0; } -static void ipv4acd_on_conflict(sd_ipv4acd *acd) { - _cleanup_free_ char *address = NULL; - union in_addr_union addr = { .in.s_addr = acd->address }; +static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) { + assert(acd); + assert(arp); + /* RFC 5227 section 2.1.1. + * "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is + * being performed, where the packet's 'sender IP address' is the address being probed for, + * then the host MUST treat this address as being in use by some other host" */ + if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0) + return true; + + if (announced) + /* the TPA matched instead of SPA, this is not a conflict */ + return false; + + /* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and + * the packet's 'sender hardware address' is not the hardware address of any of the host's + * interfaces, then the host SHOULD similarly treat this as an address conflict" */ + if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST)) + return false; /* not ARP Request, ignoring. */ + if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0) + return false; /* not ARP Probe, ignoring. */ + if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0) + return false; /* target IP address does not match, BPF code is broken? */ + + if (acd->check_mac_callback && + acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0) + /* sender hardware is one of the host's interfaces, ignoring. */ + return true; + + return true; /* conflict! */ +} + +static void ipv4acd_on_conflict(sd_ipv4acd *acd) { assert(acd); acd->n_conflict++; - (void) in_addr_to_string(AF_INET, &addr, &address); - log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict); + log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict); ipv4acd_reset(acd); ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT); @@ -365,7 +382,7 @@ static int ipv4acd_on_packet( case IPV4ACD_STATE_ANNOUNCING: case IPV4ACD_STATE_RUNNING: - if (ipv4acd_arp_conflict(acd, &packet)) { + if (ipv4acd_arp_conflict(acd, &packet, true)) { usec_t ts; assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0); @@ -373,12 +390,13 @@ static int ipv4acd_on_packet( /* Defend address */ if (ts > acd->defend_window) { acd->defend_window = ts + DEFEND_INTERVAL_USEC; - r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); + r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr); if (r < 0) { log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); goto fail; - } else - log_ipv4acd(acd, "DEFEND"); + } + + log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address)); } else ipv4acd_on_conflict(acd); @@ -388,8 +406,8 @@ static int ipv4acd_on_packet( case IPV4ACD_STATE_WAITING_PROBE: case IPV4ACD_STATE_PROBING: case IPV4ACD_STATE_WAITING_ANNOUNCE: - /* BPF ensures this packet indicates a conflict */ - ipv4acd_on_conflict(acd); + if (ipv4acd_arp_conflict(acd, &packet, false)) + ipv4acd_on_conflict(acd); break; default: @@ -438,12 +456,24 @@ const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) { } int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) { + int r; + assert_return(acd, -EINVAL); assert_return(addr, -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); + assert_return(!ether_addr_is_null(addr), -EINVAL); acd->mac_addr = *addr; + if (!sd_ipv4acd_is_running(acd)) + return 0; + + assert(acd->fd >= 0); + r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr); + if (r < 0) { + ipv4acd_reset(acd); + return r; + } + return 0; } @@ -483,21 +513,51 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *use return 0; } +int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) { + assert_return(acd, -EINVAL); + + acd->check_mac_callback = cb; + acd->check_mac_userdata = userdata; + return 0; +} + int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) { + int r; + assert_return(acd, -EINVAL); assert_return(address, -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); + assert_return(in4_addr_is_set(address), -EINVAL); - acd->address = address->s_addr; + if (in4_addr_equal(&acd->address, address)) + return 0; + acd->address = *address; + + if (!sd_ipv4acd_is_running(acd)) + return 0; + + assert(acd->fd >= 0); + r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr); + if (r < 0) + goto fail; + + r = ipv4acd_set_next_wakeup(acd, 0, 0); + if (r < 0) + goto fail; + + ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true); return 0; + +fail: + ipv4acd_reset(acd); + return r; } int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) { assert_return(acd, -EINVAL); assert_return(address, -EINVAL); - address->s_addr = acd->address; + *address = acd->address; return 0; } @@ -514,16 +574,15 @@ int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) { assert_return(acd, -EINVAL); assert_return(acd->event, -EINVAL); assert_return(acd->ifindex > 0, -EINVAL); - assert_return(acd->address != 0, -EINVAL); + assert_return(in4_addr_is_set(&acd->address), -EINVAL); assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL); assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); - r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr); + r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr); if (r < 0) return r; CLOSE_AND_REPLACE(acd->fd, r); - acd->defend_window = 0; if (reset_conflicts) acd->n_conflict = 0; diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 8053afee93..7b9d63ca3c 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -46,7 +46,10 @@ struct sd_ipv4ll { be32_t claimed_address; sd_ipv4ll_callback_t callback; - void* userdata; + void *userdata; + + sd_ipv4ll_check_mac_callback_t check_mac_callback; + void *check_mac_userdata; }; #define log_ipv4ll_errno(ll, error, fmt, ...) \ @@ -60,7 +63,8 @@ struct sd_ipv4ll { sd_ipv4ll_get_ifname(ll), \ 0, fmt, ##__VA_ARGS__) -static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); +static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata); +static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata); static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) { assert(ll); @@ -91,6 +95,10 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) { if (r < 0) return r; + r = sd_ipv4acd_set_check_mac_callback(ll->acd, ipv4ll_check_mac, ll); + if (r < 0) + return r; + *ret = TAKE_PTR(ll); return 0; @@ -137,7 +145,7 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { assert_return(ll, -EINVAL); assert_return(addr, -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + assert_return(!ether_addr_is_null(addr), -EINVAL); r = sd_ipv4acd_set_mac(ll->acd, addr); if (r < 0) @@ -168,6 +176,15 @@ int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdat return 0; } +int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata) { + assert_return(ll, -EINVAL); + + ll->check_mac_callback = cb; + ll->check_mac_userdata = userdata; + + return 0; +} + int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) { assert_return(ll, -EINVAL); assert_return(address, -EINVAL); @@ -351,3 +368,14 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { error: ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); } + +static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) { + sd_ipv4ll *ll = userdata; + + assert(ll); + + if (ll->check_mac_callback) + return ll->check_mac_callback(ll, mac, ll->check_mac_userdata); + + return 0; +} diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index 768595969b..7985185b33 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -42,43 +42,31 @@ static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { } } -static int arp_network_send_raw_socket(int fd, int ifindex, - const struct ether_arp *arp) { - assert_se(arp); - assert_se(ifindex > 0); - assert_se(fd >= 0); +int arp_send_packet( + int fd, + int ifindex, + const struct in_addr *pa, + const struct ether_addr *ha, + bool announce) { - if (send(fd, arp, sizeof(struct ether_arp), 0) < 0) + struct ether_arp ea = {}; + + assert_se(fd >= 0); + assert_se(ifindex > 0); + assert_se(pa); + assert_se(ha); + + if (send(fd, &ea, sizeof(struct ether_arp), 0) < 0) return -errno; return 0; } -int arp_send_probe(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha) { - struct ether_arp ea = {}; - - assert_se(fd >= 0); - assert_se(ifindex > 0); - assert_se(pa != 0); - assert_se(ha); - - return arp_network_send_raw_socket(fd, ifindex, &ea); +int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) { + return 0; } -int arp_send_announcement(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha) { - struct ether_arp ea = {}; - - assert_se(fd >= 0); - assert_se(ifindex > 0); - assert_se(pa != 0); - assert_se(ha); - - return arp_network_send_raw_socket(fd, ifindex, &ea); -} - -int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { +int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) { if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0) return -errno; diff --git a/src/network/meson.build b/src/network/meson.build index 5a3a23101d..4e137d7b9e 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -79,6 +79,8 @@ sources = files(''' networkd-dhcp4.h networkd-dhcp6.c networkd-dhcp6.h + networkd-ipv4acd.c + networkd-ipv4acd.h networkd-ipv4ll.c networkd-ipv4ll.h networkd-ipv6-proxy-ndp.c diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index 79a1c1b847..e1cefbefbd 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -95,7 +95,7 @@ static bool address_pool_prefix_is_taken( assert(p); assert(u); - HASHMAP_FOREACH(l, p->manager->links) { + HASHMAP_FOREACH(l, p->manager->links_by_index) { Address *a; /* Don't clash with assigned addresses */ diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 04ad2aa69d..35305aff99 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -9,6 +9,7 @@ #include "netlink-util.h" #include "networkd-address-pool.h" #include "networkd-address.h" +#include "networkd-ipv4acd.h" #include "networkd-manager.h" #include "networkd-network.h" #include "networkd-queue.h" @@ -129,6 +130,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s address->network = network; address->section = TAKE_PTR(n); + address->is_static = true; r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address); if (r < 0) @@ -152,6 +154,7 @@ Address *address_free(Address *address) { set_remove(address->link->addresses, address); set_remove(address->link->addresses_foreign, address); + set_remove(address->link->addresses_ipv4acd, address); set_remove(address->link->static_addresses, address); if (address->link->dhcp_address == address) address->link->dhcp_address = NULL; @@ -301,11 +304,23 @@ bool address_equal(const Address *a1, const Address *a2) { return address_compare_func(a1, a2) == 0; } -static int address_copy(Address *dest, const Address *src) { +int address_dup(const Address *src, Address **ret) { + _cleanup_(address_freep) Address *dest = NULL; int r; - assert(dest); assert(src); + assert(ret); + + dest = newdup(Address, src, 1); + if (!dest) + return -ENOMEM; + + /* clear all pointers */ + dest->network = NULL; + dest->section = NULL; + dest->link = NULL; + dest->label = NULL; + dest->acd = NULL; if (src->family == AF_INET) { r = free_and_strdup(&dest->label, src->label); @@ -313,17 +328,7 @@ static int address_copy(Address *dest, const Address *src) { return r; } - dest->family = src->family; - dest->prefixlen = src->prefixlen; - dest->scope = src->scope; - dest->flags = src->flags; - dest->cinfo = src->cinfo; - dest->in_addr = src->in_addr; - dest->in_addr_peer = src->in_addr_peer; - if (address_may_have_broadcast(src)) - dest->broadcast = src->broadcast; - dest->duplicate_address_detection = src->duplicate_address_detection; - + *ret = TAKE_PTR(dest); return 0; } @@ -373,11 +378,7 @@ static int address_add_internal(Link *link, Set **addresses, const Address *in, assert(addresses); assert(in); - r = address_new(&address); - if (r < 0) - return r; - - r = address_copy(address, in); + r = address_dup(in, &address); if (r < 0) return r; @@ -598,7 +599,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union assert(address); if (family == AF_INET) { - HASHMAP_FOREACH(link, manager->links) + HASHMAP_FOREACH(link, manager->links_by_index) if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0) return !check_ready || address_is_ready(a); } else { @@ -611,7 +612,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union tmp->family = family; tmp->in_addr = *address; - HASHMAP_FOREACH(link, manager->links) + HASHMAP_FOREACH(link, manager->links_by_index) if (address_get(link, tmp, &a) >= 0) return !check_ready || address_is_ready(a); } @@ -658,6 +659,7 @@ static void log_address_debug(const Address *address, const char *str, const Lin } static int address_set_netlink_message(const Address *address, sd_netlink_message *req, Link *link) { + uint32_t flags; int r; assert(address); @@ -669,14 +671,16 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag return log_link_error_errno(link, r, "Could not set prefixlen: %m"); /* On remove, only IFA_F_MANAGETEMPADDR flag for IPv6 addresses are used. But anyway, set all - * flags here unconditionally. Without setting the flag, the template addresses generated by - * kernel will not be removed automatically when the main address is removed. */ - r = sd_rtnl_message_addr_set_flags(req, address->flags & 0xff); + * flags except tentative flag here unconditionally. Without setting the flag, the template + * addresses generated by kernel will not be removed automatically when the main address is + * removed. */ + flags = address->flags & ~IFA_F_TENTATIVE; + r = sd_rtnl_message_addr_set_flags(req, flags & 0xff); if (r < 0) return log_link_error_errno(link, r, "Could not set flags: %m"); - if ((address->flags & ~0xff) != 0) { - r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags); + if ((flags & ~0xff) != 0) { + r = sd_netlink_message_append_u32(req, IFA_FLAGS, flags); if (r < 0) return log_link_error_errno(link, r, "Could not set extended flags: %m"); } @@ -955,11 +959,7 @@ static int address_acquire(Link *link, const Address *original, Address **ret) { else if (original->family == AF_INET6) in_addr.in6.s6_addr[15] |= 1; - r = address_new(&na); - if (r < 0) - return r; - - r = address_copy(na, original); + r = address_dup(original, &na); if (r < 0) return r; @@ -997,17 +997,12 @@ int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, return 1; } -static int ipv4_dad_configure(Address *address); - static int address_configure( const Address *address, Link *link, - link_netlink_message_handler_t callback, - Address **ret) { + link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - Address *acquired_address, *a; - bool update; int r; assert(address); @@ -1018,28 +1013,10 @@ static int address_configure( assert(link->manager->rtnl); assert(callback); - /* If this is a new address, then refuse adding more than the limit */ - if (address_get(link, address, NULL) <= 0 && - set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) - return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG), - "Too many addresses are configured, refusing: %m"); + log_address_debug(address, "Configuring", link); - r = address_acquire(link, address, &acquired_address); - if (r < 0) - return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m"); - if (acquired_address) - address = acquired_address; - - update = address_get(link, address, NULL) >= 0; - - log_address_debug(address, update ? "Updating" : "Configuring", link); - - if (update) - r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, - link->ifindex, address->family); - else - r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR, - link->ifindex, address->family); + r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, + link->ifindex, address->family); if (r < 0) return log_link_error_errno(link, r, "Could not allocate RTM_NEWADDR message: %m"); @@ -1075,31 +1052,11 @@ static int address_configure( if (r < 0) return log_link_error_errno(link, r, "Could not append IFA_RT_PRIORITY attribute: %m"); - r = address_add(link, address, &a); - if (r < 0) - return log_link_error_errno(link, r, "Could not add address: %m"); - - r = address_set_masquerade(a, true); - if (r < 0) - log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link); - if (r < 0) { - (void) address_set_masquerade(a, false); + if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - } link_ref(link); - - if (FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) { - r = ipv4_dad_configure(a); - if (r < 0) - log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m"); - } - - if (ret) - *ret = a; - return 0; } @@ -1151,12 +1108,45 @@ int link_request_address( link_netlink_message_handler_t netlink_handler, Request **ret) { + Address *acquired; + int r; + assert(link); assert(address); + r = address_acquire(link, address, &acquired); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m"); + if (r > 0) { + if (consume_object) { + address_free(address); + consume_object = false; /* address from pool is already managed by Link. */ + } + address = acquired; + } + log_address_debug(address, "Requesting", link); - return link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object, - message_counter, netlink_handler, ret); + r = link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object, + message_counter, netlink_handler, ret); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request address: %m"); + return r; +} + +int link_request_static_address(Link *link, Address *address, bool consume) { + Request *req; + int r; + + assert(link); + assert(address); + + r = link_request_address(link, address, consume, &link->static_address_messages, + static_address_handler, &req); + if (r <= 0) + return r; + + req->after_configure = static_address_after_configure; + return 0; } int link_request_static_addresses(Link *link) { @@ -1170,21 +1160,13 @@ int link_request_static_addresses(Link *link) { link->static_addresses_configured = false; ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) { - Request *req; - - r = link_request_address(link, a, false, &link->static_address_messages, - static_address_handler, &req); + r = link_request_static_address(link, a, false); if (r < 0) return r; - if (r == 0) - continue; - - req->after_configure = static_address_after_configure; } HASHMAP_FOREACH(p, link->network->prefixes_by_section) { _cleanup_(address_freep) Address *address = NULL; - Request *req; if (!p->assign) continue; @@ -1204,14 +1186,9 @@ int link_request_static_addresses(Link *link) { address->family = AF_INET6; address->route_metric = p->route_metric; - r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages, - static_address_handler, &req); + r = link_request_static_address(link, TAKE_PTR(address), true); if (r < 0) return r; - if (r == 0) - continue; - - req->after_configure = static_address_after_configure; } if (in4_addr_is_set(&link->network->dhcp_server_address)) { @@ -1229,14 +1206,9 @@ int link_request_static_addresses(Link *link) { /* The same address may be explicitly configured in [Address] or [Network] section. * Configure the DHCP server address only when it is not. */ if (!link_is_static_address_configured(link, address)) { - Request *req; - - r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages, - static_address_handler, &req); + r = link_request_static_address(link, TAKE_PTR(address), true); if (r < 0) return r; - if (r > 0) - req->after_configure = static_address_after_configure; } } @@ -1251,8 +1223,42 @@ int link_request_static_addresses(Link *link) { return 0; } +static int address_is_ready_to_configure(Link *link, const Address *address) { + int r; + + assert(link); + assert(address); + + if (!link_is_ready_to_configure(link, false)) + return false; + + if (link->address_remove_messages > 0) + return false; + + if (address_get(link, address, NULL) >= 0) + return true; + + /* If this is a new address, then refuse adding more than the limit */ + if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) + return log_link_warning_errno(link, SYNTHETIC_ERRNO(E2BIG), + "Too many addresses are configured, refusing: %m"); + + if (address->family == AF_INET && + address->duplicate_address_detection & ADDRESS_FAMILY_IPV4 && + link->hw_addr.length == ETH_ALEN && + !ether_addr_is_null(&link->hw_addr.ether)) + return ipv4acd_address_is_ready_to_configure(link, address); + + r = address_add(link, address, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Could not add address: %m");; + + return true; +} + int request_process_address(Request *req) { - Address *ret = NULL; /* avoid false maybe-uninitialized warning */ + Address *a; + Link *link; int r; assert(req); @@ -1260,13 +1266,17 @@ int request_process_address(Request *req) { assert(req->address); assert(req->type == REQUEST_TYPE_ADDRESS); - if (!link_is_ready_to_configure(req->link, false)) - return 0; + link = req->link; - if (req->link->address_remove_messages > 0) - return 0; + r = address_is_ready_to_configure(link, req->address); + if (r <= 0) + return r; - r = address_configure(req->address, req->link, req->netlink_handler, &ret); + r = address_get(link, req->address, &a); + if (r < 0) + return r; + + r = address_configure(a, link, req->netlink_handler); if (r < 0) return r; @@ -1274,11 +1284,15 @@ int request_process_address(Request *req) { req->message_counter = NULL; if (req->after_configure) { - r = req->after_configure(req, ret); + r = req->after_configure(req, a); if (r < 0) return r; } + r = address_set_masquerade(a, true); + if (r < 0) + log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m"); + return 1; } @@ -1319,7 +1333,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, return 0; } - r = link_get(m, ifindex, &link); + r = link_get_by_index(m, ifindex, &link); if (r < 0 || !link) { /* when enumerating we might be out of sync, but we will get the address again, so just * ignore it */ @@ -1462,157 +1476,6 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, return 1; } -static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { - Address *address; - Link *link; - int r; - - assert(acd); - assert(userdata); - - address = (Address *) userdata; - link = address->link; - - assert(address->family == AF_INET); - - switch (event) { - case SD_IPV4ACD_EVENT_STOP: - log_link_debug(link, "Stopping ACD client..."); - return; - - case SD_IPV4ACD_EVENT_BIND: - log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR, - IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - link_check_ready(link); - break; - - case SD_IPV4ACD_EVENT_CONFLICT: - log_link_warning(link, "DAD conflict. Dropping address "IPV4_ADDRESS_FMT_STR, - IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - r = address_remove(address, link); - if (r < 0) - log_link_error_errno(link, r, "Failed to drop DAD conflicted address "IPV4_ADDRESS_FMT_STR, - IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - - link_check_ready(link); - break; - - default: - assert_not_reached("Invalid IPv4ACD event."); - } - - (void) sd_ipv4acd_stop(acd); - - return; -} - -static int ipv4_dad_configure(Address *address) { - int r; - - assert(address); - assert(address->link); - - if (address->family != AF_INET) - return 0; - - log_address_debug(address, "Starting IPv4ACD client. Probing", address->link); - - if (!address->acd) { - r = sd_ipv4acd_new(&address->acd); - if (r < 0) - return r; - - r = sd_ipv4acd_attach_event(address->acd, address->link->manager->event, 0); - if (r < 0) - return r; - } - - r = sd_ipv4acd_set_ifindex(address->acd, address->link->ifindex); - if (r < 0) - return r; - - r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.ether); - if (r < 0) - return r; - - r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in); - if (r < 0) - return r; - - r = sd_ipv4acd_set_callback(address->acd, static_address_on_acd, address); - if (r < 0) - return r; - - return sd_ipv4acd_start(address->acd, true); -} - -static int ipv4_dad_update_mac_one(Address *address) { - bool running; - int r; - - assert(address); - - if (!address->acd) - return 0; - - running = sd_ipv4acd_is_running(address->acd); - - r = sd_ipv4acd_stop(address->acd); - if (r < 0) - return r; - - r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.ether); - if (r < 0) - return r; - - if (running) { - r = sd_ipv4acd_start(address->acd, true); - if (r < 0) - return r; - } - - return 0; -} - -int ipv4_dad_update_mac(Link *link) { - Address *address; - int k, r = 0; - - assert(link); - - SET_FOREACH(address, link->addresses) { - k = ipv4_dad_update_mac_one(address); - if (k < 0 && r >= 0) - r = k; - } - - return r; -} - -int ipv4_dad_stop(Link *link) { - Address *address; - int k, r = 0; - - assert(link); - - SET_FOREACH(address, link->addresses) { - k = sd_ipv4acd_stop(address->acd); - if (k < 0 && r >= 0) - r = k; - } - - return r; -} - -void ipv4_dad_unref(Link *link) { - Address *address; - - assert(link); - - SET_FOREACH(address, link->addresses) - address->acd = sd_ipv4acd_unref(address->acd); -} - int config_parse_broadcast( const char *unit, const char *filename, diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 56251089d1..ff3d46abdd 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -41,12 +41,13 @@ typedef struct Address { bool scope_set:1; bool ip_masquerade_done:1; + bool is_static:1; /* currently only used by IPv4ACD */ + bool acd_announced:1; AddressFamily duplicate_address_detection; + sd_ipv4acd *acd; /* Called when address become ready */ address_ready_callback_t callback; - - sd_ipv4acd *acd; } Address; int address_new(Address **ret); @@ -55,6 +56,7 @@ int address_get(Link *link, const Address *in, Address **ret); int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); int address_remove(const Address *address, Link *link); bool address_equal(const Address *a1, const Address *a2); +int address_dup(const Address *src, Address **ret); bool address_is_ready(const Address *a); void address_set_broadcast(Address *a); @@ -70,10 +72,6 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address ** int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret); int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready); -void ipv4_dad_unref(Link *link); -int ipv4_dad_stop(Link *link); -int ipv4_dad_update_mac(Link *link); - int link_request_address( Link *link, Address *address, @@ -81,6 +79,7 @@ int link_request_address( unsigned *message_counter, link_netlink_message_handler_t netlink_handler, Request **ret); +int link_request_static_address(Link *link, Address *address, bool consume); int link_request_static_addresses(Link *link); int request_process_address(Request *req); diff --git a/src/network/networkd-bridge-fdb.c b/src/network/networkd-bridge-fdb.c index 115ea5bb14..ab1b766d48 100644 --- a/src/network/networkd-bridge-fdb.c +++ b/src/network/networkd-bridge-fdb.c @@ -228,7 +228,7 @@ static bool bridge_fdb_is_ready_to_configure(BridgeFDB *fdb, Link *link) { fdb->outgoing_ifindex = out->ifindex; } else if (fdb->outgoing_ifindex > 0) { - if (link_get(link->manager, fdb->outgoing_ifindex, &out) < 0) + if (link_get_by_index(link->manager, fdb->outgoing_ifindex, &out) < 0) return false; } if (out && !link_is_ready_to_configure(out, false)) diff --git a/src/network/networkd-bridge-mdb.c b/src/network/networkd-bridge-mdb.c index faf3dbd0b7..0f808fb515 100644 --- a/src/network/networkd-bridge-mdb.c +++ b/src/network/networkd-bridge-mdb.c @@ -249,7 +249,7 @@ static bool bridge_mdb_is_ready_to_configure(Link *link) { if (link->master_ifindex != link->network->bridge->ifindex) return false; - if (link_get(link->manager, link->master_ifindex, &master) < 0) + if (link_get_by_index(link->manager, link->master_ifindex, &master) < 0) return false; if (!streq_ptr(master->kind, "bridge")) diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index 8c0cd37f3f..9e2faa5675 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -104,7 +104,7 @@ static int dhcp_server_find_uplink(Link *link, Link **ret) { return link_get_by_name(link->manager, link->network->dhcp_server_uplink_name, ret); if (link->network->dhcp_server_uplink_index > 0) - return link_get(link->manager, link->network->dhcp_server_uplink_index, ret); + return link_get_by_index(link->manager, link->network->dhcp_server_uplink_index, ret); if (link->network->dhcp_server_uplink_index == 0) { /* It is not necessary to propagate error in automatic selection. */ diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 878b306c1f..f80adcdbcf 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -14,6 +14,7 @@ #include "network-internal.h" #include "networkd-address.h" #include "networkd-dhcp4.h" +#include "networkd-ipv4acd.h" #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-network.h" @@ -84,9 +85,6 @@ static int dhcp4_release_old_lease(Link *link) { static void dhcp4_check_ready(Link *link) { int r; - if (link->network->dhcp_send_decline && !link->dhcp4_address_bind) - return; - if (link->dhcp4_messages > 0) { log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__); return; @@ -738,7 +736,7 @@ static int dhcp4_remove_all(Link *link) { return r; } -static int dhcp_lease_lost(Link *link) { +int dhcp4_lease_lost(Link *link) { int k, r = 0; assert(link); @@ -748,7 +746,7 @@ static int dhcp_lease_lost(Link *link) { link->dhcp4_configured = false; - /* dhcp_lease_lost() may be called during renewing IP address. */ + /* dhcp4_lease_lost() may be called during renewing IP address. */ k = dhcp4_release_old_lease(link); if (k < 0) r = k; @@ -768,7 +766,23 @@ static int dhcp_lease_lost(Link *link) { link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); link_dirty(link); - (void) sd_ipv4acd_stop(link->dhcp_acd); + if (link->network->dhcp_send_decline) { + Address *a; + + /* The acquired address may be still ARP probing and not configured. */ + + SET_FOREACH(a, link->addresses_ipv4acd) + if (!a->is_static && address_get(link, a, NULL) < 0) { + Request req = { + .link = link, + .address = a, + }; + + log_link_debug(link, "Canceling the request to configure DHCPv4 address "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(a->in_addr.in)); + request_drop(ordered_set_get(link->manager->request_queue, &req)); + } + } if (r < 0) return r; @@ -780,149 +794,6 @@ static int dhcp_lease_lost(Link *link) { return link_request_static_routes(link, true); } -static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { - struct in_addr address; - Link *link; - int r; - - assert(acd); - assert(userdata); - - link = userdata; - - switch (event) { - case SD_IPV4ACD_EVENT_STOP: - log_link_debug(link, "Stopping ACD client for DHCPv4 address."); - return; - - case SD_IPV4ACD_EVENT_BIND: - if (DEBUG_LOGGING) { - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); - log_link_debug(link, "Successfully claimed DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address)); - } - link->dhcp4_address_bind = true; - dhcp4_check_ready(link); - break; - - case SD_IPV4ACD_EVENT_CONFLICT: - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); - log_link_warning(link, "DAD conflict. Dropping DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address)); - - r = sd_dhcp_client_send_decline(link->dhcp_client); - if (r < 0) - log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m"); - - if (link->dhcp_lease) { - r = dhcp_lease_lost(link); - if (r < 0) - link_enter_failed(link); - } - break; - - default: - assert_not_reached("Invalid IPv4ACD event."); - } - - (void) sd_ipv4acd_stop(acd); - - return; -} - -static int dhcp4_configure_dad(Link *link) { - int r; - - assert(link); - assert(link->manager); - assert(link->network); - - if (!link->network->dhcp_send_decline) - return 0; - - if (!link->dhcp_acd) { - r = sd_ipv4acd_new(&link->dhcp_acd); - if (r < 0) - return r; - - r = sd_ipv4acd_attach_event(link->dhcp_acd, link->manager->event, 0); - if (r < 0) - return r; - } - - r = sd_ipv4acd_set_ifindex(link->dhcp_acd, link->ifindex); - if (r < 0) - return r; - - r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->hw_addr.ether); - if (r < 0) - return r; - - return 0; -} - -static int dhcp4_dad_update_mac(Link *link) { - bool running; - int r; - - assert(link); - - if (!link->dhcp_acd) - return 0; - - running = sd_ipv4acd_is_running(link->dhcp_acd); - - r = sd_ipv4acd_stop(link->dhcp_acd); - if (r < 0) - return r; - - r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->hw_addr.ether); - if (r < 0) - return r; - - if (running) { - r = sd_ipv4acd_start(link->dhcp_acd, true); - if (r < 0) - return r; - } - - return 0; -} - -static int dhcp4_start_acd(Link *link) { - struct in_addr addr, old; - int r; - - if (!link->network->dhcp_send_decline) - return 0; - - if (!link->dhcp_lease) - return 0; - - (void) sd_ipv4acd_stop(link->dhcp_acd); - - link->dhcp4_address_bind = false; - - r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr); - if (r < 0) - return r; - - r = sd_ipv4acd_get_address(link->dhcp_acd, &old); - if (r < 0) - return r; - - r = sd_ipv4acd_set_address(link->dhcp_acd, &addr); - if (r < 0) - return r; - - r = sd_ipv4acd_set_callback(link->dhcp_acd, dhcp_address_on_acd, link); - if (r < 0) - return r; - - log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address "IPV4_ADDRESS_FMT_STR, - IPV4_ADDRESS_FMT_VAL(addr)); - - return sd_ipv4acd_start(link->dhcp_acd, !in4_addr_equal(&addr, &old)); -} - static int dhcp4_address_ready_callback(Address *address) { assert(address); @@ -957,11 +828,6 @@ static int dhcp4_after_address_configure(Request *req, void *object) { } link->dhcp_address = address; - - r = dhcp4_start_acd(link); - if (r < 0) - return log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCPv4 address: %m"); - return 0; } @@ -1057,6 +923,7 @@ static int dhcp4_request_address(Link *link, bool announce) { addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr; SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link)); addr->route_metric = link->network->dhcp_route_metric; + addr->duplicate_address_detection = link->network->dhcp_send_decline ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_NO; if (address_get(link, addr, NULL) < 0) link->dhcp4_configured = false; @@ -1180,7 +1047,7 @@ static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) { r = dhcp_lease_acquired(client, link); if (r < 0) - (void) dhcp_lease_lost(link); + (void) dhcp4_lease_lost(link); return r; } @@ -1276,7 +1143,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { r, "Failed to send DHCP RELEASE, ignoring: %m"); } - r = dhcp_lease_lost(link); + r = dhcp4_lease_lost(link); if (r < 0) { link_enter_failed(link); return r; @@ -1291,7 +1158,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { } if (link->dhcp_lease) { - r = dhcp_lease_lost(link); + r = dhcp4_lease_lost(link); if (r < 0) { link_enter_failed(link); return r; @@ -1687,10 +1554,6 @@ int dhcp4_configure(Link *link) { if (r < 0) return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set initial DHCPv4 address: %m"); - r = dhcp4_configure_dad(link); - if (r < 0) - return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to configure service type: %m"); - return dhcp4_set_client_identifier(link); } @@ -1708,15 +1571,7 @@ int dhcp4_update_mac(Link *link) { if (r < 0) return r; - r = dhcp4_set_client_identifier(link); - if (r < 0) - return r; - - r = dhcp4_dad_update_mac(link); - if (r < 0) - return r; - - return 0; + return dhcp4_set_client_identifier(link); } int dhcp4_start(Link *link) { diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h index a33fe403be..e41e39fc6e 100644 --- a/src/network/networkd-dhcp4.h +++ b/src/network/networkd-dhcp4.h @@ -21,6 +21,7 @@ void network_adjust_dhcp4(Network *network); int dhcp4_configure(Link *link); int dhcp4_update_mac(Link *link); int dhcp4_start(Link *link); +int dhcp4_lease_lost(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_acl_ip_address); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index e2c314514b..e850e595e3 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -602,7 +602,7 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, assert(masked_pd_prefix); assert(pd_prefix_len <= 64); - HASHMAP_FOREACH(link, dhcp6_link->manager->links) { + HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { _cleanup_free_ char *assigned_buf = NULL; struct in6_addr assigned_prefix; @@ -706,7 +706,7 @@ static void dhcp6_pd_prefix_lost(Link *dhcp6_link) { assert(dhcp6_link); assert(dhcp6_link->manager); - HASHMAP_FOREACH(link, dhcp6_link->manager->links) { + HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { if (link == dhcp6_link) continue; @@ -954,7 +954,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { assert(dhcp6_link); assert(dhcp6_link->dhcp6_lease); - HASHMAP_FOREACH(link, dhcp6_link->manager->links) { + HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { if (link == dhcp6_link) continue; @@ -1024,7 +1024,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { false); } - HASHMAP_FOREACH(link, dhcp6_link->manager->links) { + HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { if (link == dhcp6_link) continue; @@ -1450,7 +1450,7 @@ int dhcp6_request_prefix_delegation(Link *link) { log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link"); - HASHMAP_FOREACH(l, link->manager->links) { + HASHMAP_FOREACH(l, link->manager->links_by_index) { int r, enabled; if (l == link) @@ -1548,7 +1548,7 @@ static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { assert(dhcp6_link); assert(dhcp6_link->manager); - HASHMAP_FOREACH(link, dhcp6_link->manager->links) { + HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { if (link == dhcp6_link) continue; diff --git a/src/network/networkd-ipv4acd.c b/src/network/networkd-ipv4acd.c new file mode 100644 index 0000000000..ea902d9766 --- /dev/null +++ b/src/network/networkd-ipv4acd.c @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-dhcp-client.h" +#include "sd-ipv4acd.h" + +#include "networkd-address.h" +#include "networkd-dhcp4.h" +#include "networkd-ipv4acd.h" +#include "networkd-link.h" +#include "networkd-manager.h" + +static int static_address_on_stop(Link *link, Address *address) { + int r; + + assert(link); + assert(address); + + if (address_get(link, address, NULL) < 0) + return 0; + + log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.", + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + + r = address_remove(address, link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m", + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + + return 0; +} + +static int static_address_on_conflict(Link *link, Address *address) { + int r; + + assert(link); + assert(address); + + if (address_get(link, address, NULL) < 0) { + log_link_warning(link, "Cannot configure requested address "IPV4_ADDRESS_FMT_STR", as an address conflict is detected.", + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + return 0; + } + + log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict is detected.", + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + + r = address_remove(address, link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m", + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + + return 0; +} + +static int dhcp4_address_on_conflict(Link *link) { + int r; + + assert(link); + assert(link->dhcp_client); + + r = sd_dhcp_client_send_decline(link->dhcp_client); + if (r < 0) + log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m"); + + if (!link->dhcp_lease) + /* Unlikely, but during probing the address, the lease may be lost. */ + return 0; + + log_link_warning(link, "Dropping DHCPv4 lease, as an address conflict is detected."); + r = dhcp4_lease_lost(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m"); + + /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */ + return 0; +} + +static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) { + Address *address = userdata; + Link *link; + int r; + + assert(acd); + assert(address); + assert(address->acd == acd); + assert(address->link); + assert(address->family == AF_INET); + + link = address->link; + + switch (event) { + case SD_IPV4ACD_EVENT_STOP: + if (is_static) { + r = static_address_on_stop(link, address); + if (r < 0) + link_enter_failed(link); + } + + /* We have nothing to do for DHCPv4 lease here, as the dhcp client is already stopped + * when stopping the ipv4acd client. See link_stop_engines(). */ + break; + + case SD_IPV4ACD_EVENT_BIND: + log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + + address->acd_announced = true; + break; + + case SD_IPV4ACD_EVENT_CONFLICT: + if (is_static) + r = static_address_on_conflict(link, address); + else + r = dhcp4_address_on_conflict(link); + if (r < 0) + link_enter_failed(link); + break; + + default: + assert_not_reached("Invalid IPv4ACD event."); + } +} + +static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { + on_acd(acd, event, userdata, true); +} + +static void dhcp4_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { + on_acd(acd, event, userdata, false); +} + +static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) { + Manager *m = userdata; + struct hw_addr_data hw_addr; + + assert(m); + assert(mac); + + hw_addr = (struct hw_addr_data) { + .length = ETH_ALEN, + .ether = *mac, + }; + + return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0; +} + +static int ipv4acd_configure(Link *link, const Address *a) { + _cleanup_(address_freep) Address *address = NULL; + int r; + + assert(link); + assert(a); + assert(a->family == AF_INET); + + log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(a->in_addr.in)); + + r = address_dup(a, &address); + if (r < 0) + return r; + + r = set_ensure_put(&link->addresses_ipv4acd, &address_hash_ops, address); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + address->link = link; + + r = sd_ipv4acd_new(&address->acd); + if (r < 0) + return r; + + r = sd_ipv4acd_attach_event(address->acd, link->manager->event, 0); + if (r < 0) + return r; + + r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex); + if (r < 0) + return r; + + r = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether); + if (r < 0) + return r; + + r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in); + if (r < 0) + return r; + + r = sd_ipv4acd_set_callback(address->acd, + address->is_static ? static_address_on_acd : dhcp4_address_on_acd, + address); + if (r < 0) + return r; + + r = sd_ipv4acd_set_check_mac_callback(address->acd, ipv4acd_check_mac, link->manager); + if (r < 0) + return r; + + if (link_has_carrier(link)) { + r = sd_ipv4acd_start(address->acd, true); + if (r < 0) + return r; + } + + TAKE_PTR(address); + return 0; +} + +int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address) { + Address *acd_address; + int r; + + acd_address = set_get(link->addresses_ipv4acd, address); + if (!acd_address) { + r = ipv4acd_configure(link, address); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure IPv4ACD client: %m"); + + return false; + } + + if (!acd_address->acd_announced) + return false; + + r = set_ensure_put(&link->addresses, &address_hash_ops, acd_address); + if (r < 0) + return log_oom(); + if (r == 0) + return log_link_warning_errno(link, SYNTHETIC_ERRNO(EEXIST), "Address already exists."); + + acd_address->flags |= IFA_F_TENTATIVE; + return true; +} + +int ipv4acd_update_mac(Link *link) { + Address *address; + int k, r = 0; + + assert(link); + + if (link->hw_addr.length != ETH_ALEN) + return 0; + if (ether_addr_is_null(&link->hw_addr.ether)) + return 0; + + SET_FOREACH(address, link->addresses_ipv4acd) { + assert(address->acd); + + k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether); + if (k < 0) + r = k; + } + if (r < 0) + link_enter_failed(link); + + return r; +} + +int ipv4acd_start(Link *link) { + Address *address; + int r; + + assert(link); + + SET_FOREACH(address, link->addresses_ipv4acd) { + if (sd_ipv4acd_is_running(address->acd)) + continue; + + r = sd_ipv4acd_start(address->acd, true); + if (r < 0) + return r; + } + + return 0; +} + +int ipv4acd_stop(Link *link) { + Address *address; + int k, r = 0; + + assert(link); + + SET_FOREACH(address, link->addresses_ipv4acd) { + k = sd_ipv4acd_stop(address->acd); + if (k < 0) + r = k; + } + + return r; +} diff --git a/src/network/networkd-ipv4acd.h b/src/network/networkd-ipv4acd.h new file mode 100644 index 0000000000..d2b4ff775f --- /dev/null +++ b/src/network/networkd-ipv4acd.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +typedef struct Address Address; +typedef struct Link Link; + +int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address); +int ipv4acd_update_mac(Link *link); +int ipv4acd_start(Link *link); +int ipv4acd_stop(Link *link); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index a2a76d848c..83ed3726f7 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -122,8 +122,10 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { } r = sd_ipv4ll_restart(ll); - if (r < 0) + if (r < 0) { log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); + link_enter_failed(link); + } break; case SD_IPV4LL_EVENT_BIND: r = ipv4ll_address_claimed(ll, link); @@ -139,6 +141,21 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { } } +static int ipv4ll_check_mac(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata) { + Manager *m = userdata; + struct hw_addr_data hw_addr; + + assert(m); + assert(mac); + + hw_addr = (struct hw_addr_data) { + .length = ETH_ALEN, + .ether = *mac, + }; + + return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0; +} + int ipv4ll_configure(Link *link) { uint64_t seed; int r; @@ -178,35 +195,20 @@ int ipv4ll_configure(Link *link) { if (r < 0) return r; - return 0; + return sd_ipv4ll_set_check_mac_callback(link->ipv4ll, ipv4ll_check_mac, link->manager); } int ipv4ll_update_mac(Link *link) { - bool restart; - int r; - assert(link); + if (link->hw_addr.length != ETH_ALEN) + return 0; + if (ether_addr_is_null(&link->hw_addr.ether)) + return 0; if (!link->ipv4ll) return 0; - restart = sd_ipv4ll_is_running(link->ipv4ll) > 0; - - r = sd_ipv4ll_stop(link->ipv4ll); - if (r < 0) - return r; - - r = sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether); - if (r < 0) - return r; - - if (restart) { - r = sd_ipv4ll_start(link->ipv4ll); - if (r < 0) - return r; - } - - return 0; + return sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether); } int config_parse_ipv4ll( diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index c78e653c0b..2bae2f2913 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -114,11 +114,11 @@ int manager_build_json(Manager *manager, JsonVariant **ret) { assert(manager); assert(ret); - elements = new(JsonVariant*, hashmap_size(manager->links)); + elements = new(JsonVariant*, hashmap_size(manager->links_by_index)); if (!elements) return -ENOMEM; - HASHMAP_FOREACH(link, manager->links) { + HASHMAP_FOREACH(link, manager->links_by_index) { r = link_build_json(link, elements + n); if (r < 0) goto finalize; diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index 50c3f60080..f4e3556517 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -834,11 +834,11 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char *** assert(m); assert(nodes); - l = new0(char*, hashmap_size(m->links) + 1); + l = new0(char*, hashmap_size(m->links_by_index) + 1); if (!l) return -ENOMEM; - HASHMAP_FOREACH(link, m->links) { + HASHMAP_FOREACH(link, m->links_by_index) { char *p; p = link_bus_path(link); @@ -874,7 +874,7 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void if (ifindex < 0) return 0; - r = link_get(m, ifindex, &link); + r = link_get_by_index(m, ifindex, &link); if (r < 0) return 0; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index c98f684bfd..4efe74ad41 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -33,6 +33,7 @@ #include "networkd-dhcp-server.h" #include "networkd-dhcp4.h" #include "networkd-dhcp6.h" +#include "networkd-ipv4acd.h" #include "networkd-ipv4ll.h" #include "networkd-ipv6-proxy-ndp.h" #include "networkd-link-bus.h" @@ -74,6 +75,12 @@ bool link_ipv4ll_enabled(Link *link) { if (link->iftype == ARPHRD_CAN) return false; + if (link->hw_addr.length != ETH_ALEN) + return false; + + if (ether_addr_is_null(&link->hw_addr.ether)) + return false; + if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "ip6gre","ip6tnl", "sit", "vti", "vti6", "nlmon", "xfrm", "bareudp")) @@ -198,7 +205,6 @@ static void link_free_engines(Link *link) { link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server); link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client); link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); - link->dhcp_acd = sd_ipv4acd_unref(link->dhcp_acd); link->lldp = sd_lldp_unref(link->lldp); link_lldp_emit_stop(link); @@ -210,8 +216,6 @@ static void link_free_engines(Link *link) { link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease); link->ndisc = sd_ndisc_unref(link->ndisc); link->radv = sd_radv_unref(link->radv); - - ipv4_dad_unref(link); } static Link *link_free(Link *link) { @@ -238,6 +242,7 @@ static Link *link_free(Link *link) { link->addresses = set_free(link->addresses); link->addresses_foreign = set_free(link->addresses_foreign); + link->addresses_ipv4acd = set_free(link->addresses_ipv4acd); link->pool_addresses = set_free(link->pool_addresses); link->static_addresses = set_free(link->static_addresses); link->dhcp6_addresses = set_free(link->dhcp6_addresses); @@ -274,13 +279,13 @@ static Link *link_free(Link *link) { DEFINE_TRIVIAL_REF_UNREF_FUNC(Link, link, link_free); -int link_get(Manager *m, int ifindex, Link **ret) { +int link_get_by_index(Manager *m, int ifindex, Link **ret) { Link *link; assert(m); assert(ifindex > 0); - link = hashmap_get(m->links, INT_TO_PTR(ifindex)); + link = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex)); if (!link) return -ENODEV; @@ -304,6 +309,21 @@ int link_get_by_name(Manager *m, const char *ifname, Link **ret) { return 0; } +int link_get_by_hw_addr(Manager *m, const struct hw_addr_data *hw_addr, Link **ret) { + Link *link; + + assert(m); + assert(hw_addr); + + link = hashmap_get(m->links_by_hw_addr, hw_addr); + if (!link) + return -ENODEV; + + if (ret) + *ret = link; + return 0; +} + int link_get_master(Link *link, Link **ret) { assert(link); assert(link->manager); @@ -312,7 +332,7 @@ int link_get_master(Link *link, Link **ret) { if (link->master_ifindex <= 0 || link->master_ifindex == link->ifindex) return -ENODEV; - return link_get(link->manager, link->master_ifindex, ret); + return link_get_by_index(link->manager, link->master_ifindex, ret); } void link_set_state(Link *link, LinkState state) { @@ -340,6 +360,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { bool keep_dhcp = may_keep_dhcp && link->network && + !link->network->dhcp_send_decline && /* IPv4 ACD for the DHCPv4 address is running. */ (link->manager->restarting || FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP)); @@ -349,10 +370,6 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m"); } - k = sd_ipv4acd_stop(link->dhcp_acd); - if (k < 0) - r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client for DHCPv4: %m"); - k = sd_dhcp_server_stop(link->dhcp_server); if (k < 0) r = log_link_warning_errno(link, k, "Could not stop DHCPv4 server: %m"); @@ -365,7 +382,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m"); - k = ipv4_dad_stop(link); + k = ipv4acd_stop(link); if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m"); @@ -647,6 +664,10 @@ static int link_acquire_dynamic_ipv4_conf(Link *link) { return log_link_warning_errno(link, r, "Could not start DHCP server: %m"); } + r = ipv4acd_start(link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not start IPv4 ACD client: %m"); + return 0; } @@ -774,7 +795,7 @@ static int link_new_bound_by_list(Link *link) { m = link->manager; - HASHMAP_FOREACH(carrier, m->links) { + HASHMAP_FOREACH(carrier, m->links_by_index) { if (!carrier->network) continue; @@ -813,7 +834,7 @@ static int link_new_bound_to_list(Link *link) { m = link->manager; - HASHMAP_FOREACH(carrier, m->links) { + HASHMAP_FOREACH(carrier, m->links_by_index) { if (strv_fnmatch(link->network->bind_carrier, carrier->ifname)) { r = link_put_carrier(link, carrier, &link->bound_to_links); if (r < 0) @@ -961,11 +982,14 @@ static Link *link_drop(Link *link) { STRV_FOREACH(n, link->alternative_names) hashmap_remove(link->manager->links_by_name, *n); - hashmap_remove(link->manager->links_by_name, link->ifname); + /* bonding master and its slaves have the same hardware address. */ + if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link) + hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr); + /* The following must be called at last. */ - assert_se(hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)) == link); + assert_se(hashmap_remove(link->manager->links_by_index, INT_TO_PTR(link->ifindex)) == link); return link_unref(link); } @@ -1471,7 +1495,7 @@ int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, voi return 0; } - r = link_get(m, ifindex, &link); + r = link_get_by_index(m, ifindex, &link); if (r < 0) { log_device_debug_errno(device, r, "Failed to get link from ifindex %i, ignoring: %m", ifindex); return 0; @@ -1974,7 +1998,7 @@ static int link_update_master(Link *link, sd_netlink_message *message) { } static int link_update_hardware_address(Link *link, sd_netlink_message *message) { - struct hw_addr_data hw_addr; + struct hw_addr_data old; int r; assert(link); @@ -1984,18 +2008,38 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message) if (r < 0 && r != -ENODATA) return log_link_debug_errno(link, r, "rtnl: failed to read broadcast address: %m"); - r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, &hw_addr); + old = link->hw_addr; + r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, &link->hw_addr); if (r == -ENODATA) return 0; if (r < 0) - return log_link_warning_errno(link, r, "rtnl: failed to read hardware address: %m"); + return log_link_debug_errno(link, r, "rtnl: failed to read hardware address: %m"); - if (hw_addr_equal(&link->hw_addr, &hw_addr)) + if (hw_addr_equal(&link->hw_addr, &old)) return 0; - link->hw_addr = hw_addr; + if (hw_addr_is_null(&old)) + log_link_debug(link, "Saved hardware address: %s", HW_ADDR_TO_STR(&link->hw_addr)); + else { + log_link_debug(link, "Hardware address is changed: %s → %s", + HW_ADDR_TO_STR(&old), HW_ADDR_TO_STR(&link->hw_addr)); - log_link_debug(link, "Gained new hardware address: %s", HW_ADDR_TO_STR(&hw_addr)); + if (hashmap_get(link->manager->links_by_hw_addr, &old) == link) + hashmap_remove(link->manager->links_by_hw_addr, &old); + } + + if (!hw_addr_is_null(&link->hw_addr)) { + r = hashmap_ensure_put(&link->manager->links_by_hw_addr, &hw_addr_hash_ops, &link->hw_addr, link); + if (r == -EEXIST && streq_ptr(link->kind, "bond")) + /* bonding master and its slaves have the same hardware address. */ + r = hashmap_replace(link->manager->links_by_hw_addr, &link->hw_addr, link); + if (r < 0) + log_link_debug_errno(link, r, "Failed to manage link by its new hardware address, ignoring: %m"); + } + + r = ipv4ll_update_mac(link); + if (r < 0) + return log_link_debug_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m"); r = ipv4ll_update_mac(link); if (r < 0) @@ -2025,10 +2069,6 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message) return log_link_debug_errno(link, r, "Could not update MAC address for LLDP: %m"); } - r = ipv4_dad_update_mac(link); - if (r < 0) - return log_link_debug_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m"); - return 0; } @@ -2257,7 +2297,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID, }; - r = hashmap_ensure_put(&manager->links, NULL, INT_TO_PTR(link->ifindex), link); + r = hashmap_ensure_put(&manager->links_by_index, NULL, INT_TO_PTR(link->ifindex), link); if (r < 0) return log_link_debug_errno(link, r, "Failed to store link into manager: %m"); @@ -2323,7 +2363,7 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man return 0; } - (void) link_get(manager, ifindex, &link); + (void) link_get_by_index(manager, ifindex, &link); (void) netdev_get(manager, name, &netdev); switch (type) { diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index f5a2fc802b..4077ccaf09 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -104,6 +104,7 @@ typedef struct Link { Set *addresses; Set *addresses_foreign; + Set *addresses_ipv4acd; Set *pool_addresses; Set *static_addresses; Set *neighbors; @@ -119,11 +120,9 @@ typedef struct Link { Set *dhcp_routes, *dhcp_routes_old; char *lease_file; unsigned dhcp4_messages; - sd_ipv4acd *dhcp_acd; bool dhcp4_route_failed:1; bool dhcp4_route_retrying:1; bool dhcp4_configured:1; - bool dhcp4_address_bind:1; sd_ipv4ll *ipv4ll; bool ipv4ll_address_configured:1; @@ -218,8 +217,9 @@ Link *link_ref(Link *link); DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref); DEFINE_TRIVIAL_DESTRUCTOR(link_netlink_destroy_callback, Link, link_unref); -int link_get(Manager *m, int ifindex, Link **ret); +int link_get_by_index(Manager *m, int ifindex, Link **ret); int link_get_by_name(Manager *m, const char *ifname, Link **ret); +int link_get_by_hw_addr(Manager *m, const struct hw_addr_data *hw_addr, Link **ret); int link_get_master(Link *link, Link **ret); int link_getlink_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c index a1520240d7..8a5ad8eac3 100644 --- a/src/network/networkd-manager-bus.c +++ b/src/network/networkd-manager-bus.c @@ -31,7 +31,7 @@ static int method_list_links(sd_bus_message *message, void *userdata, sd_bus_err if (r < 0) return r; - HASHMAP_FOREACH(link, manager->links) { + HASHMAP_FOREACH(link, manager->links_by_index) { _cleanup_free_ char *path = NULL; path = link_bus_path(link); @@ -95,8 +95,8 @@ static int method_get_link_by_index(sd_bus_message *message, void *userdata, sd_ if (r < 0) return r; - link = hashmap_get(manager->links, INT_TO_PTR(ifindex)); - if (!link) + r = link_get_by_index(manager, ifindex, &link); + if (r < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex); r = sd_bus_message_new_method_return(message, &reply); @@ -126,8 +126,8 @@ static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_ if (r < 0) return r; - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); - if (!l) + r = link_get_by_index(m, ifindex, &l); + if (r < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex); return handler(message, l, error); @@ -215,7 +215,7 @@ static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_err if (r < 0) return r; - HASHMAP_FOREACH(link, manager->links) { + HASHMAP_FOREACH(link, manager->links_by_index) { r = link_reconfigure(link, /* force = */ false); if (r < 0) return r; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 5b6eefb743..374d27bef3 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -58,7 +58,7 @@ static int manager_reset_all(Manager *m) { assert(m); - HASHMAP_FOREACH(link, m->links) { + HASHMAP_FOREACH(link, m->links_by_index) { r = link_carrier_reset(link); if (r < 0) { log_link_warning_errno(link, r, "Could not reset carrier: %m"); @@ -451,7 +451,7 @@ Manager* manager_free(Manager *m) { free(m->state_file); - HASHMAP_FOREACH(link, m->links) + HASHMAP_FOREACH(link, m->links_by_index) (void) link_stop_engines(link, true); m->request_queue = ordered_set_free(m->request_queue); @@ -462,7 +462,8 @@ Manager* manager_free(Manager *m) { m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref); m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref); m->links_by_name = hashmap_free(m->links_by_name); - m->links = hashmap_free_with_destructor(m->links, link_unref); + m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr); + m->links_by_index = hashmap_free_with_destructor(m->links_by_index, link_unref); m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref); @@ -528,7 +529,7 @@ int manager_start(Manager *m) { if (r < 0) log_warning_errno(r, "Failed to update state file %s, ignoring: %m", m->state_file); - HASHMAP_FOREACH(link, m->links) { + HASHMAP_FOREACH(link, m->links_by_index) { r = link_save(link); if (r < 0) log_link_warning_errno(link, r, "Failed to update link state file %s, ignoring: %m", link->state_file); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 24845a55c2..4ee48f3468 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -44,8 +44,9 @@ struct Manager { LinkAddressState ipv6_address_state; LinkOnlineState online_state; - Hashmap *links; + Hashmap *links_by_index; Hashmap *links_by_name; + Hashmap *links_by_hw_addr; Hashmap *netdevs; OrderedHashmap *networks; Hashmap *dhcp6_prefixes; diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 147dca7ef3..b23f081bfb 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -536,7 +536,7 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, return 0; } - r = link_get(m, ifindex, &link); + r = link_get_by_index(m, ifindex, &link); if (r < 0 || !link) { /* when enumerating we might be out of sync, but we will get the neighbor again. Also, * kernel sends messages about neighbors after a link is removed. So, just ignore it. */ diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index 4bc29ca057..a52e5dcb16 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -645,7 +645,7 @@ static bool links_have_nexthop(const Manager *manager, const NextHop *nexthop, c assert(manager); - HASHMAP_FOREACH(link, manager->links) { + HASHMAP_FOREACH(link, manager->links_by_index) { if (link == except) continue; @@ -765,7 +765,7 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { if (!FLAGS_SET(link->flags, IFF_UP)) return false; - HASHMAP_FOREACH(l, link->manager->links) { + HASHMAP_FOREACH(l, link->manager->links_by_index) { if (l->address_remove_messages > 0) return false; if (l->nexthop_remove_messages > 0) @@ -868,7 +868,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, return 0; } - r = link_get(m, ifindex, &link); + r = link_get_by_index(m, ifindex, &link); if (r < 0 || !link) { if (!m->enumerating) log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex); diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 3480f5c890..5ef4acf5ac 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -78,6 +78,9 @@ static Request *request_free(Request *req) { DEFINE_TRIVIAL_CLEANUP_FUNC(Request*, request_free); void request_drop(Request *req) { + if (!req) + return; + if (req->message_counter) (*req->message_counter)--; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 2893c0949d..7b36b48141 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -753,7 +753,7 @@ bool manager_address_is_reachable(Manager *manager, int family, const union in_a assert(IN_SET(family, AF_INET, AF_INET6)); assert(address); - HASHMAP_FOREACH(link, manager->links) { + HASHMAP_FOREACH(link, manager->links_by_index) { Route *route; SET_FOREACH(route, link->routes) @@ -807,7 +807,7 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) { /* Looks for a suitable "uplink", via black magic: an interface that is up and where the * default route with the highest priority points to. */ - HASHMAP_FOREACH(link, m->links) { + HASHMAP_FOREACH(link, m->links_by_index) { if (link == exclude) continue; @@ -1106,7 +1106,7 @@ static bool links_have_static_route(const Manager *manager, const Route *route, assert(manager); - HASHMAP_FOREACH(link, manager->links) { + HASHMAP_FOREACH(link, manager->links_by_index) { if (link == except) continue; @@ -1260,7 +1260,7 @@ static int route_add_and_setup_timer_one(Link *link, const Route *route, const M } else if (m && m->ifindex != 0 && m->ifindex != link->ifindex) { Link *link_gw; - r = link_get(link->manager, m->ifindex, &link_gw); + r = link_get_by_index(link->manager, m->ifindex, &link_gw); if (r < 0) return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex); @@ -1685,7 +1685,7 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { } else { Link *l; - HASHMAP_FOREACH(l, link->manager->links) { + HASHMAP_FOREACH(l, link->manager->links_by_index) { if (l->address_remove_messages > 0) return false; if (l->nexthop_remove_messages > 0) @@ -1720,7 +1720,7 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { m->ifindex = l->ifindex; } else if (m->ifindex > 0) { - if (link_get(link->manager, m->ifindex, &l) < 0) + if (link_get_by_index(link->manager, m->ifindex, &l) < 0) return false; } if (l && !link_is_ready_to_configure(l, true)) @@ -1805,7 +1805,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "rtnl: received multipath route with invalid ifindex, ignoring."); - r = link_get(manager, m->ifindex, &link); + r = link_get_by_index(manager, m->ifindex, &link); if (r < 0) { log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex); return 0; @@ -1898,7 +1898,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma return 0; } - r = link_get(m, ifindex, &link); + r = link_get_by_index(m, ifindex, &link); if (r < 0 || !link) { /* when enumerating we might be out of sync, but we will * get the route again, so just ignore it */ diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 411022a24f..af7e8a973c 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -607,7 +607,7 @@ static int links_have_routing_policy_rule(const Manager *m, const RoutingPolicyR assert(m); assert(rule); - HASHMAP_FOREACH(link, m->links) { + HASHMAP_FOREACH(link, m->links_by_index) { RoutingPolicyRule *link_rule; if (link == except) diff --git a/src/network/networkd-speed-meter.c b/src/network/networkd-speed-meter.c index 97204c89e9..6c3f97079e 100644 --- a/src/network/networkd-speed-meter.c +++ b/src/network/networkd-speed-meter.c @@ -26,9 +26,9 @@ static int process_message(Manager *manager, sd_netlink_message *message) { if (r < 0) return r; - link = hashmap_get(manager->links, INT_TO_PTR(ifindex)); - if (!link) - return -ENODEV; + r = link_get_by_index(manager, ifindex, &link); + if (r < 0) + return r; link->stats_old = link->stats_new; @@ -62,7 +62,7 @@ static int speed_meter_handler(sd_event_source *s, uint64_t usec, void *userdata manager->speed_meter_usec_old = manager->speed_meter_usec_new; manager->speed_meter_usec_new = usec_now; - HASHMAP_FOREACH(link, manager->links) + HASHMAP_FOREACH(link, manager->links_by_index) link->stats_updated = false; r = sd_rtnl_message_new_link(manager->rtnl, &req, RTM_GETLINK, 0); diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c index b9dab9b211..745aa4a995 100644 --- a/src/network/networkd-state-file.c +++ b/src/network/networkd-state-file.c @@ -121,7 +121,7 @@ int manager_save(Manager *m) { assert(m); assert(m->state_file); - HASHMAP_FOREACH(link, m->links) { + HASHMAP_FOREACH(link, m->links_by_index) { const struct in_addr *addresses; if (link->flags & IFF_LOOPBACK) diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h index 1e89a81b31..3213db553c 100644 --- a/src/systemd/sd-ipv4acd.h +++ b/src/systemd/sd-ipv4acd.h @@ -36,11 +36,13 @@ enum { typedef struct sd_ipv4acd sd_ipv4acd; typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata); +typedef int (*sd_ipv4acd_check_mac_callback_t)(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata); int sd_ipv4acd_detach_event(sd_ipv4acd *acd); int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority); int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address); int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata); +int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata); int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr); int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index); int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd); diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h index bf5596ab61..27263bc837 100644 --- a/src/systemd/sd-ipv4ll.h +++ b/src/systemd/sd-ipv4ll.h @@ -36,11 +36,13 @@ enum { typedef struct sd_ipv4ll sd_ipv4ll; typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata); +typedef int (*sd_ipv4ll_check_mac_callback_t)(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata); int sd_ipv4ll_detach_event(sd_ipv4ll *ll); int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority); int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata); +int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata); int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index); int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll); diff --git a/test/test-network/conf/25-address-dad-veth99.network b/test/test-network/conf/25-address-dad-veth99.network deleted file mode 100644 index 8e323d8395..0000000000 --- a/test/test-network/conf/25-address-dad-veth99.network +++ /dev/null @@ -1,8 +0,0 @@ -[Match] -Name=veth99 - -[Network] -IPv6AcceptRA=no - -[Address] -Address=192.168.100.10/24 diff --git a/test/test-network/conf/25-address-dad-veth-peer.network b/test/test-network/conf/25-address-ipv4acd-veth99.network similarity index 65% rename from test/test-network/conf/25-address-dad-veth-peer.network rename to test/test-network/conf/25-address-ipv4acd-veth99.network index 2827a751af..c8af4bafc8 100644 --- a/test/test-network/conf/25-address-dad-veth-peer.network +++ b/test/test-network/conf/25-address-ipv4acd-veth99.network @@ -1,9 +1,9 @@ [Match] -Name=veth-peer +Name=veth99 [Network] IPv6AcceptRA=no [Address] -Address=192.168.100.10/24 +Address=192.168.100.11/24 DuplicateAddressDetection=ipv4 diff --git a/test/test-network/conf/25-address-ipv4acd-veth99.network.d/conflict-address.conf b/test/test-network/conf/25-address-ipv4acd-veth99.network.d/conflict-address.conf new file mode 100644 index 0000000000..cec39e4bf2 --- /dev/null +++ b/test/test-network/conf/25-address-ipv4acd-veth99.network.d/conflict-address.conf @@ -0,0 +1,3 @@ +[Address] +Address=192.168.100.10/24 +DuplicateAddressDetection=ipv4 diff --git a/test/test-network/conf/dhcp-server-decline.network b/test/test-network/conf/dhcp-server-decline.network index 9009eaeb57..2c80e88736 100644 --- a/test/test-network/conf/dhcp-server-decline.network +++ b/test/test-network/conf/dhcp-server-decline.network @@ -3,12 +3,11 @@ Name=veth-peer [Network] Address=192.168.5.1/24 -Address=192.168.5.10/24 IPv6AcceptRA=false DHCPServer=yes [DHCPServer] PoolOffset=10 -PoolSize=1 +PoolSize=100 DNS=192.168.5.1 NTP=192.168.5.1 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 4cc0c64f90..4279b612d0 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1797,8 +1797,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '23-active-slave.network', '24-keep-configuration-static.network', '24-search-domain.network', - '25-address-dad-veth-peer.network', - '25-address-dad-veth99.network', + '25-address-ipv4acd-veth99.network', '25-address-link-section.network', '25-address-peer-ipv4.network', '25-address-static.network', @@ -1868,6 +1867,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): remove_routes(self.routes) remove_links(self.links) stop_networkd(show_logs=False) + call('ip netns del ns99', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) def tearDown(self): remove_blackhole_nexthops() @@ -1876,6 +1876,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): remove_links(self.links) remove_unit_from_networkd_path(self.units) stop_networkd(show_logs=True) + call('ip netns del ns99', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) def test_address_static(self): copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev') @@ -1929,19 +1930,33 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): for i in range(1,254): self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output) - def test_address_dad(self): - copy_unit_to_networkd_unit_path('25-address-dad-veth99.network', '25-address-dad-veth-peer.network', - '25-veth.netdev') + def test_address_ipv4acd(self): + check_output('ip netns add ns99') + check_output('ip link add veth99 type veth peer veth-peer') + check_output('ip link set veth-peer netns ns99') + check_output('ip link set veth99 up') + check_output('ip netns exec ns99 ip link set veth-peer up') + check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer') + + copy_unit_to_networkd_unit_path('25-address-ipv4acd-veth99.network', dropins=False) start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:degraded']) + self.wait_online(['veth99:routable']) output = check_output('ip -4 address show dev veth99') print(output) - self.assertRegex(output, '192.168.100.10/24') + self.assertNotIn('192.168.100.10/24', output) + self.assertIn('192.168.100.11/24', output) - output = check_output('ip -4 address show dev veth-peer') + copy_unit_to_networkd_unit_path('25-address-ipv4acd-veth99.network.d/conflict-address.conf') + run(*networkctl_cmd, 'reload', env=env) + time.sleep(1) + rc = call(*wait_online_cmd, '--timeout=10s', '--interface=veth99:routable', env=env) + self.assertTrue(rc == 1) + + output = check_output('ip -4 address show dev veth99') print(output) - self.assertNotRegex(output, '192.168.100.10/24') + self.assertNotIn('192.168.100.10/24', output) + self.assertIn('192.168.100.11/24', output) def test_address_peer_ipv4(self): # test for issue #17304 @@ -4619,9 +4634,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-decline.network', 'dhcp-client-decline.network') start_networkd() - self.wait_online(['veth-peer:carrier']) - rc = call(*wait_online_cmd, '--timeout=10s', '--interface=veth99:routable', env=env) - self.assertTrue(rc == 1) + self.wait_online(['veth99:routable', 'veth-peer:routable']) + + output = check_output('ip -4 address show dev veth99 scope global dynamic') + print(output) + self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): links = ['veth99']