diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-06-21 03:45:29 +0900 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-07-01 00:49:02 +0900 |
commit | 7f77917c0effe92d5fed52503bceddabcb4667ba (patch) | |
tree | 9bb08026ffe91dc06d6a374b134b603efea65e19 /src/libsystemd-network/sd-ipv4acd.c | |
parent | 5c35c13a4d6c6c457dd07e128c92abfd76550516 (diff) | |
download | systemd-7f77917c0effe92d5fed52503bceddabcb4667ba.tar.gz |
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.
Diffstat (limited to 'src/libsystemd-network/sd-ipv4acd.c')
-rw-r--r-- | src/libsystemd-network/sd-ipv4acd.c | 64 |
1 files changed, 48 insertions, 16 deletions
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index e1ddd3fcac..9a77a33317 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -18,6 +18,7 @@ #include "fd-util.h" #include "in-addr-util.h" #include "log-link.h" +#include "memory-util.h" #include "network-common.h" #include "random-util.h" #include "siphash24.h" @@ -72,7 +73,9 @@ struct sd_ipv4acd { sd_event *event; int event_priority; sd_ipv4acd_callback_t callback; - void* userdata; + void *userdata; + sd_ipv4acd_check_mac_callback_t check_mac_callback; + void *check_mac_userdata; }; #define log_ipv4acd_errno(acd, error, fmt, ...) \ @@ -208,18 +211,6 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u acd->event_priority, "ipv4acd-timer", true); } -static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) { - assert(acd); - assert(arp); - - /* see the BPF */ - if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0) - return true; - - /* the TPA matched instead of the SPA, this is not a conflict */ - return false; -} - static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) { sd_ipv4acd *acd = userdata; int r = 0; @@ -314,6 +305,39 @@ fail: return 0; } +static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) { + assert(acd); + assert(arp); + + /* RFC 5227 section 2.1.1. + * "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is + * being performed, where the packet's 'sender IP address' is the address being probed for, + * then the host MUST treat this address as being in use by some other host" */ + if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0) + return true; + + if (announced) + /* the TPA matched instead of SPA, this is not a conflict */ + return false; + + /* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and + * the packet's 'sender hardware address' is not the hardware address of any of the host's + * interfaces, then the host SHOULD similarly treat this as an address conflict" */ + if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST)) + return false; /* not ARP Request, ignoring. */ + if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0) + return false; /* not ARP Probe, ignoring. */ + if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0) + return false; /* target IP address does not match, BPF code is broken? */ + + if (acd->check_mac_callback && + acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0) + /* sender hardware is one of the host's interfaces, ignoring. */ + return true; + + return true; /* conflict! */ +} + static void ipv4acd_on_conflict(sd_ipv4acd *acd) { assert(acd); @@ -358,7 +382,7 @@ static int ipv4acd_on_packet( case IPV4ACD_STATE_ANNOUNCING: case IPV4ACD_STATE_RUNNING: - if (ipv4acd_arp_conflict(acd, &packet)) { + if (ipv4acd_arp_conflict(acd, &packet, true)) { usec_t ts; assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0); @@ -382,8 +406,8 @@ static int ipv4acd_on_packet( case IPV4ACD_STATE_WAITING_PROBE: case IPV4ACD_STATE_PROBING: case IPV4ACD_STATE_WAITING_ANNOUNCE: - /* BPF ensures this packet indicates a conflict */ - ipv4acd_on_conflict(acd); + if (ipv4acd_arp_conflict(acd, &packet, false)) + ipv4acd_on_conflict(acd); break; default: @@ -489,6 +513,14 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *use return 0; } +int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) { + assert_return(acd, -EINVAL); + + acd->check_mac_callback = cb; + acd->check_mac_userdata = userdata; + return 0; +} + int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) { int r; |