5566386f5f
At various points we need to track the lengths of a packet including or excluding various different sets of headers. We don't always use the same variable names for doing so. Worse in some places we use the same name for different things: e.g. tcp_fill_headers[46]() use ip_len for the length including the IP headers, but then tcp_send_flag() which calls it uses it to mean the IP payload length only. To improve clarity, standardise on these names: dlen: L4 protocol payload length ("data length") l4len: plen + length of L4 protocol header l3len: l4len + length of IPv4/IPv6 header l2len: l3len + length of L2 (ethernet) header Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
88 lines
2.2 KiB
C
88 lines
2.2 KiB
C
// 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"
|
|
|
|
/**
|
|
* arp() - Check if this is a supported ARP message, reply as needed
|
|
* @c: Execution context
|
|
* @p: Packet pool, single packet with Ethernet buffer
|
|
*
|
|
* Return: 1 if handled, -1 on failure
|
|
*/
|
|
int arp(const struct ctx *c, const struct pool *p)
|
|
{
|
|
unsigned char swap[4];
|
|
struct ethhdr *eh;
|
|
struct arphdr *ah;
|
|
struct arpmsg *am;
|
|
size_t l2len;
|
|
|
|
eh = packet_get(p, 0, 0, sizeof(*eh), NULL);
|
|
ah = packet_get(p, 0, sizeof(*eh), sizeof(*ah), NULL);
|
|
am = packet_get(p, 0, sizeof(*eh) + sizeof(*ah), sizeof(*am), NULL);
|
|
|
|
if (!eh || !ah || !am)
|
|
return -1;
|
|
|
|
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 1;
|
|
|
|
/* Discard announcements (but not 0.0.0.0 "probes"): we might have the
|
|
* same IP address, hide that.
|
|
*/
|
|
if (memcmp(am->sip, (unsigned char[4]){ 0 }, sizeof(am->tip)) &&
|
|
!memcmp(am->sip, am->tip, sizeof(am->sip)))
|
|
return 1;
|
|
|
|
/* Don't resolve our own address, either. */
|
|
if (!memcmp(am->tip, &c->ip4.addr, sizeof(am->tip)))
|
|
return 1;
|
|
|
|
ah->ar_op = htons(ARPOP_REPLY);
|
|
memcpy(am->tha, am->sha, sizeof(am->tha));
|
|
memcpy(am->sha, c->mac, sizeof(am->sha));
|
|
|
|
memcpy(swap, am->tip, sizeof(am->tip));
|
|
memcpy(am->tip, am->sip, sizeof(am->tip));
|
|
memcpy(am->sip, swap, sizeof(am->sip));
|
|
|
|
l2len = sizeof(*eh) + sizeof(*ah) + sizeof(*am);
|
|
memcpy(eh->h_dest, eh->h_source, sizeof(eh->h_dest));
|
|
memcpy(eh->h_source, c->mac, sizeof(eh->h_source));
|
|
|
|
tap_send_single(c, eh, l2len);
|
|
|
|
return 1;
|
|
}
|