aboutgitcodebugslistschat
diff options
context:
space:
mode:
authorJon Maloy <jmaloy@redhat.com>2025-10-23 21:29:28 -0400
committerStefano Brivio <sbrivio@redhat.com>2025-10-30 12:01:01 +0100
commit7917155ba259185bf6a7a83f5d09cad267a6951f (patch)
tree75528c0068fe2a713fb4d328280bb196cfc1a41b
parente456c02a0e84bedba8011a6c0b6659a7409ad14b (diff)
downloadpasst-7917155ba259185bf6a7a83f5d09cad267a6951f.tar
passt-7917155ba259185bf6a7a83f5d09cad267a6951f.tar.gz
passt-7917155ba259185bf6a7a83f5d09cad267a6951f.tar.bz2
passt-7917155ba259185bf6a7a83f5d09cad267a6951f.tar.lz
passt-7917155ba259185bf6a7a83f5d09cad267a6951f.tar.xz
passt-7917155ba259185bf6a7a83f5d09cad267a6951f.tar.zst
passt-7917155ba259185bf6a7a83f5d09cad267a6951f.zip
arp/ndp: send ARP announcement / unsolicited NA when neigbour entry added
ARP announcements and unsolicited NAs should be handled with caution because of the risk of malignant users emitting them to disturb network communication. There is however one case we where we know it is legitimate and safe for us to send out such messages: The one time we switch from using ctx->own_tap_mac to a MAC address received via the recently added neigbour subscription function. Later changes to the MAC address of a host in an existing entry cannot be fully trusted, so we abstain from doing it in such cases. When sending this type of messages, we notice that the guest accepts the update, but shortly later asks for a confirmation in the form of a regular ARP/NS request. This is responded to with the new value, and we have exactly the effect we wanted. This commit adds this functionality. Signed-off-by: Jon Maloy <jmaloy@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> [sbrivio: Fix "announcment" typo in arp_announce()] Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r--arp.c51
-rw-r--r--arp.h2
-rw-r--r--fwd.c10
-rw-r--r--ndp.c10
-rw-r--r--ndp.h1
5 files changed, 74 insertions, 0 deletions
diff --git a/arp.c b/arp.c
index b4a686f..33b03cf 100644
--- a/arp.c
+++ b/arp.c
@@ -150,3 +150,54 @@ void arp_send_init_req(const struct ctx *c)
debug("Sending initial ARP request for guest MAC address");
tap_send_single(c, &req, sizeof(req));
}
+
+/**
+ * arp_announce() - Send an ARP announcement for an IPv4 host
+ * @c: Execution context
+ * @ip: IPv4 address we announce as owned by @mac
+ * @mac: MAC address to advertise for @ip
+ */
+void arp_announce(const struct ctx *c, struct in_addr *ip,
+ const unsigned char *mac)
+{
+ char ip_str[INET_ADDRSTRLEN];
+ char mac_str[ETH_ADDRSTRLEN];
+ struct {
+ struct ethhdr eh;
+ struct arphdr ah;
+ struct arpmsg am;
+ } __attribute__((__packed__)) msg;
+
+ /* Ethernet header */
+ msg.eh.h_proto = htons(ETH_P_ARP);
+ memcpy(msg.eh.h_dest, MAC_BROADCAST, sizeof(msg.eh.h_dest));
+ memcpy(msg.eh.h_source, mac, sizeof(msg.eh.h_source));
+
+ /* ARP header */
+ msg.ah.ar_op = htons(ARPOP_REQUEST);
+ msg.ah.ar_hrd = htons(ARPHRD_ETHER);
+ msg.ah.ar_pro = htons(ETH_P_IP);
+ msg.ah.ar_hln = ETH_ALEN;
+ msg.ah.ar_pln = 4;
+
+ /* RFC5227, section 2.1.1, about Probe messages: "The client MUST fill
+ * in the 'sender hardware address' field of the ARP Request with the
+ * hardware address of the interface through which it is sending the
+ * packet. [...] The 'target hardware address' field is ignored and
+ * SHOULD be set to all zeroes."
+ *
+ * RFC5227, section 2.3: "An ARP Announcement is identical to the ARP
+ * Probe described above, except that now the sender and target IP
+ * addresses are both set to the host's newly selected IPv4 address."
+ */
+ memcpy(msg.am.sha, mac, sizeof(msg.am.sha));
+ memcpy(msg.am.sip, ip, sizeof(msg.am.sip));
+ memcpy(msg.am.tha, MAC_ZERO, sizeof(msg.am.tha));
+ memcpy(msg.am.tip, ip, sizeof(msg.am.tip));
+
+ inet_ntop(AF_INET, ip, ip_str, sizeof(ip_str));
+ eth_ntop(mac, mac_str, sizeof(mac_str));
+ debug("ARP announcement for %s / %s", ip_str, mac_str);
+
+ tap_send_single(c, &msg, sizeof(msg));
+}
diff --git a/arp.h b/arp.h
index d5ad0e1..4862e90 100644
--- a/arp.h
+++ b/arp.h
@@ -22,5 +22,7 @@ struct arpmsg {
int arp(const struct ctx *c, struct iov_tail *data);
void arp_send_init_req(const struct ctx *c);
+void arp_announce(const struct ctx *c, struct in_addr *ip,
+ const unsigned char *mac);
#endif /* ARP_H */
diff --git a/fwd.c b/fwd.c
index c6cbb6f..539b290 100644
--- a/fwd.c
+++ b/fwd.c
@@ -27,6 +27,8 @@
#include "lineread.h"
#include "flow_table.h"
#include "netlink.h"
+#include "arp.h"
+#include "ndp.h"
/* Ephemeral port range: values from RFC 6335 */
static in_port_t fwd_ephemeral_min = (1 << 15) + (1 << 14);
@@ -140,6 +142,14 @@ void fwd_neigh_table_update(const struct ctx *c, const union inany_addr *addr,
memcpy(&e->addr, addr, sizeof(*addr));
memcpy(e->mac, mac, ETH_ALEN);
e->permanent = permanent;
+
+ if (!memcmp(mac, c->our_tap_mac, ETH_ALEN))
+ return;
+
+ if (inany_v4(addr))
+ arp_announce(c, inany_v4(addr), e->mac);
+ else
+ ndp_unsolicited_na(c, &addr->a6);
}
/**
diff --git a/ndp.c b/ndp.c
index 7e2ae2a..430d420 100644
--- a/ndp.c
+++ b/ndp.c
@@ -221,6 +221,16 @@ static void ndp_na(const struct ctx *c, const struct in6_addr *dst,
}
/**
+ * ndp_unsolicited_na() - Send unsolicited NA
+ * @c: Execution context
+ * @addr: IPv6 address to advertise
+ */
+void ndp_unsolicited_na(const struct ctx *c, const struct in6_addr *addr)
+{
+ ndp_na(c, &in6addr_ll_all_nodes, addr);
+}
+
+/**
* ndp_ra() - Send an NDP Router Advertisement (RA) message
* @c: Execution context
* @dst: IPv6 address to send the RA to
diff --git a/ndp.h b/ndp.h
index 781ea86..56b756d 100644
--- a/ndp.h
+++ b/ndp.h
@@ -12,5 +12,6 @@ int ndp(const struct ctx *c, const struct in6_addr *saddr,
struct iov_tail *data);
void ndp_timer(const struct ctx *c, const struct timespec *now);
void ndp_send_init_req(const struct ctx *c);
+void ndp_unsolicited_na(const struct ctx *c, const struct in6_addr *addr);
#endif /* NDP_H */