]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-ipv4acd: update condition of address conflict
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 20 Jun 2021 18:45:29 +0000 (03:45 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 30 Jun 2021 15:49:02 +0000 (00:49 +0900)
See RFC 5227 section 2.1.1.

This introduces a callback which intend to a library user, e.g.
networkd, checks whether the sender hardware address is a MAC address of
the host's intrerface or not.

src/libsystemd-network/sd-ipv4acd.c
src/systemd/sd-ipv4acd.h

index e1ddd3fcace1d84434a688a40de5da7219bee14f..9a77a333175a9be0458fdbb85fce88d6efdf7a6f 100644 (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"
@@ -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;
 
index 1e89a81b31516e0ca99907b5fdd94d33f4d27aa0..3213db553c8cae50639fef4affa9e1467e6e1a40 100644 (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);