mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
Merge pull request #19980 from yuwata/sd-ipv4acd-filter-all-hwaddr
network: IPv4LL and ACD fixes
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
290
src/network/networkd-ipv4acd.c
Normal file
290
src/network/networkd-ipv4acd.c
Normal 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;
|
||||
}
|
||||
10
src/network/networkd-ipv4acd.h
Normal file
10
src/network/networkd-ipv4acd.h
Normal 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);
|
||||
@@ -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(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user