mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-03 23:47:16 +00:00 
			
		
		
		
	Neither tabs nor newlines are acceptable in syslog messages. They also break line-based parsing of file logs. Signed-off-by: David Lamparter <equinox@diac24.net>
		
			
				
	
	
		
			220 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * VRRP ARP handling.
 | 
						|
 * Copyright (C) 2001-2017 Alexandre Cassen
 | 
						|
 * Portions:
 | 
						|
 *     Copyright (C) 2018-2019 Cumulus Networks, Inc.
 | 
						|
 *     Quentin Young
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License as published by the Free
 | 
						|
 * Software Foundation; either version 2 of the License, or (at your option)
 | 
						|
 * any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
						|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | 
						|
 * more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License along with
 | 
						|
 * this program; see the file COPYING; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include <linux/if_packet.h>
 | 
						|
#include <net/if_arp.h>
 | 
						|
#include <netinet/if_ether.h>
 | 
						|
 | 
						|
#include "lib/if.h"
 | 
						|
#include "lib/linklist.h"
 | 
						|
#include "lib/log.h"
 | 
						|
#include "lib/memory.h"
 | 
						|
#include "lib/prefix.h"
 | 
						|
 | 
						|
#include "vrrp.h"
 | 
						|
#include "vrrp_arp.h"
 | 
						|
#include "vrrp_debug.h"
 | 
						|
 | 
						|
#define VRRP_LOGPFX "[ARP] "
 | 
						|
 | 
						|
/*
 | 
						|
 * The size of the garp packet buffer should be the large enough to hold the
 | 
						|
 * largest arp packet to be sent + the size of the link layer header for the
 | 
						|
 * corresponding protocol. In this case we hardcode for Ethernet.
 | 
						|
 */
 | 
						|
#define GARP_BUFFER_SIZE                                                       \
 | 
						|
	sizeof(struct ether_header) + sizeof(struct arphdr) + 2 * ETH_ALEN     \
 | 
						|
		+ 2 * sizeof(struct in_addr)
 | 
						|
 | 
						|
/* static vars */
 | 
						|
static int garp_fd = -1;
 | 
						|
 | 
						|
/* Send the gratuitous ARP message */
 | 
						|
static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf,
 | 
						|
			      ssize_t pack_len)
 | 
						|
{
 | 
						|
	struct sockaddr_ll sll;
 | 
						|
	ssize_t len;
 | 
						|
 | 
						|
	/* Build the dst device */
 | 
						|
	memset(&sll, 0, sizeof(sll));
 | 
						|
	sll.sll_family = AF_PACKET;
 | 
						|
	sll.sll_protocol = ETH_P_ARP;
 | 
						|
	sll.sll_ifindex = (int)ifp->ifindex;
 | 
						|
	sll.sll_halen = ifp->hw_addr_len;
 | 
						|
	memset(sll.sll_addr, 0xFF, ETH_ALEN);
 | 
						|
 | 
						|
	/* Send packet */
 | 
						|
	len = sendto(garp_fd, buf, pack_len, 0, (struct sockaddr *)&sll,
 | 
						|
		     sizeof(sll));
 | 
						|
 | 
						|
	return len;
 | 
						|
}
 | 
						|
 | 
						|
/* Build a gratuitous ARP message over a specific interface */
 | 
						|
static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp,
 | 
						|
			       struct in_addr *v4)
 | 
						|
{
 | 
						|
	uint8_t *arp_ptr;
 | 
						|
 | 
						|
	if (ifp->hw_addr_len == 0)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	/* Build Ethernet header */
 | 
						|
	struct ether_header *eth = (struct ether_header *)buf;
 | 
						|
 | 
						|
	memset(eth->ether_dhost, 0xFF, ETH_ALEN);
 | 
						|
	memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN);
 | 
						|
	eth->ether_type = htons(ETHERTYPE_ARP);
 | 
						|
 | 
						|
	/* Build ARP payload */
 | 
						|
	struct arphdr *arph = (struct arphdr *)(buf + ETHER_HDR_LEN);
 | 
						|
 | 
						|
	arph->ar_hrd = htons(HWTYPE_ETHER);
 | 
						|
	arph->ar_pro = htons(ETHERTYPE_IP);
 | 
						|
	arph->ar_hln = ifp->hw_addr_len;
 | 
						|
	arph->ar_pln = sizeof(struct in_addr);
 | 
						|
	arph->ar_op = htons(ARPOP_REQUEST);
 | 
						|
	arp_ptr = (uint8_t *)(arph + 1);
 | 
						|
	/* Source MAC: us */
 | 
						|
	memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len);
 | 
						|
	arp_ptr += ifp->hw_addr_len;
 | 
						|
	/* Source IP: us */
 | 
						|
	memcpy(arp_ptr, v4, sizeof(struct in_addr));
 | 
						|
	arp_ptr += sizeof(struct in_addr);
 | 
						|
	/* Dest MAC: broadcast */
 | 
						|
	memset(arp_ptr, 0xFF, ETH_ALEN);
 | 
						|
	arp_ptr += ifp->hw_addr_len;
 | 
						|
	/* Dest IP: us */
 | 
						|
	memcpy(arp_ptr, v4, sizeof(struct in_addr));
 | 
						|
	arp_ptr += sizeof(struct in_addr);
 | 
						|
 | 
						|
	return arp_ptr - buf;
 | 
						|
}
 | 
						|
 | 
						|
void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4)
 | 
						|
{
 | 
						|
	struct interface *ifp = r->mvl_ifp;
 | 
						|
	uint8_t garpbuf[GARP_BUFFER_SIZE];
 | 
						|
	ssize_t garpbuf_len;
 | 
						|
	ssize_t sent_len;
 | 
						|
	char astr[INET_ADDRSTRLEN];
 | 
						|
 | 
						|
	/* If the interface doesn't support ARP, don't try sending */
 | 
						|
	if (ifp->flags & IFF_NOARP) {
 | 
						|
		zlog_warn(
 | 
						|
			VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
 | 
						|
			"Unable to send gratuitous ARP on %s; has IFF_NOARP",
 | 
						|
			r->vr->vrid, family2str(r->family), ifp->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Build garp */
 | 
						|
	garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4);
 | 
						|
 | 
						|
	if (garpbuf_len < 0) {
 | 
						|
		zlog_warn(
 | 
						|
			VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
 | 
						|
			"Unable to send gratuitous ARP on %s; MAC address unknown",
 | 
						|
			r->vr->vrid, family2str(r->family), ifp->name);
 | 
						|
		return;
 | 
						|
	};
 | 
						|
 | 
						|
	/* Send garp */
 | 
						|
	inet_ntop(AF_INET, v4, astr, sizeof(astr));
 | 
						|
 | 
						|
	DEBUGD(&vrrp_dbg_arp,
 | 
						|
	       VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
 | 
						|
	       "Sending gratuitous ARP on %s for %s",
 | 
						|
	       r->vr->vrid, family2str(r->family), ifp->name, astr);
 | 
						|
	if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL))
 | 
						|
		zlog_hexdump(garpbuf, garpbuf_len);
 | 
						|
 | 
						|
	sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len);
 | 
						|
 | 
						|
	if (sent_len < 0)
 | 
						|
		zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
 | 
						|
			  "Error sending gratuitous ARP on %s for %s",
 | 
						|
			  r->vr->vrid, family2str(r->family), ifp->name, astr);
 | 
						|
	else
 | 
						|
		++r->stats.garp_tx_cnt;
 | 
						|
}
 | 
						|
 | 
						|
void vrrp_garp_send_all(struct vrrp_router *r)
 | 
						|
{
 | 
						|
	assert(r->family == AF_INET);
 | 
						|
 | 
						|
	struct interface *ifp = r->mvl_ifp;
 | 
						|
 | 
						|
	/* If the interface doesn't support ARP, don't try sending */
 | 
						|
	if (ifp->flags & IFF_NOARP) {
 | 
						|
		zlog_warn(
 | 
						|
			VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
 | 
						|
			"Unable to send gratuitous ARP on %s; has IFF_NOARP",
 | 
						|
			r->vr->vrid, family2str(r->family), ifp->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	struct listnode *ln;
 | 
						|
	struct ipaddr *ip;
 | 
						|
 | 
						|
	for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip))
 | 
						|
		vrrp_garp_send(r, &ip->ipaddr_v4);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void vrrp_garp_init(void)
 | 
						|
{
 | 
						|
	/* Create the socket descriptor */
 | 
						|
	/* FIXME: why ETH_P_RARP? */
 | 
						|
	errno = 0;
 | 
						|
	frr_with_privs(&vrrp_privs) {
 | 
						|
		garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC,
 | 
						|
				 htons(ETH_P_RARP));
 | 
						|
	}
 | 
						|
 | 
						|
	if (garp_fd > 0) {
 | 
						|
		DEBUGD(&vrrp_dbg_sock,
 | 
						|
		       VRRP_LOGPFX "Initialized gratuitous ARP socket");
 | 
						|
		DEBUGD(&vrrp_dbg_arp,
 | 
						|
		       VRRP_LOGPFX "Initialized gratuitous ARP subsystem");
 | 
						|
	} else {
 | 
						|
		zlog_err(VRRP_LOGPFX
 | 
						|
			 "Error initializing gratuitous ARP subsystem");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void vrrp_garp_fini(void)
 | 
						|
{
 | 
						|
	close(garp_fd);
 | 
						|
	garp_fd = -1;
 | 
						|
 | 
						|
	DEBUGD(&vrrp_dbg_arp,
 | 
						|
	       VRRP_LOGPFX "Deinitialized gratuitous ARP subsystem");
 | 
						|
}
 | 
						|
 | 
						|
bool vrrp_garp_is_init(void)
 | 
						|
{
 | 
						|
	return garp_fd > 0;
 | 
						|
}
 |