1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-10 01:17:44 +03:00

Merge pull request #19980 from yuwata/sd-ipv4acd-filter-all-hwaddr

network: IPv4LL and ACD fixes
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2021-06-30 22:22:47 +02:00 committed by GitHub
commit 807aa0b64b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 881 additions and 684 deletions

View File

@ -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);

View File

@ -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]

View File

@ -8,11 +8,13 @@
#include <netinet/if_ether.h>
#include "arp-util.h"
#include "ether-addr-util.h"
#include "fd-util.h"
#include "in-addr-util.h"
#include "unaligned.h"
#include "util.h"
int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) {
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
@ -46,13 +48,13 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
@ -61,15 +63,25 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
};
struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
.filter = (struct sock_filter*) filter
.len = ELEMENTSOF(filter),
.filter = (struct sock_filter*) filter,
};
assert(fd >= 0);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
return -errno;
return 0;
}
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) {
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_ARP),
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
_cleanup_close_ int s = -1;
int r;
@ -80,59 +92,57 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
if (s < 0)
return -errno;
r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
r = arp_update_filter(s, a, eth_mac);
if (r < 0)
return -errno;
return r;
r = bind(s, &link.sa, sizeof(link.ll));
if (r < 0)
if (bind(s, &link.sa, sizeof(link.ll)) < 0)
return -errno;
return TAKE_FD(s);
}
static int arp_send_packet(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha,
bool announce) {
int arp_send_packet(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha,
bool announce) {
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_ARP),
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
struct ether_arp arp = {
.ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
.ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
.ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
.ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
.ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
.ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
.ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
.ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
.ea_hdr.ar_pln = sizeof(struct in_addr), /* PLEN */
.ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
};
int r;
ssize_t n;
assert(fd >= 0);
assert(pa != 0);
assert(ifindex > 0);
assert(pa);
assert(in4_addr_is_set(pa));
assert(ha);
assert(!ether_addr_is_null(ha));
memcpy(&arp.arp_sha, ha, ETH_ALEN);
memcpy(&arp.arp_tpa, &pa, sizeof(pa));
memcpy(&arp.arp_tpa, pa, sizeof(struct in_addr));
if (announce)
memcpy(&arp.arp_spa, &pa, sizeof(pa));
memcpy(&arp.arp_spa, pa, sizeof(struct in_addr));
r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
if (r < 0)
n = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
if (n < 0)
return -errno;
if (n != sizeof(struct ether_arp))
return -EIO;
return 0;
}
int arp_send_probe(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha) {
return arp_send_packet(fd, ifindex, pa, ha, false);
}
int arp_send_announcement(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha) {
return arp_send_packet(fd, ifindex, pa, ha, true);
}

View File

@ -6,13 +6,31 @@
***/
#include <net/ethernet.h>
#include <netinet/in.h>
#include "socket-util.h"
#include "sparse-endian.h"
int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac);
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac);
int arp_send_probe(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha);
int arp_send_announcement(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha);
int arp_send_packet(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha,
bool announce);
static inline int arp_send_probe(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha) {
return arp_send_packet(fd, ifindex, pa, ha, false);
}
static inline int arp_send_announcement(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha) {
return arp_send_packet(fd, ifindex, pa, ha, true);
}

View File

@ -18,6 +18,7 @@
#include "fd-util.h"
#include "in-addr-util.h"
#include "log-link.h"
#include "memory-util.h"
#include "network-common.h"
#include "random-util.h"
#include "siphash24.h"
@ -64,7 +65,7 @@ struct sd_ipv4acd {
sd_event_source *timer_event_source;
usec_t defend_window;
be32_t address;
struct in_addr address;
/* External */
struct ether_addr mac_addr;
@ -72,7 +73,9 @@ struct sd_ipv4acd {
sd_event *event;
int event_priority;
sd_ipv4acd_callback_t callback;
void* userdata;
void *userdata;
sd_ipv4acd_check_mac_callback_t check_mac_callback;
void *check_mac_userdata;
};
#define log_ipv4acd_errno(acd, error, fmt, ...) \
@ -208,18 +211,6 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u
acd->event_priority, "ipv4acd-timer", true);
}
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
assert(acd);
assert(arp);
/* see the BPF */
if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
return true;
/* the TPA matched instead of the SPA, this is not a conflict */
return false;
}
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
sd_ipv4acd *acd = userdata;
int r = 0;
@ -229,38 +220,34 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
switch (acd->state) {
case IPV4ACD_STATE_STARTED:
acd->defend_window = 0;
ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
if (acd->n_conflict >= MAX_CONFLICTS) {
char ts[FORMAT_TIMESPAN_MAX];
log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
log_ipv4acd(acd, "Max conflicts reached, delaying by %s",
format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
if (r < 0)
goto fail;
} else {
} else
r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
if (r < 0)
goto fail;
}
if (r < 0)
goto fail;
break;
case IPV4ACD_STATE_WAITING_PROBE:
case IPV4ACD_STATE_PROBING:
/* Send a probe */
r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0) {
log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
goto fail;
} else {
_cleanup_free_ char *address = NULL;
union in_addr_union addr = { .in.s_addr = acd->address };
(void) in_addr_to_string(AF_INET, &addr, &address);
log_ipv4acd(acd, "Probing %s", strna(address));
}
log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
if (acd->n_iteration < PROBE_NUM - 2) {
ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
@ -286,12 +273,13 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
_fallthrough_;
case IPV4ACD_STATE_WAITING_ANNOUNCE:
/* Send announcement packet */
r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0) {
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
goto fail;
} else
log_ipv4acd(acd, "ANNOUNCE");
}
log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
@ -317,16 +305,45 @@ fail:
return 0;
}
static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
_cleanup_free_ char *address = NULL;
union in_addr_union addr = { .in.s_addr = acd->address };
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) {
assert(acd);
assert(arp);
/* RFC 5227 section 2.1.1.
* "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is
* being performed, where the packet's 'sender IP address' is the address being probed for,
* then the host MUST treat this address as being in use by some other host" */
if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0)
return true;
if (announced)
/* the TPA matched instead of SPA, this is not a conflict */
return false;
/* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and
* the packet's 'sender hardware address' is not the hardware address of any of the host's
* interfaces, then the host SHOULD similarly treat this as an address conflict" */
if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST))
return false; /* not ARP Request, ignoring. */
if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0)
return false; /* not ARP Probe, ignoring. */
if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0)
return false; /* target IP address does not match, BPF code is broken? */
if (acd->check_mac_callback &&
acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0)
/* sender hardware is one of the host's interfaces, ignoring. */
return true;
return true; /* conflict! */
}
static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
assert(acd);
acd->n_conflict++;
(void) in_addr_to_string(AF_INET, &addr, &address);
log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict);
ipv4acd_reset(acd);
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
@ -365,7 +382,7 @@ static int ipv4acd_on_packet(
case IPV4ACD_STATE_ANNOUNCING:
case IPV4ACD_STATE_RUNNING:
if (ipv4acd_arp_conflict(acd, &packet)) {
if (ipv4acd_arp_conflict(acd, &packet, true)) {
usec_t ts;
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
@ -373,12 +390,13 @@ static int ipv4acd_on_packet(
/* Defend address */
if (ts > acd->defend_window) {
acd->defend_window = ts + DEFEND_INTERVAL_USEC;
r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0) {
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
goto fail;
} else
log_ipv4acd(acd, "DEFEND");
}
log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
} else
ipv4acd_on_conflict(acd);
@ -388,8 +406,8 @@ static int ipv4acd_on_packet(
case IPV4ACD_STATE_WAITING_PROBE:
case IPV4ACD_STATE_PROBING:
case IPV4ACD_STATE_WAITING_ANNOUNCE:
/* BPF ensures this packet indicates a conflict */
ipv4acd_on_conflict(acd);
if (ipv4acd_arp_conflict(acd, &packet, false))
ipv4acd_on_conflict(acd);
break;
default:
@ -438,12 +456,24 @@ const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) {
}
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
int r;
assert_return(acd, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
assert_return(!ether_addr_is_null(addr), -EINVAL);
acd->mac_addr = *addr;
if (!sd_ipv4acd_is_running(acd))
return 0;
assert(acd->fd >= 0);
r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
if (r < 0) {
ipv4acd_reset(acd);
return r;
}
return 0;
}
@ -483,21 +513,51 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *use
return 0;
}
int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) {
assert_return(acd, -EINVAL);
acd->check_mac_callback = cb;
acd->check_mac_userdata = userdata;
return 0;
}
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
int r;
assert_return(acd, -EINVAL);
assert_return(address, -EINVAL);
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
assert_return(in4_addr_is_set(address), -EINVAL);
acd->address = address->s_addr;
if (in4_addr_equal(&acd->address, address))
return 0;
acd->address = *address;
if (!sd_ipv4acd_is_running(acd))
return 0;
assert(acd->fd >= 0);
r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
if (r < 0)
goto fail;
r = ipv4acd_set_next_wakeup(acd, 0, 0);
if (r < 0)
goto fail;
ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
return 0;
fail:
ipv4acd_reset(acd);
return r;
}
int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
assert_return(acd, -EINVAL);
assert_return(address, -EINVAL);
address->s_addr = acd->address;
*address = acd->address;
return 0;
}
@ -514,16 +574,15 @@ int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
assert_return(acd, -EINVAL);
assert_return(acd->event, -EINVAL);
assert_return(acd->ifindex > 0, -EINVAL);
assert_return(acd->address != 0, -EINVAL);
assert_return(in4_addr_is_set(&acd->address), -EINVAL);
assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0)
return r;
CLOSE_AND_REPLACE(acd->fd, r);
acd->defend_window = 0;
if (reset_conflicts)
acd->n_conflict = 0;

View File

@ -46,7 +46,10 @@ struct sd_ipv4ll {
be32_t claimed_address;
sd_ipv4ll_callback_t callback;
void* userdata;
void *userdata;
sd_ipv4ll_check_mac_callback_t check_mac_callback;
void *check_mac_userdata;
};
#define log_ipv4ll_errno(ll, error, fmt, ...) \
@ -60,7 +63,8 @@ struct sd_ipv4ll {
sd_ipv4ll_get_ifname(ll), \
0, fmt, ##__VA_ARGS__)
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata);
static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata);
static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) {
assert(ll);
@ -91,6 +95,10 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
if (r < 0)
return r;
r = sd_ipv4acd_set_check_mac_callback(ll->acd, ipv4ll_check_mac, ll);
if (r < 0)
return r;
*ret = TAKE_PTR(ll);
return 0;
@ -137,7 +145,7 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
assert_return(ll, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
assert_return(!ether_addr_is_null(addr), -EINVAL);
r = sd_ipv4acd_set_mac(ll->acd, addr);
if (r < 0)
@ -168,6 +176,15 @@ int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdat
return 0;
}
int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata) {
assert_return(ll, -EINVAL);
ll->check_mac_callback = cb;
ll->check_mac_userdata = userdata;
return 0;
}
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
assert_return(ll, -EINVAL);
assert_return(address, -EINVAL);
@ -351,3 +368,14 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
error:
ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
}
static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
sd_ipv4ll *ll = userdata;
assert(ll);
if (ll->check_mac_callback)
return ll->check_mac_callback(ll, mac, ll->check_mac_userdata);
return 0;
}

View File

@ -42,43 +42,31 @@ static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) {
}
}
static int arp_network_send_raw_socket(int fd, int ifindex,
const struct ether_arp *arp) {
assert_se(arp);
assert_se(ifindex > 0);
assert_se(fd >= 0);
int arp_send_packet(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha,
bool announce) {
if (send(fd, arp, sizeof(struct ether_arp), 0) < 0)
struct ether_arp ea = {};
assert_se(fd >= 0);
assert_se(ifindex > 0);
assert_se(pa);
assert_se(ha);
if (send(fd, &ea, sizeof(struct ether_arp), 0) < 0)
return -errno;
return 0;
}
int arp_send_probe(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha) {
struct ether_arp ea = {};
assert_se(fd >= 0);
assert_se(ifindex > 0);
assert_se(pa != 0);
assert_se(ha);
return arp_network_send_raw_socket(fd, ifindex, &ea);
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) {
return 0;
}
int arp_send_announcement(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha) {
struct ether_arp ea = {};
assert_se(fd >= 0);
assert_se(ifindex > 0);
assert_se(pa != 0);
assert_se(ha);
return arp_network_send_raw_socket(fd, ifindex, &ea);
}
int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) {
if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
return -errno;

View File

@ -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

View File

@ -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 */

View File

@ -9,6 +9,7 @@
#include "netlink-util.h"
#include "networkd-address-pool.h"
#include "networkd-address.h"
#include "networkd-ipv4acd.h"
#include "networkd-manager.h"
#include "networkd-network.h"
#include "networkd-queue.h"
@ -129,6 +130,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s
address->network = network;
address->section = TAKE_PTR(n);
address->is_static = true;
r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address);
if (r < 0)
@ -152,6 +154,7 @@ Address *address_free(Address *address) {
set_remove(address->link->addresses, address);
set_remove(address->link->addresses_foreign, address);
set_remove(address->link->addresses_ipv4acd, address);
set_remove(address->link->static_addresses, address);
if (address->link->dhcp_address == address)
address->link->dhcp_address = NULL;
@ -301,11 +304,23 @@ bool address_equal(const Address *a1, const Address *a2) {
return address_compare_func(a1, a2) == 0;
}
static int address_copy(Address *dest, const Address *src) {
int address_dup(const Address *src, Address **ret) {
_cleanup_(address_freep) Address *dest = NULL;
int r;
assert(dest);
assert(src);
assert(ret);
dest = newdup(Address, src, 1);
if (!dest)
return -ENOMEM;
/* clear all pointers */
dest->network = NULL;
dest->section = NULL;
dest->link = NULL;
dest->label = NULL;
dest->acd = NULL;
if (src->family == AF_INET) {
r = free_and_strdup(&dest->label, src->label);
@ -313,17 +328,7 @@ static int address_copy(Address *dest, const Address *src) {
return r;
}
dest->family = src->family;
dest->prefixlen = src->prefixlen;
dest->scope = src->scope;
dest->flags = src->flags;
dest->cinfo = src->cinfo;
dest->in_addr = src->in_addr;
dest->in_addr_peer = src->in_addr_peer;
if (address_may_have_broadcast(src))
dest->broadcast = src->broadcast;
dest->duplicate_address_detection = src->duplicate_address_detection;
*ret = TAKE_PTR(dest);
return 0;
}
@ -373,11 +378,7 @@ static int address_add_internal(Link *link, Set **addresses, const Address *in,
assert(addresses);
assert(in);
r = address_new(&address);
if (r < 0)
return r;
r = address_copy(address, in);
r = address_dup(in, &address);
if (r < 0)
return r;
@ -598,7 +599,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union
assert(address);
if (family == AF_INET) {
HASHMAP_FOREACH(link, manager->links)
HASHMAP_FOREACH(link, manager->links_by_index)
if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0)
return !check_ready || address_is_ready(a);
} else {
@ -611,7 +612,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union
tmp->family = family;
tmp->in_addr = *address;
HASHMAP_FOREACH(link, manager->links)
HASHMAP_FOREACH(link, manager->links_by_index)
if (address_get(link, tmp, &a) >= 0)
return !check_ready || address_is_ready(a);
}
@ -658,6 +659,7 @@ static void log_address_debug(const Address *address, const char *str, const Lin
}
static int address_set_netlink_message(const Address *address, sd_netlink_message *req, Link *link) {
uint32_t flags;
int r;
assert(address);
@ -669,14 +671,16 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag
return log_link_error_errno(link, r, "Could not set prefixlen: %m");
/* On remove, only IFA_F_MANAGETEMPADDR flag for IPv6 addresses are used. But anyway, set all
* flags here unconditionally. Without setting the flag, the template addresses generated by
* kernel will not be removed automatically when the main address is removed. */
r = sd_rtnl_message_addr_set_flags(req, address->flags & 0xff);
* flags except tentative flag here unconditionally. Without setting the flag, the template
* addresses generated by kernel will not be removed automatically when the main address is
* removed. */
flags = address->flags & ~IFA_F_TENTATIVE;
r = sd_rtnl_message_addr_set_flags(req, flags & 0xff);
if (r < 0)
return log_link_error_errno(link, r, "Could not set flags: %m");
if ((address->flags & ~0xff) != 0) {
r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags);
if ((flags & ~0xff) != 0) {
r = sd_netlink_message_append_u32(req, IFA_FLAGS, flags);
if (r < 0)
return log_link_error_errno(link, r, "Could not set extended flags: %m");
}
@ -955,11 +959,7 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
else if (original->family == AF_INET6)
in_addr.in6.s6_addr[15] |= 1;
r = address_new(&na);
if (r < 0)
return r;
r = address_copy(na, original);
r = address_dup(original, &na);
if (r < 0)
return r;
@ -997,17 +997,12 @@ int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m,
return 1;
}
static int ipv4_dad_configure(Address *address);
static int address_configure(
const Address *address,
Link *link,
link_netlink_message_handler_t callback,
Address **ret) {
link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
Address *acquired_address, *a;
bool update;
int r;
assert(address);
@ -1018,28 +1013,10 @@ static int address_configure(
assert(link->manager->rtnl);
assert(callback);
/* If this is a new address, then refuse adding more than the limit */
if (address_get(link, address, NULL) <= 0 &&
set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
"Too many addresses are configured, refusing: %m");
log_address_debug(address, "Configuring", link);
r = address_acquire(link, address, &acquired_address);
if (r < 0)
return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m");
if (acquired_address)
address = acquired_address;
update = address_get(link, address, NULL) >= 0;
log_address_debug(address, update ? "Updating" : "Configuring", link);
if (update)
r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
link->ifindex, address->family);
else
r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
link->ifindex, address->family);
r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
link->ifindex, address->family);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate RTM_NEWADDR message: %m");
@ -1075,31 +1052,11 @@ static int address_configure(
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFA_RT_PRIORITY attribute: %m");
r = address_add(link, address, &a);
if (r < 0)
return log_link_error_errno(link, r, "Could not add address: %m");
r = address_set_masquerade(a, true);
if (r < 0)
log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m");
r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link);
if (r < 0) {
(void) address_set_masquerade(a, false);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
}
link_ref(link);
if (FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) {
r = ipv4_dad_configure(a);
if (r < 0)
log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m");
}
if (ret)
*ret = a;
return 0;
}
@ -1151,12 +1108,45 @@ int link_request_address(
link_netlink_message_handler_t netlink_handler,
Request **ret) {
Address *acquired;
int r;
assert(link);
assert(address);
r = address_acquire(link, address, &acquired);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m");
if (r > 0) {
if (consume_object) {
address_free(address);
consume_object = false; /* address from pool is already managed by Link. */
}
address = acquired;
}
log_address_debug(address, "Requesting", link);
return link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object,
message_counter, netlink_handler, ret);
r = link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object,
message_counter, netlink_handler, ret);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to request address: %m");
return r;
}
int link_request_static_address(Link *link, Address *address, bool consume) {
Request *req;
int r;
assert(link);
assert(address);
r = link_request_address(link, address, consume, &link->static_address_messages,
static_address_handler, &req);
if (r <= 0)
return r;
req->after_configure = static_address_after_configure;
return 0;
}
int link_request_static_addresses(Link *link) {
@ -1170,21 +1160,13 @@ int link_request_static_addresses(Link *link) {
link->static_addresses_configured = false;
ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) {
Request *req;
r = link_request_address(link, a, false, &link->static_address_messages,
static_address_handler, &req);
r = link_request_static_address(link, a, false);
if (r < 0)
return r;
if (r == 0)
continue;
req->after_configure = static_address_after_configure;
}
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
_cleanup_(address_freep) Address *address = NULL;
Request *req;
if (!p->assign)
continue;
@ -1204,14 +1186,9 @@ int link_request_static_addresses(Link *link) {
address->family = AF_INET6;
address->route_metric = p->route_metric;
r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages,
static_address_handler, &req);
r = link_request_static_address(link, TAKE_PTR(address), true);
if (r < 0)
return r;
if (r == 0)
continue;
req->after_configure = static_address_after_configure;
}
if (in4_addr_is_set(&link->network->dhcp_server_address)) {
@ -1229,14 +1206,9 @@ int link_request_static_addresses(Link *link) {
/* The same address may be explicitly configured in [Address] or [Network] section.
* Configure the DHCP server address only when it is not. */
if (!link_is_static_address_configured(link, address)) {
Request *req;
r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages,
static_address_handler, &req);
r = link_request_static_address(link, TAKE_PTR(address), true);
if (r < 0)
return r;
if (r > 0)
req->after_configure = static_address_after_configure;
}
}
@ -1251,8 +1223,42 @@ int link_request_static_addresses(Link *link) {
return 0;
}
static int address_is_ready_to_configure(Link *link, const Address *address) {
int r;
assert(link);
assert(address);
if (!link_is_ready_to_configure(link, false))
return false;
if (link->address_remove_messages > 0)
return false;
if (address_get(link, address, NULL) >= 0)
return true;
/* If this is a new address, then refuse adding more than the limit */
if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
return log_link_warning_errno(link, SYNTHETIC_ERRNO(E2BIG),
"Too many addresses are configured, refusing: %m");
if (address->family == AF_INET &&
address->duplicate_address_detection & ADDRESS_FAMILY_IPV4 &&
link->hw_addr.length == ETH_ALEN &&
!ether_addr_is_null(&link->hw_addr.ether))
return ipv4acd_address_is_ready_to_configure(link, address);
r = address_add(link, address, NULL);
if (r < 0)
return log_link_warning_errno(link, r, "Could not add address: %m");;
return true;
}
int request_process_address(Request *req) {
Address *ret = NULL; /* avoid false maybe-uninitialized warning */
Address *a;
Link *link;
int r;
assert(req);
@ -1260,13 +1266,17 @@ int request_process_address(Request *req) {
assert(req->address);
assert(req->type == REQUEST_TYPE_ADDRESS);
if (!link_is_ready_to_configure(req->link, false))
return 0;
link = req->link;
if (req->link->address_remove_messages > 0)
return 0;
r = address_is_ready_to_configure(link, req->address);
if (r <= 0)
return r;
r = address_configure(req->address, req->link, req->netlink_handler, &ret);
r = address_get(link, req->address, &a);
if (r < 0)
return r;
r = address_configure(a, link, req->netlink_handler);
if (r < 0)
return r;
@ -1274,11 +1284,15 @@ int request_process_address(Request *req) {
req->message_counter = NULL;
if (req->after_configure) {
r = req->after_configure(req, ret);
r = req->after_configure(req, a);
if (r < 0)
return r;
}
r = address_set_masquerade(a, true);
if (r < 0)
log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m");
return 1;
}
@ -1319,7 +1333,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
return 0;
}
r = link_get(m, ifindex, &link);
r = link_get_by_index(m, ifindex, &link);
if (r < 0 || !link) {
/* when enumerating we might be out of sync, but we will get the address again, so just
* ignore it */
@ -1462,157 +1476,6 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
return 1;
}
static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
Address *address;
Link *link;
int r;
assert(acd);
assert(userdata);
address = (Address *) userdata;
link = address->link;
assert(address->family == AF_INET);
switch (event) {
case SD_IPV4ACD_EVENT_STOP:
log_link_debug(link, "Stopping ACD client...");
return;
case SD_IPV4ACD_EVENT_BIND:
log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
link_check_ready(link);
break;
case SD_IPV4ACD_EVENT_CONFLICT:
log_link_warning(link, "DAD conflict. Dropping address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
r = address_remove(address, link);
if (r < 0)
log_link_error_errno(link, r, "Failed to drop DAD conflicted address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
link_check_ready(link);
break;
default:
assert_not_reached("Invalid IPv4ACD event.");
}
(void) sd_ipv4acd_stop(acd);
return;
}
static int ipv4_dad_configure(Address *address) {
int r;
assert(address);
assert(address->link);
if (address->family != AF_INET)
return 0;
log_address_debug(address, "Starting IPv4ACD client. Probing", address->link);
if (!address->acd) {
r = sd_ipv4acd_new(&address->acd);
if (r < 0)
return r;
r = sd_ipv4acd_attach_event(address->acd, address->link->manager->event, 0);
if (r < 0)
return r;
}
r = sd_ipv4acd_set_ifindex(address->acd, address->link->ifindex);
if (r < 0)
return r;
r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.ether);
if (r < 0)
return r;
r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in);
if (r < 0)
return r;
r = sd_ipv4acd_set_callback(address->acd, static_address_on_acd, address);
if (r < 0)
return r;
return sd_ipv4acd_start(address->acd, true);
}
static int ipv4_dad_update_mac_one(Address *address) {
bool running;
int r;
assert(address);
if (!address->acd)
return 0;
running = sd_ipv4acd_is_running(address->acd);
r = sd_ipv4acd_stop(address->acd);
if (r < 0)
return r;
r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.ether);
if (r < 0)
return r;
if (running) {
r = sd_ipv4acd_start(address->acd, true);
if (r < 0)
return r;
}
return 0;
}
int ipv4_dad_update_mac(Link *link) {
Address *address;
int k, r = 0;
assert(link);
SET_FOREACH(address, link->addresses) {
k = ipv4_dad_update_mac_one(address);
if (k < 0 && r >= 0)
r = k;
}
return r;
}
int ipv4_dad_stop(Link *link) {
Address *address;
int k, r = 0;
assert(link);
SET_FOREACH(address, link->addresses) {
k = sd_ipv4acd_stop(address->acd);
if (k < 0 && r >= 0)
r = k;
}
return r;
}
void ipv4_dad_unref(Link *link) {
Address *address;
assert(link);
SET_FOREACH(address, link->addresses)
address->acd = sd_ipv4acd_unref(address->acd);
}
int config_parse_broadcast(
const char *unit,
const char *filename,

View File

@ -41,12 +41,13 @@ typedef struct Address {
bool scope_set:1;
bool ip_masquerade_done:1;
bool is_static:1; /* currently only used by IPv4ACD */
bool acd_announced:1;
AddressFamily duplicate_address_detection;
sd_ipv4acd *acd;
/* Called when address become ready */
address_ready_callback_t callback;
sd_ipv4acd *acd;
} Address;
int address_new(Address **ret);
@ -55,6 +56,7 @@ int address_get(Link *link, const Address *in, Address **ret);
int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
int address_remove(const Address *address, Link *link);
bool address_equal(const Address *a1, const Address *a2);
int address_dup(const Address *src, Address **ret);
bool address_is_ready(const Address *a);
void address_set_broadcast(Address *a);
@ -70,10 +72,6 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **
int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret);
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);
void ipv4_dad_unref(Link *link);
int ipv4_dad_stop(Link *link);
int ipv4_dad_update_mac(Link *link);
int link_request_address(
Link *link,
Address *address,
@ -81,6 +79,7 @@ int link_request_address(
unsigned *message_counter,
link_netlink_message_handler_t netlink_handler,
Request **ret);
int link_request_static_address(Link *link, Address *address, bool consume);
int link_request_static_addresses(Link *link);
int request_process_address(Request *req);

View File

@ -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))

View File

@ -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"))

View File

@ -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. */

View File

@ -14,6 +14,7 @@
#include "network-internal.h"
#include "networkd-address.h"
#include "networkd-dhcp4.h"
#include "networkd-ipv4acd.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@ -84,9 +85,6 @@ static int dhcp4_release_old_lease(Link *link) {
static void dhcp4_check_ready(Link *link) {
int r;
if (link->network->dhcp_send_decline && !link->dhcp4_address_bind)
return;
if (link->dhcp4_messages > 0) {
log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__);
return;
@ -738,7 +736,7 @@ static int dhcp4_remove_all(Link *link) {
return r;
}
static int dhcp_lease_lost(Link *link) {
int dhcp4_lease_lost(Link *link) {
int k, r = 0;
assert(link);
@ -748,7 +746,7 @@ static int dhcp_lease_lost(Link *link) {
link->dhcp4_configured = false;
/* dhcp_lease_lost() may be called during renewing IP address. */
/* dhcp4_lease_lost() may be called during renewing IP address. */
k = dhcp4_release_old_lease(link);
if (k < 0)
r = k;
@ -768,7 +766,23 @@ static int dhcp_lease_lost(Link *link) {
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
link_dirty(link);
(void) sd_ipv4acd_stop(link->dhcp_acd);
if (link->network->dhcp_send_decline) {
Address *a;
/* The acquired address may be still ARP probing and not configured. */
SET_FOREACH(a, link->addresses_ipv4acd)
if (!a->is_static && address_get(link, a, NULL) < 0) {
Request req = {
.link = link,
.address = a,
};
log_link_debug(link, "Canceling the request to configure DHCPv4 address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
request_drop(ordered_set_get(link->manager->request_queue, &req));
}
}
if (r < 0)
return r;
@ -780,149 +794,6 @@ static int dhcp_lease_lost(Link *link) {
return link_request_static_routes(link, true);
}
static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
struct in_addr address;
Link *link;
int r;
assert(acd);
assert(userdata);
link = userdata;
switch (event) {
case SD_IPV4ACD_EVENT_STOP:
log_link_debug(link, "Stopping ACD client for DHCPv4 address.");
return;
case SD_IPV4ACD_EVENT_BIND:
if (DEBUG_LOGGING) {
(void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
log_link_debug(link, "Successfully claimed DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address));
}
link->dhcp4_address_bind = true;
dhcp4_check_ready(link);
break;
case SD_IPV4ACD_EVENT_CONFLICT:
(void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
log_link_warning(link, "DAD conflict. Dropping DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address));
r = sd_dhcp_client_send_decline(link->dhcp_client);
if (r < 0)
log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m");
if (link->dhcp_lease) {
r = dhcp_lease_lost(link);
if (r < 0)
link_enter_failed(link);
}
break;
default:
assert_not_reached("Invalid IPv4ACD event.");
}
(void) sd_ipv4acd_stop(acd);
return;
}
static int dhcp4_configure_dad(Link *link) {
int r;
assert(link);
assert(link->manager);
assert(link->network);
if (!link->network->dhcp_send_decline)
return 0;
if (!link->dhcp_acd) {
r = sd_ipv4acd_new(&link->dhcp_acd);
if (r < 0)
return r;
r = sd_ipv4acd_attach_event(link->dhcp_acd, link->manager->event, 0);
if (r < 0)
return r;
}
r = sd_ipv4acd_set_ifindex(link->dhcp_acd, link->ifindex);
if (r < 0)
return r;
r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->hw_addr.ether);
if (r < 0)
return r;
return 0;
}
static int dhcp4_dad_update_mac(Link *link) {
bool running;
int r;
assert(link);
if (!link->dhcp_acd)
return 0;
running = sd_ipv4acd_is_running(link->dhcp_acd);
r = sd_ipv4acd_stop(link->dhcp_acd);
if (r < 0)
return r;
r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->hw_addr.ether);
if (r < 0)
return r;
if (running) {
r = sd_ipv4acd_start(link->dhcp_acd, true);
if (r < 0)
return r;
}
return 0;
}
static int dhcp4_start_acd(Link *link) {
struct in_addr addr, old;
int r;
if (!link->network->dhcp_send_decline)
return 0;
if (!link->dhcp_lease)
return 0;
(void) sd_ipv4acd_stop(link->dhcp_acd);
link->dhcp4_address_bind = false;
r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
if (r < 0)
return r;
r = sd_ipv4acd_get_address(link->dhcp_acd, &old);
if (r < 0)
return r;
r = sd_ipv4acd_set_address(link->dhcp_acd, &addr);
if (r < 0)
return r;
r = sd_ipv4acd_set_callback(link->dhcp_acd, dhcp_address_on_acd, link);
if (r < 0)
return r;
log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(addr));
return sd_ipv4acd_start(link->dhcp_acd, !in4_addr_equal(&addr, &old));
}
static int dhcp4_address_ready_callback(Address *address) {
assert(address);
@ -957,11 +828,6 @@ static int dhcp4_after_address_configure(Request *req, void *object) {
}
link->dhcp_address = address;
r = dhcp4_start_acd(link);
if (r < 0)
return log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCPv4 address: %m");
return 0;
}
@ -1057,6 +923,7 @@ static int dhcp4_request_address(Link *link, bool announce) {
addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr;
SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link));
addr->route_metric = link->network->dhcp_route_metric;
addr->duplicate_address_detection = link->network->dhcp_send_decline ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_NO;
if (address_get(link, addr, NULL) < 0)
link->dhcp4_configured = false;
@ -1180,7 +1047,7 @@ static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) {
r = dhcp_lease_acquired(client, link);
if (r < 0)
(void) dhcp_lease_lost(link);
(void) dhcp4_lease_lost(link);
return r;
}
@ -1276,7 +1143,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
r, "Failed to send DHCP RELEASE, ignoring: %m");
}
r = dhcp_lease_lost(link);
r = dhcp4_lease_lost(link);
if (r < 0) {
link_enter_failed(link);
return r;
@ -1291,7 +1158,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
}
if (link->dhcp_lease) {
r = dhcp_lease_lost(link);
r = dhcp4_lease_lost(link);
if (r < 0) {
link_enter_failed(link);
return r;
@ -1687,10 +1554,6 @@ int dhcp4_configure(Link *link) {
if (r < 0)
return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set initial DHCPv4 address: %m");
r = dhcp4_configure_dad(link);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to configure service type: %m");
return dhcp4_set_client_identifier(link);
}
@ -1708,15 +1571,7 @@ int dhcp4_update_mac(Link *link) {
if (r < 0)
return r;
r = dhcp4_set_client_identifier(link);
if (r < 0)
return r;
r = dhcp4_dad_update_mac(link);
if (r < 0)
return r;
return 0;
return dhcp4_set_client_identifier(link);
}
int dhcp4_start(Link *link) {

View File

@ -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);

View File

@ -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;

View File

@ -0,0 +1,290 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-dhcp-client.h"
#include "sd-ipv4acd.h"
#include "networkd-address.h"
#include "networkd-dhcp4.h"
#include "networkd-ipv4acd.h"
#include "networkd-link.h"
#include "networkd-manager.h"
static int static_address_on_stop(Link *link, Address *address) {
int r;
assert(link);
assert(address);
if (address_get(link, address, NULL) < 0)
return 0;
log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
r = address_remove(address, link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
return 0;
}
static int static_address_on_conflict(Link *link, Address *address) {
int r;
assert(link);
assert(address);
if (address_get(link, address, NULL) < 0) {
log_link_warning(link, "Cannot configure requested address "IPV4_ADDRESS_FMT_STR", as an address conflict is detected.",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
return 0;
}
log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict is detected.",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
r = address_remove(address, link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
return 0;
}
static int dhcp4_address_on_conflict(Link *link) {
int r;
assert(link);
assert(link->dhcp_client);
r = sd_dhcp_client_send_decline(link->dhcp_client);
if (r < 0)
log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m");
if (!link->dhcp_lease)
/* Unlikely, but during probing the address, the lease may be lost. */
return 0;
log_link_warning(link, "Dropping DHCPv4 lease, as an address conflict is detected.");
r = dhcp4_lease_lost(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m");
/* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
return 0;
}
static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
Address *address = userdata;
Link *link;
int r;
assert(acd);
assert(address);
assert(address->acd == acd);
assert(address->link);
assert(address->family == AF_INET);
link = address->link;
switch (event) {
case SD_IPV4ACD_EVENT_STOP:
if (is_static) {
r = static_address_on_stop(link, address);
if (r < 0)
link_enter_failed(link);
}
/* We have nothing to do for DHCPv4 lease here, as the dhcp client is already stopped
* when stopping the ipv4acd client. See link_stop_engines(). */
break;
case SD_IPV4ACD_EVENT_BIND:
log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
address->acd_announced = true;
break;
case SD_IPV4ACD_EVENT_CONFLICT:
if (is_static)
r = static_address_on_conflict(link, address);
else
r = dhcp4_address_on_conflict(link);
if (r < 0)
link_enter_failed(link);
break;
default:
assert_not_reached("Invalid IPv4ACD event.");
}
}
static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
on_acd(acd, event, userdata, true);
}
static void dhcp4_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
on_acd(acd, event, userdata, false);
}
static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
Manager *m = userdata;
struct hw_addr_data hw_addr;
assert(m);
assert(mac);
hw_addr = (struct hw_addr_data) {
.length = ETH_ALEN,
.ether = *mac,
};
return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
}
static int ipv4acd_configure(Link *link, const Address *a) {
_cleanup_(address_freep) Address *address = NULL;
int r;
assert(link);
assert(a);
assert(a->family == AF_INET);
log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
r = address_dup(a, &address);
if (r < 0)
return r;
r = set_ensure_put(&link->addresses_ipv4acd, &address_hash_ops, address);
if (r < 0)
return r;
if (r == 0)
return -EEXIST;
address->link = link;
r = sd_ipv4acd_new(&address->acd);
if (r < 0)
return r;
r = sd_ipv4acd_attach_event(address->acd, link->manager->event, 0);
if (r < 0)
return r;
r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex);
if (r < 0)
return r;
r = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
if (r < 0)
return r;
r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in);
if (r < 0)
return r;
r = sd_ipv4acd_set_callback(address->acd,
address->is_static ? static_address_on_acd : dhcp4_address_on_acd,
address);
if (r < 0)
return r;
r = sd_ipv4acd_set_check_mac_callback(address->acd, ipv4acd_check_mac, link->manager);
if (r < 0)
return r;
if (link_has_carrier(link)) {
r = sd_ipv4acd_start(address->acd, true);
if (r < 0)
return r;
}
TAKE_PTR(address);
return 0;
}
int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address) {
Address *acd_address;
int r;
acd_address = set_get(link->addresses_ipv4acd, address);
if (!acd_address) {
r = ipv4acd_configure(link, address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to configure IPv4ACD client: %m");
return false;
}
if (!acd_address->acd_announced)
return false;
r = set_ensure_put(&link->addresses, &address_hash_ops, acd_address);
if (r < 0)
return log_oom();
if (r == 0)
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EEXIST), "Address already exists.");
acd_address->flags |= IFA_F_TENTATIVE;
return true;
}
int ipv4acd_update_mac(Link *link) {
Address *address;
int k, r = 0;
assert(link);
if (link->hw_addr.length != ETH_ALEN)
return 0;
if (ether_addr_is_null(&link->hw_addr.ether))
return 0;
SET_FOREACH(address, link->addresses_ipv4acd) {
assert(address->acd);
k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
if (k < 0)
r = k;
}
if (r < 0)
link_enter_failed(link);
return r;
}
int ipv4acd_start(Link *link) {
Address *address;
int r;
assert(link);
SET_FOREACH(address, link->addresses_ipv4acd) {
if (sd_ipv4acd_is_running(address->acd))
continue;
r = sd_ipv4acd_start(address->acd, true);
if (r < 0)
return r;
}
return 0;
}
int ipv4acd_stop(Link *link) {
Address *address;
int k, r = 0;
assert(link);
SET_FOREACH(address, link->addresses_ipv4acd) {
k = sd_ipv4acd_stop(address->acd);
if (k < 0)
r = k;
}
return r;
}

View File

@ -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);

View File

@ -122,8 +122,10 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
}
r = sd_ipv4ll_restart(ll);
if (r < 0)
if (r < 0) {
log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
link_enter_failed(link);
}
break;
case SD_IPV4LL_EVENT_BIND:
r = ipv4ll_address_claimed(ll, link);
@ -139,6 +141,21 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
}
}
static int ipv4ll_check_mac(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata) {
Manager *m = userdata;
struct hw_addr_data hw_addr;
assert(m);
assert(mac);
hw_addr = (struct hw_addr_data) {
.length = ETH_ALEN,
.ether = *mac,
};
return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
}
int ipv4ll_configure(Link *link) {
uint64_t seed;
int r;
@ -178,35 +195,20 @@ int ipv4ll_configure(Link *link) {
if (r < 0)
return r;
return 0;
return sd_ipv4ll_set_check_mac_callback(link->ipv4ll, ipv4ll_check_mac, link->manager);
}
int ipv4ll_update_mac(Link *link) {
bool restart;
int r;
assert(link);
if (link->hw_addr.length != ETH_ALEN)
return 0;
if (ether_addr_is_null(&link->hw_addr.ether))
return 0;
if (!link->ipv4ll)
return 0;
restart = sd_ipv4ll_is_running(link->ipv4ll) > 0;
r = sd_ipv4ll_stop(link->ipv4ll);
if (r < 0)
return r;
r = sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether);
if (r < 0)
return r;
if (restart) {
r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
return r;
}
return 0;
return sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether);
}
int config_parse_ipv4ll(

View File

@ -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;

View File

@ -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;

View File

@ -33,6 +33,7 @@
#include "networkd-dhcp-server.h"
#include "networkd-dhcp4.h"
#include "networkd-dhcp6.h"
#include "networkd-ipv4acd.h"
#include "networkd-ipv4ll.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-link-bus.h"
@ -74,6 +75,12 @@ bool link_ipv4ll_enabled(Link *link) {
if (link->iftype == ARPHRD_CAN)
return false;
if (link->hw_addr.length != ETH_ALEN)
return false;
if (ether_addr_is_null(&link->hw_addr.ether))
return false;
if (STRPTR_IN_SET(link->kind,
"vrf", "wireguard", "ipip", "gre", "ip6gre","ip6tnl", "sit", "vti",
"vti6", "nlmon", "xfrm", "bareudp"))
@ -198,7 +205,6 @@ static void link_free_engines(Link *link) {
link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server);
link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client);
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
link->dhcp_acd = sd_ipv4acd_unref(link->dhcp_acd);
link->lldp = sd_lldp_unref(link->lldp);
link_lldp_emit_stop(link);
@ -210,8 +216,6 @@ static void link_free_engines(Link *link) {
link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
link->ndisc = sd_ndisc_unref(link->ndisc);
link->radv = sd_radv_unref(link->radv);
ipv4_dad_unref(link);
}
static Link *link_free(Link *link) {
@ -238,6 +242,7 @@ static Link *link_free(Link *link) {
link->addresses = set_free(link->addresses);
link->addresses_foreign = set_free(link->addresses_foreign);
link->addresses_ipv4acd = set_free(link->addresses_ipv4acd);
link->pool_addresses = set_free(link->pool_addresses);
link->static_addresses = set_free(link->static_addresses);
link->dhcp6_addresses = set_free(link->dhcp6_addresses);
@ -274,13 +279,13 @@ static Link *link_free(Link *link) {
DEFINE_TRIVIAL_REF_UNREF_FUNC(Link, link, link_free);
int link_get(Manager *m, int ifindex, Link **ret) {
int link_get_by_index(Manager *m, int ifindex, Link **ret) {
Link *link;
assert(m);
assert(ifindex > 0);
link = hashmap_get(m->links, INT_TO_PTR(ifindex));
link = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex));
if (!link)
return -ENODEV;
@ -304,6 +309,21 @@ int link_get_by_name(Manager *m, const char *ifname, Link **ret) {
return 0;
}
int link_get_by_hw_addr(Manager *m, const struct hw_addr_data *hw_addr, Link **ret) {
Link *link;
assert(m);
assert(hw_addr);
link = hashmap_get(m->links_by_hw_addr, hw_addr);
if (!link)
return -ENODEV;
if (ret)
*ret = link;
return 0;
}
int link_get_master(Link *link, Link **ret) {
assert(link);
assert(link->manager);
@ -312,7 +332,7 @@ int link_get_master(Link *link, Link **ret) {
if (link->master_ifindex <= 0 || link->master_ifindex == link->ifindex)
return -ENODEV;
return link_get(link->manager, link->master_ifindex, ret);
return link_get_by_index(link->manager, link->master_ifindex, ret);
}
void link_set_state(Link *link, LinkState state) {
@ -340,6 +360,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
bool keep_dhcp = may_keep_dhcp &&
link->network &&
!link->network->dhcp_send_decline && /* IPv4 ACD for the DHCPv4 address is running. */
(link->manager->restarting ||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
@ -349,10 +370,6 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m");
}
k = sd_ipv4acd_stop(link->dhcp_acd);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client for DHCPv4: %m");
k = sd_dhcp_server_stop(link->dhcp_server);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop DHCPv4 server: %m");
@ -365,7 +382,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m");
k = ipv4_dad_stop(link);
k = ipv4acd_stop(link);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m");
@ -647,6 +664,10 @@ static int link_acquire_dynamic_ipv4_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not start DHCP server: %m");
}
r = ipv4acd_start(link);
if (r < 0)
return log_link_warning_errno(link, r, "Could not start IPv4 ACD client: %m");
return 0;
}
@ -774,7 +795,7 @@ static int link_new_bound_by_list(Link *link) {
m = link->manager;
HASHMAP_FOREACH(carrier, m->links) {
HASHMAP_FOREACH(carrier, m->links_by_index) {
if (!carrier->network)
continue;
@ -813,7 +834,7 @@ static int link_new_bound_to_list(Link *link) {
m = link->manager;
HASHMAP_FOREACH(carrier, m->links) {
HASHMAP_FOREACH(carrier, m->links_by_index) {
if (strv_fnmatch(link->network->bind_carrier, carrier->ifname)) {
r = link_put_carrier(link, carrier, &link->bound_to_links);
if (r < 0)
@ -961,11 +982,14 @@ static Link *link_drop(Link *link) {
STRV_FOREACH(n, link->alternative_names)
hashmap_remove(link->manager->links_by_name, *n);
hashmap_remove(link->manager->links_by_name, link->ifname);
/* bonding master and its slaves have the same hardware address. */
if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link)
hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr);
/* The following must be called at last. */
assert_se(hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)) == link);
assert_se(hashmap_remove(link->manager->links_by_index, INT_TO_PTR(link->ifindex)) == link);
return link_unref(link);
}
@ -1471,7 +1495,7 @@ int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, voi
return 0;
}
r = link_get(m, ifindex, &link);
r = link_get_by_index(m, ifindex, &link);
if (r < 0) {
log_device_debug_errno(device, r, "Failed to get link from ifindex %i, ignoring: %m", ifindex);
return 0;
@ -1974,7 +1998,7 @@ static int link_update_master(Link *link, sd_netlink_message *message) {
}
static int link_update_hardware_address(Link *link, sd_netlink_message *message) {
struct hw_addr_data hw_addr;
struct hw_addr_data old;
int r;
assert(link);
@ -1984,18 +2008,38 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message)
if (r < 0 && r != -ENODATA)
return log_link_debug_errno(link, r, "rtnl: failed to read broadcast address: %m");
r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, &hw_addr);
old = link->hw_addr;
r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, &link->hw_addr);
if (r == -ENODATA)
return 0;
if (r < 0)
return log_link_warning_errno(link, r, "rtnl: failed to read hardware address: %m");
return log_link_debug_errno(link, r, "rtnl: failed to read hardware address: %m");
if (hw_addr_equal(&link->hw_addr, &hw_addr))
if (hw_addr_equal(&link->hw_addr, &old))
return 0;
link->hw_addr = hw_addr;
if (hw_addr_is_null(&old))
log_link_debug(link, "Saved hardware address: %s", HW_ADDR_TO_STR(&link->hw_addr));
else {
log_link_debug(link, "Hardware address is changed: %s → %s",
HW_ADDR_TO_STR(&old), HW_ADDR_TO_STR(&link->hw_addr));
log_link_debug(link, "Gained new hardware address: %s", HW_ADDR_TO_STR(&hw_addr));
if (hashmap_get(link->manager->links_by_hw_addr, &old) == link)
hashmap_remove(link->manager->links_by_hw_addr, &old);
}
if (!hw_addr_is_null(&link->hw_addr)) {
r = hashmap_ensure_put(&link->manager->links_by_hw_addr, &hw_addr_hash_ops, &link->hw_addr, link);
if (r == -EEXIST && streq_ptr(link->kind, "bond"))
/* bonding master and its slaves have the same hardware address. */
r = hashmap_replace(link->manager->links_by_hw_addr, &link->hw_addr, link);
if (r < 0)
log_link_debug_errno(link, r, "Failed to manage link by its new hardware address, ignoring: %m");
}
r = ipv4ll_update_mac(link);
if (r < 0)
return log_link_debug_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m");
r = ipv4ll_update_mac(link);
if (r < 0)
@ -2025,10 +2069,6 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message)
return log_link_debug_errno(link, r, "Could not update MAC address for LLDP: %m");
}
r = ipv4_dad_update_mac(link);
if (r < 0)
return log_link_debug_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m");
return 0;
}
@ -2257,7 +2297,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
.dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
};
r = hashmap_ensure_put(&manager->links, NULL, INT_TO_PTR(link->ifindex), link);
r = hashmap_ensure_put(&manager->links_by_index, NULL, INT_TO_PTR(link->ifindex), link);
if (r < 0)
return log_link_debug_errno(link, r, "Failed to store link into manager: %m");
@ -2323,7 +2363,7 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man
return 0;
}
(void) link_get(manager, ifindex, &link);
(void) link_get_by_index(manager, ifindex, &link);
(void) netdev_get(manager, name, &netdev);
switch (type) {

View File

@ -104,6 +104,7 @@ typedef struct Link {
Set *addresses;
Set *addresses_foreign;
Set *addresses_ipv4acd;
Set *pool_addresses;
Set *static_addresses;
Set *neighbors;
@ -119,11 +120,9 @@ typedef struct Link {
Set *dhcp_routes, *dhcp_routes_old;
char *lease_file;
unsigned dhcp4_messages;
sd_ipv4acd *dhcp_acd;
bool dhcp4_route_failed:1;
bool dhcp4_route_retrying:1;
bool dhcp4_configured:1;
bool dhcp4_address_bind:1;
sd_ipv4ll *ipv4ll;
bool ipv4ll_address_configured:1;
@ -218,8 +217,9 @@ Link *link_ref(Link *link);
DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
DEFINE_TRIVIAL_DESTRUCTOR(link_netlink_destroy_callback, Link, link_unref);
int link_get(Manager *m, int ifindex, Link **ret);
int link_get_by_index(Manager *m, int ifindex, Link **ret);
int link_get_by_name(Manager *m, const char *ifname, Link **ret);
int link_get_by_hw_addr(Manager *m, const struct hw_addr_data *hw_addr, Link **ret);
int link_get_master(Link *link, Link **ret);
int link_getlink_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);

View File

@ -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;

View File

@ -58,7 +58,7 @@ static int manager_reset_all(Manager *m) {
assert(m);
HASHMAP_FOREACH(link, m->links) {
HASHMAP_FOREACH(link, m->links_by_index) {
r = link_carrier_reset(link);
if (r < 0) {
log_link_warning_errno(link, r, "Could not reset carrier: %m");
@ -451,7 +451,7 @@ Manager* manager_free(Manager *m) {
free(m->state_file);
HASHMAP_FOREACH(link, m->links)
HASHMAP_FOREACH(link, m->links_by_index)
(void) link_stop_engines(link, true);
m->request_queue = ordered_set_free(m->request_queue);
@ -462,7 +462,8 @@ Manager* manager_free(Manager *m) {
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref);
m->links_by_name = hashmap_free(m->links_by_name);
m->links = hashmap_free_with_destructor(m->links, link_unref);
m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
m->links_by_index = hashmap_free_with_destructor(m->links_by_index, link_unref);
m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref);
@ -528,7 +529,7 @@ int manager_start(Manager *m) {
if (r < 0)
log_warning_errno(r, "Failed to update state file %s, ignoring: %m", m->state_file);
HASHMAP_FOREACH(link, m->links) {
HASHMAP_FOREACH(link, m->links_by_index) {
r = link_save(link);
if (r < 0)
log_link_warning_errno(link, r, "Failed to update link state file %s, ignoring: %m", link->state_file);

View File

@ -44,8 +44,9 @@ struct Manager {
LinkAddressState ipv6_address_state;
LinkOnlineState online_state;
Hashmap *links;
Hashmap *links_by_index;
Hashmap *links_by_name;
Hashmap *links_by_hw_addr;
Hashmap *netdevs;
OrderedHashmap *networks;
Hashmap *dhcp6_prefixes;

View File

@ -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. */

View File

@ -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);

View File

@ -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)--;

View File

@ -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 */

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -1,8 +0,0 @@
[Match]
Name=veth99
[Network]
IPv6AcceptRA=no
[Address]
Address=192.168.100.10/24

View File

@ -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

View File

@ -0,0 +1,3 @@
[Address]
Address=192.168.100.10/24
DuplicateAddressDetection=ipv4

View File

@ -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

View File

@ -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']