mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 17:06:39 +09:00
sd-ipv4acd: update condition of address conflict
See RFC 5227 section 2.1.1. This introduces a callback which intend to a library user, e.g. networkd, checks whether the sender hardware address is a MAC address of the host's intrerface or not.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "log-link.h"
|
||||
#include "memory-util.h"
|
||||
#include "network-common.h"
|
||||
#include "random-util.h"
|
||||
#include "siphash24.h"
|
||||
@@ -72,7 +73,9 @@ struct sd_ipv4acd {
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
sd_ipv4acd_callback_t callback;
|
||||
void* userdata;
|
||||
void *userdata;
|
||||
sd_ipv4acd_check_mac_callback_t check_mac_callback;
|
||||
void *check_mac_userdata;
|
||||
};
|
||||
|
||||
#define log_ipv4acd_errno(acd, error, fmt, ...) \
|
||||
@@ -208,18 +211,6 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u
|
||||
acd->event_priority, "ipv4acd-timer", true);
|
||||
}
|
||||
|
||||
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
|
||||
assert(acd);
|
||||
assert(arp);
|
||||
|
||||
/* see the BPF */
|
||||
if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
|
||||
return true;
|
||||
|
||||
/* the TPA matched instead of the SPA, this is not a conflict */
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_ipv4acd *acd = userdata;
|
||||
int r = 0;
|
||||
@@ -314,6 +305,39 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) {
|
||||
assert(acd);
|
||||
assert(arp);
|
||||
|
||||
/* RFC 5227 section 2.1.1.
|
||||
* "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is
|
||||
* being performed, where the packet's 'sender IP address' is the address being probed for,
|
||||
* then the host MUST treat this address as being in use by some other host" */
|
||||
if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0)
|
||||
return true;
|
||||
|
||||
if (announced)
|
||||
/* the TPA matched instead of SPA, this is not a conflict */
|
||||
return false;
|
||||
|
||||
/* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and
|
||||
* the packet's 'sender hardware address' is not the hardware address of any of the host's
|
||||
* interfaces, then the host SHOULD similarly treat this as an address conflict" */
|
||||
if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST))
|
||||
return false; /* not ARP Request, ignoring. */
|
||||
if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0)
|
||||
return false; /* not ARP Probe, ignoring. */
|
||||
if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0)
|
||||
return false; /* target IP address does not match, BPF code is broken? */
|
||||
|
||||
if (acd->check_mac_callback &&
|
||||
acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0)
|
||||
/* sender hardware is one of the host's interfaces, ignoring. */
|
||||
return true;
|
||||
|
||||
return true; /* conflict! */
|
||||
}
|
||||
|
||||
static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
|
||||
assert(acd);
|
||||
|
||||
@@ -358,7 +382,7 @@ static int ipv4acd_on_packet(
|
||||
case IPV4ACD_STATE_ANNOUNCING:
|
||||
case IPV4ACD_STATE_RUNNING:
|
||||
|
||||
if (ipv4acd_arp_conflict(acd, &packet)) {
|
||||
if (ipv4acd_arp_conflict(acd, &packet, true)) {
|
||||
usec_t ts;
|
||||
|
||||
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
|
||||
@@ -382,8 +406,8 @@ static int ipv4acd_on_packet(
|
||||
case IPV4ACD_STATE_WAITING_PROBE:
|
||||
case IPV4ACD_STATE_PROBING:
|
||||
case IPV4ACD_STATE_WAITING_ANNOUNCE:
|
||||
/* BPF ensures this packet indicates a conflict */
|
||||
ipv4acd_on_conflict(acd);
|
||||
if (ipv4acd_arp_conflict(acd, &packet, false))
|
||||
ipv4acd_on_conflict(acd);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -489,6 +513,14 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *use
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) {
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
acd->check_mac_callback = cb;
|
||||
acd->check_mac_userdata = userdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user