public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
c57e0471aee8399aefb1d8a8b5bd716435c9e28e blob 5830 bytes (raw)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
 
// SPDX-License-Identifier: GPL-2.0-or-later

/* PASST - Plug A Simple Socket Transport
 *  for qemu/UNIX domain socket mode
 *
 * PASTA - Pack A Subtle Tap Abstraction
 *  for network namespace/tap device mode
 *
 * arp.c - ARP implementation
 *
 * Copyright (c) 2020-2021 Red Hat GmbH
 * Author: Stefano Brivio <sbrivio@redhat.com>
 */

#include <arpa/inet.h>
#include <limits.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/if_ether.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#include "util.h"
#include "log.h"
#include "arp.h"
#include "dhcp.h"
#include "passt.h"
#include "tap.h"

/**
 * ignore_arp() - Check if we should ignore this ARP message
 * @c:		Execution context
 * @ah:		ARP header
 * @am:		ARP message
 *
 * Return: true if the ARP message should be ignored, false otherwise
 */
static bool ignore_arp(const struct ctx *c,
		       const struct arphdr *ah, const struct arpmsg *am)
{
	if (ah->ar_hrd != htons(ARPHRD_ETHER)	||
	    ah->ar_pro != htons(ETH_P_IP)	||
	    ah->ar_hln != ETH_ALEN		||
	    ah->ar_pln != 4			||
	    ah->ar_op  != htons(ARPOP_REQUEST))
		return true;

	/* Discard announcements, but not 0.0.0.0 "probes" */
	if (memcmp(am->sip, &in4addr_any, sizeof(am->sip)) &&
	    !memcmp(am->sip, am->tip, sizeof(am->sip)))
		return true;

	/* Don't resolve the guest's assigned address, either. */
	if (!memcmp(am->tip, &c->ip4.addr, sizeof(am->tip)))
		return true;

	return false;
}

/**
 * arp() - Check if this is a supported ARP message, reply as needed
 * @c:		Execution context
 * @queue:	Queue to use to send the reply
 * @data:	Single packet with Ethernet buffer
 *
 * Return: 1 if handled, -1 on failure
 */
int arp(const struct ctx *c, int queue, struct iov_tail *data)
{
	union inany_addr tgt;
	struct {
		struct ethhdr eh;
		struct arphdr ah;
		struct arpmsg am;
	} __attribute__((__packed__)) resp;
	struct arphdr ah_storage;
	struct ethhdr eh_storage;
	struct arpmsg am_storage;
	const struct ethhdr *eh;
	const struct arphdr *ah;
	const struct arpmsg *am;

	eh = IOV_REMOVE_HEADER(data, eh_storage);
	ah = IOV_REMOVE_HEADER(data, ah_storage);
	am = IOV_REMOVE_HEADER(data, am_storage);
	if (!eh || !ah || !am)
		return -1;

	if (ignore_arp(c, ah, am))
		return 1;

	/* Ethernet header */
	resp.eh.h_proto = htons(ETH_P_ARP);
	memcpy(resp.eh.h_dest, eh->h_source, sizeof(resp.eh.h_dest));
	memcpy(resp.eh.h_source, c->our_tap_mac, sizeof(resp.eh.h_source));

	/* ARP header */
	resp.ah.ar_op = htons(ARPOP_REPLY);
	resp.ah.ar_hrd = ah->ar_hrd;
	resp.ah.ar_pro = ah->ar_pro;
	resp.ah.ar_hln = ah->ar_hln;
	resp.ah.ar_pln = ah->ar_pln;

	/* MAC address to return in ARP message */
	inany_from_af(&tgt, AF_INET, am->tip);
	fwd_neigh_mac_get(c, &tgt, resp.am.sha);

	/* Rest of ARP message */
	memcpy(resp.am.sip,		am->tip,	sizeof(resp.am.sip));
	memcpy(resp.am.tha,		am->sha,	sizeof(resp.am.tha));
	memcpy(resp.am.tip,		am->sip,	sizeof(resp.am.tip));

	tap_send_single(c, queue, &resp, sizeof(resp));

	return 1;
}

/**
 * arp_send_init_req() - Send initial ARP request to retrieve guest MAC address
 * @c:		Execution context
 */
void arp_send_init_req(const struct ctx *c)
{
	struct {
		struct ethhdr eh;
		struct arphdr ah;
		struct arpmsg am;
	} __attribute__((__packed__)) req;

	/* Ethernet header */
	req.eh.h_proto = htons(ETH_P_ARP);
	memcpy(req.eh.h_dest, MAC_BROADCAST, sizeof(req.eh.h_dest));
	memcpy(req.eh.h_source, c->our_tap_mac, sizeof(req.eh.h_source));

	/* ARP header */
	req.ah.ar_op = htons(ARPOP_REQUEST);
	req.ah.ar_hrd = htons(ARPHRD_ETHER);
	req.ah.ar_pro = htons(ETH_P_IP);
	req.ah.ar_hln = ETH_ALEN;
	req.ah.ar_pln = 4;

	/* ARP message */
	memcpy(req.am.sha,	c->our_tap_mac,		sizeof(req.am.sha));
	memcpy(req.am.sip,	&c->ip4.our_tap_addr,	sizeof(req.am.sip));
	memcpy(req.am.tha,	MAC_BROADCAST,		sizeof(req.am.tha));
	memcpy(req.am.tip,	&c->ip4.addr,		sizeof(req.am.tip));

	debug("Sending initial ARP request for guest MAC address");
	tap_send_single(c, VHOST_USER_RX_QUEUE, &req, sizeof(req));
}

/**
 * arp_announce() - Send an ARP announcement for an IPv4 host
 * @c:		Execution context
 * @queue:	Queue to use to send the announcement
 * @ip:	IPv4 address we announce as owned by @mac
 * @mac:	MAC address to advertise for @ip
 */
void arp_announce(const struct ctx *c, int queue, 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, queue, &msg, sizeof(msg));
}
debug log:

solving c57e0471aee8 ...
found c57e0471aee8 in https://archives.passt.top/passt-dev/20251107143901.89955-3-lvivier@redhat.com/
found 33b03cf6c316 in https://passt.top/passt
preparing index
index prepared:
100644 33b03cf6c316bd4a495e8fa1e486776dfde74ae0	arp.c

applying [1/1] https://archives.passt.top/passt-dev/20251107143901.89955-3-lvivier@redhat.com/
diff --git a/arp.c b/arp.c
index 33b03cf6c316..c57e0471aee8 100644

Checking patch arp.c...
Applied patch arp.c cleanly.

index at:
100644 c57e0471aee8399aefb1d8a8b5bd716435c9e28e	arp.c

Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).