From 9be2ba5e12c701a5d34abad6b954586da1e9b223 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 18 Jun 2021 15:12:25 +0900 Subject: [PATCH 01/30] sd-ipv4acd: use struct in_addr instead of be32_t --- src/libsystemd-network/sd-ipv4acd.c | 33 ++++++++++++----------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index b4af60741a..c8e3378386 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -64,7 +64,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; @@ -213,7 +213,7 @@ static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) { assert(arp); /* see the BPF */ - if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0) + if (memcmp(arp->arp_spa, &acd->address.s_addr, sizeof(acd->address.s_addr)) == 0) return true; /* the TPA matched instead of the SPA, this is not a conflict */ @@ -249,18 +249,14 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) 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.s_addr, &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,7 +282,7 @@ 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.s_addr, &acd->mac_addr); if (r < 0) { log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); goto fail; @@ -318,15 +314,11 @@ fail: } static void ipv4acd_on_conflict(sd_ipv4acd *acd) { - _cleanup_free_ char *address = NULL; - union in_addr_union addr = { .in.s_addr = acd->address }; - 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); @@ -373,7 +365,7 @@ 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.s_addr, &acd->mac_addr); if (r < 0) { log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); goto fail; @@ -486,9 +478,10 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *use int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) { assert_return(acd, -EINVAL); assert_return(address, -EINVAL); + assert_return(in4_addr_is_set(address), -EINVAL); assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); - acd->address = address->s_addr; + acd->address = *address; return 0; } @@ -497,7 +490,7 @@ 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,11 +507,11 @@ 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.s_addr, &acd->mac_addr); if (r < 0) return r; From 265b6d4e0c3af72c73f02e4196d4dd652d6dda50 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 01:42:58 +0900 Subject: [PATCH 02/30] sd-ipv4acd: include announced address in log message --- src/libsystemd-network/sd-ipv4acd.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index c8e3378386..549c18225f 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -286,8 +286,9 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) 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); @@ -369,8 +370,9 @@ static int ipv4acd_on_packet( 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); From 51d113512fd83c52e2c97f99511c318571ca1a74 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 18 Jun 2021 15:16:11 +0900 Subject: [PATCH 03/30] arp-util: align elements --- src/libsystemd-network/arp-util.c | 40 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index 327fb2ff3e..3c04a86e89 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -61,15 +61,15 @@ 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, }; 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; @@ -91,22 +91,26 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ return TAKE_FD(s); } -static int arp_send_packet(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha, - bool announce) { +static int arp_send_packet( + int fd, + int ifindex, + be32_t 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(be32_t), /* PLEN */ + .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ }; int r; From f6720fe62e879e7a73a0e95fa7260cb2af92b28b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 18 Jun 2021 15:17:11 +0900 Subject: [PATCH 04/30] arp-util: check sent message size --- src/libsystemd-network/arp-util.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index 3c04a86e89..d17ae1be04 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -112,7 +112,7 @@ static int arp_send_packet( .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */ .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ }; - int r; + ssize_t n; assert(fd >= 0); assert(pa != 0); @@ -124,9 +124,11 @@ static int arp_send_packet( if (announce) memcpy(&arp.arp_spa, &pa, sizeof(pa)); - 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; } From 504bf2b54a4d9f16caaf25425a4879a458aaa2b7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 18 Jun 2021 15:18:57 +0900 Subject: [PATCH 05/30] arp-util: check ifindex and mac address --- src/libsystemd-network/arp-util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index d17ae1be04..9786fccfbd 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -8,6 +8,7 @@ #include #include "arp-util.h" +#include "ether-addr-util.h" #include "fd-util.h" #include "unaligned.h" #include "util.h" @@ -115,8 +116,10 @@ static int arp_send_packet( ssize_t n; assert(fd >= 0); + assert(ifindex > 0); assert(pa != 0); assert(ha); + assert(!ether_addr_is_null(ha)); memcpy(&arp.arp_sha, ha, ETH_ALEN); memcpy(&arp.arp_tpa, &pa, sizeof(pa)); From 28ba416c99d04b46a31ff707d8cd5291e1ec5eae Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 18 Jun 2021 15:29:07 +0900 Subject: [PATCH 06/30] arp-util: shorten code a bit --- src/libsystemd-network/arp-util.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index 9786fccfbd..251a7a7989 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -73,7 +73,6 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, }; _cleanup_close_ int s = -1; - int r; assert(ifindex > 0); @@ -81,12 +80,10 @@ 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)); - if (r < 0) + if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) return -errno; - 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); From e1a3915b413a407f4f38a9ac5af05a36011154ae Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 18 Jun 2021 15:40:38 +0900 Subject: [PATCH 07/30] arp-util: make arp_send_{probe,announcement}() inline --- src/libsystemd-network/arp-util.c | 12 +------- src/libsystemd-network/arp-util.h | 24 ++++++++++++--- src/libsystemd-network/test-ipv4ll.c | 44 +++++++++------------------- 3 files changed, 35 insertions(+), 45 deletions(-) diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index 251a7a7989..149479fc07 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -89,7 +89,7 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ return TAKE_FD(s); } -static int arp_send_packet( +int arp_send_packet( int fd, int ifindex, be32_t pa, @@ -132,13 +132,3 @@ static int arp_send_packet( 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..db4afec6fa 100644 --- a/src/libsystemd-network/arp-util.h +++ b/src/libsystemd-network/arp-util.h @@ -12,7 +12,23 @@ int arp_network_bind_raw_socket(int index, be32_t address, 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, + be32_t pa, + const struct ether_addr *ha, + bool announce); +static inline 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); +} +static inline 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/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index 768595969b..c08039f3bb 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -42,42 +42,26 @@ 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, + be32_t 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 != 0); + 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_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) { if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0) return -errno; From ecad63f8503ea9f6264b34936b0ac8e7b6e718ac Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 18 Jun 2021 16:02:46 +0900 Subject: [PATCH 08/30] arp-util: use struct in_addr --- src/libsystemd-network/arp-util.c | 18 ++++++++++-------- src/libsystemd-network/arp-util.h | 9 +++++---- src/libsystemd-network/sd-ipv4acd.c | 10 +++++----- src/libsystemd-network/test-ipv4ll.c | 6 +++--- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index 149479fc07..cb15e51fd1 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -10,10 +10,11 @@ #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_network_bind_raw_socket(int ifindex, 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 ? */ @@ -47,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 */ @@ -92,7 +93,7 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ int arp_send_packet( int fd, int ifindex, - be32_t pa, + const struct in_addr *pa, const struct ether_addr *ha, bool announce) { @@ -107,22 +108,23 @@ int arp_send_packet( .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_pln = sizeof(struct in_addr), /* PLEN */ .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ }; ssize_t n; assert(fd >= 0); assert(ifindex > 0); - assert(pa != 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)); n = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); if (n < 0) diff --git a/src/libsystemd-network/arp-util.h b/src/libsystemd-network/arp-util.h index db4afec6fa..c711b57312 100644 --- a/src/libsystemd-network/arp-util.h +++ b/src/libsystemd-network/arp-util.h @@ -6,29 +6,30 @@ ***/ #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_network_bind_raw_socket(int index, const struct in_addr *a, const struct ether_addr *eth_mac); int arp_send_packet( int fd, int ifindex, - be32_t pa, + const struct in_addr *pa, const struct ether_addr *ha, bool announce); static inline int arp_send_probe( int fd, int ifindex, - be32_t pa, + 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, - be32_t pa, + 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 549c18225f..ee4f215002 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -213,7 +213,7 @@ static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) { assert(arp); /* see the BPF */ - if (memcmp(arp->arp_spa, &acd->address.s_addr, sizeof(acd->address.s_addr)) == 0) + 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 */ @@ -249,7 +249,7 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) case IPV4ACD_STATE_WAITING_PROBE: case IPV4ACD_STATE_PROBING: /* Send a probe */ - r = arp_send_probe(acd->fd, acd->ifindex, acd->address.s_addr, &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; @@ -282,7 +282,7 @@ 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.s_addr, &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; @@ -366,7 +366,7 @@ 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.s_addr, &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; @@ -513,7 +513,7 @@ int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) { 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.s_addr, &acd->mac_addr); + r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr); if (r < 0) return r; diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index c08039f3bb..1036b26412 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -45,7 +45,7 @@ static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { int arp_send_packet( int fd, int ifindex, - be32_t pa, + const struct in_addr *pa, const struct ether_addr *ha, bool announce) { @@ -53,7 +53,7 @@ int arp_send_packet( assert_se(fd >= 0); assert_se(ifindex > 0); - assert_se(pa != 0); + assert_se(pa); assert_se(ha); if (send(fd, &ea, sizeof(struct ether_arp), 0) < 0) @@ -62,7 +62,7 @@ int arp_send_packet( return 0; } -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; From d17ed573aae791ee440a1c84746868194896aa71 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 02:21:59 +0900 Subject: [PATCH 09/30] arp-util: split out logic of setting BPF code into a function --- src/libsystemd-network/arp-util.c | 18 +++++++++++++++--- src/libsystemd-network/arp-util.h | 3 ++- src/libsystemd-network/test-ipv4ll.c | 4 ++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index cb15e51fd1..d8d94ab5cb 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -14,7 +14,7 @@ #include "unaligned.h" #include "util.h" -int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, 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 ? */ @@ -66,6 +66,16 @@ int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const stru .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_protocol = htobe16(ETH_P_ARP), @@ -74,6 +84,7 @@ int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const stru .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, }; _cleanup_close_ int s = -1; + int r; assert(ifindex > 0); @@ -81,8 +92,9 @@ int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const stru if (s < 0) return -errno; - if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) - return -errno; + r = arp_update_filter(s, a, eth_mac); + if (r < 0) + return r; if (bind(s, &link.sa, sizeof(link.ll)) < 0) return -errno; diff --git a/src/libsystemd-network/arp-util.h b/src/libsystemd-network/arp-util.h index c711b57312..e8615a158d 100644 --- a/src/libsystemd-network/arp-util.h +++ b/src/libsystemd-network/arp-util.h @@ -11,7 +11,8 @@ #include "socket-util.h" #include "sparse-endian.h" -int arp_network_bind_raw_socket(int index, const struct in_addr *a, 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_packet( int fd, diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index 1036b26412..7985185b33 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -62,6 +62,10 @@ int arp_send_packet( return 0; } +int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) { + return 0; +} + 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; From fcb7345950beb1f21237df93e1e288c3e222493e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 02:29:46 +0900 Subject: [PATCH 10/30] sd-ipv4acd: allow to change MAC address without restarting sd-ipv4acd This also makes sd_ipv4acd_set_mac() refuses null MAC address. --- src/libsystemd-network/sd-ipv4acd.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index ee4f215002..d763ba8e62 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -432,12 +432,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; } From 3d817902c47db62a24f306c89fb77e782c6dee2a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 02:45:45 +0900 Subject: [PATCH 11/30] sd-ipv4acd: set defend window in timeout event and state is IPV4ACD_STATE_STARTED --- src/libsystemd-network/sd-ipv4acd.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index d763ba8e62..699e2baf6d 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -229,20 +229,20 @@ 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; @@ -530,7 +530,6 @@ int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) { return r; CLOSE_AND_REPLACE(acd->fd, r); - acd->defend_window = 0; if (reset_conflicts) acd->n_conflict = 0; From 5c35c13a4d6c6c457dd07e128c92abfd76550516 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 02:47:20 +0900 Subject: [PATCH 12/30] sd-ipv4acd: allow to change requesting address without restarting sd-ipv4acd --- src/libsystemd-network/sd-ipv4acd.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index 699e2baf6d..e1ddd3fcac 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -490,14 +490,35 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *use } 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(in4_addr_is_set(address), -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); + + 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) { From 7f77917c0effe92d5fed52503bceddabcb4667ba Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 03:45:29 +0900 Subject: [PATCH 13/30] sd-ipv4acd: update condition of address conflict See RFC 5227 section 2.1.1. This introduces a callback which intend to a library user, e.g. networkd, checks whether the sender hardware address is a MAC address of the host's intrerface or not. --- src/libsystemd-network/sd-ipv4acd.c | 64 +++++++++++++++++++++-------- src/systemd/sd-ipv4acd.h | 2 + 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index e1ddd3fcac..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" @@ -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; @@ -314,6 +305,39 @@ fail: return 0; } +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); @@ -358,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); @@ -382,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: @@ -489,6 +513,14 @@ 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; 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); From 9e5aa34fc8b3cfef00b9019c3ca231358ba4b6f3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 04:00:31 +0900 Subject: [PATCH 14/30] sd-ipv4ll: allow to set MAC address without stopping sd-ipv4ll engine --- src/libsystemd-network/sd-ipv4ll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 8053afee93..58a7fcb567 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -137,7 +137,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) From 1e59122d45de7f56e512fc2efb009159c54e255c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jun 2021 16:50:22 +0900 Subject: [PATCH 15/30] sd-ipv4ll: introduce sd_ipv4ll_set_check_mac_callback() --- src/libsystemd-network/sd-ipv4ll.c | 32 ++++++++++++++++++++++++++++-- src/systemd/sd-ipv4ll.h | 2 ++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 58a7fcb567..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; @@ -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/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); From 907d5ce4d4f74abb121eb8979ee4fd0a6d8f501a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jun 2021 15:55:36 +0900 Subject: [PATCH 16/30] ether-addr-util: introduce hw_addr_hash_ops --- src/basic/ether-addr-util.c | 10 ++++++++++ src/basic/ether-addr-util.h | 2 ++ 2 files changed, 12 insertions(+) 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] From 6eab614db805504cec3af0c1c7b21ae6275a89a6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jun 2021 15:58:30 +0900 Subject: [PATCH 17/30] network: rename Manager::links -> Manager::links_by_index This also renames link_get() -> link_get_by_index(). --- src/network/networkd-address-pool.c | 2 +- src/network/networkd-address.c | 6 +++--- src/network/networkd-bridge-fdb.c | 2 +- src/network/networkd-bridge-mdb.c | 2 +- src/network/networkd-dhcp-server.c | 2 +- src/network/networkd-dhcp6.c | 12 ++++++------ src/network/networkd-json.c | 4 ++-- src/network/networkd-link-bus.c | 6 +++--- src/network/networkd-link.c | 18 +++++++++--------- src/network/networkd-link.h | 2 +- src/network/networkd-manager-bus.c | 12 ++++++------ src/network/networkd-manager.c | 8 ++++---- src/network/networkd-manager.h | 2 +- src/network/networkd-neighbor.c | 2 +- src/network/networkd-nexthop.c | 6 +++--- src/network/networkd-route.c | 16 ++++++++-------- src/network/networkd-routing-policy-rule.c | 2 +- src/network/networkd-speed-meter.c | 8 ++++---- src/network/networkd-state-file.c | 2 +- 19 files changed, 57 insertions(+), 57 deletions(-) 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..2b0967d914 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -598,7 +598,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 +611,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); } @@ -1319,7 +1319,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 */ 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-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-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..c3be8aa5c2 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -274,13 +274,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; @@ -312,7 +312,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) { @@ -774,7 +774,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 +813,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) @@ -965,7 +965,7 @@ static Link *link_drop(Link *link) { hashmap_remove(link->manager->links_by_name, link->ifname); /* 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 +1471,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; @@ -2257,7 +2257,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 +2323,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..c133927fd6 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -218,7 +218,7 @@ 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_master(Link *link, Link **ret); 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..b95742f490 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,7 @@ 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_index = hashmap_free_with_destructor(m->links_by_index, link_unref); m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref); @@ -528,7 +528,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..cef5eb889b 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -44,7 +44,7 @@ struct Manager { LinkAddressState ipv6_address_state; LinkOnlineState online_state; - Hashmap *links; + Hashmap *links_by_index; Hashmap *links_by_name; Hashmap *netdevs; OrderedHashmap *networks; 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-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) From fe321d457c118be5d1640b586135a69d1db9270a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jun 2021 16:25:48 +0900 Subject: [PATCH 18/30] network: introduce link_get_by_hw_addr() --- src/network/networkd-link.c | 48 +++++++++++++++++++++++++++++----- src/network/networkd-link.h | 1 + src/network/networkd-manager.c | 1 + src/network/networkd-manager.h | 1 + 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index c3be8aa5c2..378420d9d3 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -304,6 +304,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); @@ -961,9 +976,12 @@ 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_by_index, INT_TO_PTR(link->ifindex)) == link); return link_unref(link); @@ -1974,7 +1992,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 +2002,34 @@ 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) diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index c133927fd6..28c2821ee6 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -220,6 +220,7 @@ DEFINE_TRIVIAL_DESTRUCTOR(link_netlink_destroy_callback, Link, link_unref); 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.c b/src/network/networkd-manager.c index b95742f490..374d27bef3 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -462,6 +462,7 @@ 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_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); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index cef5eb889b..4ee48f3468 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -46,6 +46,7 @@ struct Manager { Hashmap *links_by_index; Hashmap *links_by_name; + Hashmap *links_by_hw_addr; Hashmap *netdevs; OrderedHashmap *networks; Hashmap *dhcp6_prefixes; From 89d8ed9932fe368a07495a9398bb4cabcf4add20 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 04:30:56 +0900 Subject: [PATCH 19/30] network: ipv4ll: make link enter failed state on failure of restarting ipv4ll engine --- src/network/networkd-ipv4ll.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index a2a76d848c..e1dbbabf8f 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); From a1e35fca46681eab34608377b04d77f68128483a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 22 Jun 2021 04:11:15 +0900 Subject: [PATCH 20/30] network: ipv4ll: refuse to configure IPv4LL address on interface where the length of the hardware address is not ETH_ALEN Currently, sd-ipv4acd assumes hardware address is ETH_ALEN. --- src/network/networkd-link.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 378420d9d3..ba1bd283df 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -74,6 +74,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")) From f8d6397ac21bc95989b6220eeb914af8f8d120d3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 04:28:09 +0900 Subject: [PATCH 21/30] network: ipv4ll: reset MAC address without stopping sd-ipv4ll engine --- src/network/networkd-ipv4ll.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index e1dbbabf8f..be6fedf90f 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -184,31 +184,16 @@ int ipv4ll_configure(Link *link) { } 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( From 99b466693e10863655150bef52a9ed493cdfbe54 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Jun 2021 16:42:22 +0900 Subject: [PATCH 22/30] network: introduce address_dup() and replace address_copy() with it --- src/network/networkd-address.c | 40 +++++++++++++++------------------- src/network/networkd-address.h | 1 + 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 2b0967d914..3a701b7dc9 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -301,11 +301,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 +325,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 +375,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; @@ -955,11 +953,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; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 56251089d1..9de2fae2df 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -55,6 +55,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); From 8c29f484d5ec017e7be49339170b9b256e9d1ee5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 22 Jun 2021 14:52:47 +0900 Subject: [PATCH 23/30] network: introduce link_request_static_address() --- src/network/networkd-address.c | 40 ++++++++++++++++------------------ src/network/networkd-address.h | 1 + 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 3a701b7dc9..2756ce74f8 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -1153,6 +1153,22 @@ int link_request_address( message_counter, netlink_handler, ret); } +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) { Address *a; Prefix *p; @@ -1164,21 +1180,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; @@ -1198,14 +1206,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)) { @@ -1223,14 +1226,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; } } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 9de2fae2df..a24320fca2 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -82,6 +82,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); From d5e3b3eb8c66b0240431950a212597561d06fb77 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 22 Jun 2021 19:36:39 +0900 Subject: [PATCH 24/30] network: address: do not send tentative flag Currently, networkd does not set tentative flag on create, and kernel ignore the flag on remove. So, this commit does not change any current behaviour. This is just a preparation for later commits. --- src/network/networkd-address.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 2756ce74f8..700475f27b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -656,6 +656,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); @@ -667,14 +668,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"); } From 9155da08ee0afacde18ecd4cc4cfc2a9280052dc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 22 Jun 2021 19:48:55 +0900 Subject: [PATCH 25/30] network: rename dhcp_lease_lost() -> dhcp4_lease_lost() and expose it A preparation for later commits. --- src/network/networkd-dhcp4.c | 12 ++++++------ src/network/networkd-dhcp4.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 878b306c1f..1aaeda98e2 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -738,7 +738,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 +748,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; @@ -813,7 +813,7 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m"); if (link->dhcp_lease) { - r = dhcp_lease_lost(link); + r = dhcp4_lease_lost(link); if (r < 0) link_enter_failed(link); } @@ -1180,7 +1180,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 +1276,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 +1291,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; 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); From c0c04a4b222f27ae1ebb10748b97f35abcc7943a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 22 Jun 2021 23:57:57 +0900 Subject: [PATCH 26/30] network: acquire address on request configure rather than on configure A preparation for later commits. --- src/network/networkd-address.c | 128 +++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 55 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 700475f27b..a8f081ec81 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -999,12 +999,9 @@ 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); @@ -1015,28 +1012,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"); @@ -1072,31 +1051,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; } @@ -1148,12 +1107,29 @@ 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) { @@ -1246,8 +1222,36 @@ 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"); + + 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); @@ -1255,13 +1259,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; @@ -1269,11 +1277,21 @@ 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"); + + if (FLAGS_SET(a->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"); + } + return 1; } From 475ec3346f0b8b311cb9275523e322f2b1b3157c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 1 Jul 2021 00:45:47 +0900 Subject: [PATCH 27/30] network: make request_drop() accept NULL --- src/network/networkd-queue.c | 3 +++ 1 file changed, 3 insertions(+) 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)--; From 76a86ffdbee2dd9ef0f2b5338e14eb6ba7671456 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 22 Jun 2021 04:08:19 +0900 Subject: [PATCH 28/30] network: ipv4acd: first probe address and then assign it Previously, if IPv4 ACD is enabled on an address, then we first assign the address, and start sd-ipv4acd daemon for the address. This is not only RFC incompliant, but also the address is always dropped, as the daemon always considers the address is conflicted. This commit makes networkd first starts sd-ipv4acd daemon to probe the address, and then the address is configured if no conflict is detected. Fixes #17235. --- src/network/meson.build | 2 + src/network/networkd-address.c | 168 ++------------------ src/network/networkd-address.h | 9 +- src/network/networkd-dhcp4.c | 185 +++------------------- src/network/networkd-ipv4acd.c | 271 +++++++++++++++++++++++++++++++++ src/network/networkd-ipv4acd.h | 10 ++ src/network/networkd-link.c | 24 +-- src/network/networkd-link.h | 3 +- 8 files changed, 328 insertions(+), 344 deletions(-) create mode 100644 src/network/networkd-ipv4acd.c create mode 100644 src/network/networkd-ipv4acd.h 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.c b/src/network/networkd-address.c index a8f081ec81..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; @@ -994,8 +997,6 @@ 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, @@ -1242,6 +1243,12 @@ static int address_is_ready_to_configure(Link *link, const Address *address) { 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");; @@ -1286,12 +1293,6 @@ int request_process_address(Request *req) { if (r < 0) log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m"); - if (FLAGS_SET(a->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"); - } - return 1; } @@ -1475,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 a24320fca2..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); @@ -71,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, diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 1aaeda98e2..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; @@ -768,7 +766,23 @@ int dhcp4_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 @@ int dhcp4_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 = dhcp4_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; @@ -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-ipv4acd.c b/src/network/networkd-ipv4acd.c new file mode 100644 index 0000000000..06dfa2d540 --- /dev/null +++ b/src/network/networkd-ipv4acd.c @@ -0,0 +1,271 @@ +/* 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_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; + + 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-link.c b/src/network/networkd-link.c index ba1bd283df..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" @@ -204,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); @@ -216,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) { @@ -244,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); @@ -361,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)); @@ -370,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"); @@ -386,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"); @@ -668,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; } @@ -2037,6 +2037,10 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message) 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) return log_link_debug_errno(link, r, "Could not update MAC address in IPv4LL client: %m"); @@ -2065,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; } diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 28c2821ee6..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; From d7ab6ef098f9d0eb01bfba5ed3bd2a1e3a960156 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jun 2021 23:33:11 +0900 Subject: [PATCH 29/30] network: introduce callback fucntions to check the sender MAC address for IPv4ACD and IPv4LL Fixes #12145. --- src/network/networkd-ipv4acd.c | 19 +++++++++++++++++++ src/network/networkd-ipv4ll.c | 17 ++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/network/networkd-ipv4acd.c b/src/network/networkd-ipv4acd.c index 06dfa2d540..ea902d9766 100644 --- a/src/network/networkd-ipv4acd.c +++ b/src/network/networkd-ipv4acd.c @@ -129,6 +129,21 @@ 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; @@ -177,6 +192,10 @@ static int ipv4acd_configure(Link *link, const Address *a) { 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) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index be6fedf90f..83ed3726f7 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -141,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; @@ -180,7 +195,7 @@ 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) { From 449244312499eff837b48638db55f65007db500b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 22 Jun 2021 15:32:57 +0900 Subject: [PATCH 30/30] test-network: update tests for ipv4acd --- .../conf/25-address-dad-veth99.network | 8 ---- ...work => 25-address-ipv4acd-veth99.network} | 4 +- .../conflict-address.conf | 3 ++ .../conf/dhcp-server-decline.network | 3 +- test/test-network/systemd-networkd-tests.py | 41 +++++++++++++------ 5 files changed, 35 insertions(+), 24 deletions(-) delete mode 100644 test/test-network/conf/25-address-dad-veth99.network rename test/test-network/conf/{25-address-dad-veth-peer.network => 25-address-ipv4acd-veth99.network} (65%) create mode 100644 test/test-network/conf/25-address-ipv4acd-veth99.network.d/conflict-address.conf 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']