From 41ee544212ac5b9af8ccc347161d2417fc1ae27e Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 30 Nov 2018 08:45:36 +0000 Subject: [PATCH] vrrpd: implement gratuitous ARP Implement gratuitous ARP functionality. Ethernet only. Signed-off-by: Quentin Young --- vrrpd/subdir.am | 12 ++-- vrrpd/vrrp.c | 2 + vrrpd/vrrp.h | 6 +- vrrpd/vrrp_arp.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++ vrrpd/vrrp_arp.h | 35 +++++++++ 5 files changed, 227 insertions(+), 8 deletions(-) create mode 100644 vrrpd/vrrp_arp.c create mode 100644 vrrpd/vrrp_arp.h diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index 40dee9c145..5c040932e6 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -11,18 +11,20 @@ man8 += $(MANBUILD)/vrrpd.8 endif vrrpd_libvrrp_a_SOURCES = \ + vrrpd/vrrp.c \ + vrrpd/vrrp_arp.c \ vrrpd/vrrp_memory.c \ - vrrpd/vrrp_zebra.c \ - vrrpd/vrrp_vty.c \ vrrpd/vrrp_packet.c \ - vrrpd/vrrp.c \ + vrrpd/vrrp_vty.c \ + vrrpd/vrrp_zebra.c \ # end noinst_HEADERS += \ + vrrpd/vrrp.h \ + vrrpd/vrrp_arp.h \ vrrpd/vrrp_memory.h \ - vrrpd/vrrp_zebra.h \ vrrpd/vrrp_vty.h \ - vrrpd/vrrp.h \ + vrrpd/vrrp_zebra.h \ # end vrrpd/vrrp_vty_clippy.c: $(CLIPPY_DEPS) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 2cc0896605..f0a106ba05 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -17,6 +17,8 @@ * 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 + #include "memory.h" #include "if.h" #include "linklist.h" diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 308e3a8e10..95feeb4dfc 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -17,8 +17,8 @@ * 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 */ -#ifndef __VRRP_H__ -#define __VRRP_H_ +#ifndef _VRRP_H +#define _VRRP_H #include #include "linklist.h" @@ -159,4 +159,4 @@ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid); */ void vrrp_event(struct vrrp_vrouter *vr, int event); -#endif +#endif /* _VRRP_H */ diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c new file mode 100644 index 0000000000..cbea02212b --- /dev/null +++ b/vrrpd/vrrp_arp.c @@ -0,0 +1,180 @@ +/* + * VRRP ARP primitives. + * + * Copyright (C) 2001-2017 Alexandre Cassen + * Portions: + * Copyright (C) 2018 Cumulus Networks + * 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 + +#include +#include +#include + +#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" + +/* + * 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 + sizeof(struct arphdr)); + /* 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_vrouter *vr, struct in_addr *v4) +{ + struct interface *ifp = vr->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( + "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", + ifp->name); + return; + } + + /* Build garp */ + garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4); + + /* Send garp */ + inet_ntop(AF_INET, v4, astr, sizeof(astr)); + zlog_info("Sending gratuitous ARP on %s for %s", ifp->name, astr); + sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); + + if (sent_len < 0) + zlog_warn("Error sending gratuitous ARP on %s for %s", + ifp->name, astr); +} + +void vrrp_garp_send_all(struct vrrp_vrouter *vr) +{ + struct interface *ifp = vr->ifp; + + /* If the interface doesn't support ARP, don't try sending */ + if (ifp->flags & IFF_NOARP) { + zlog_warn( + "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", + ifp->name); + return; + } + + struct listnode *ln; + struct in_addr *v4; + + for (ALL_LIST_ELEMENTS_RO(vr->v4, ln, v4)) + vrrp_garp_send(vr, v4); +} + + +void vrrp_garp_init(void) +{ + /* Create the socket descriptor */ + /* FIXME: why ETH_P_RARP? */ + garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC, htons(ETH_P_RARP)); + + if (garp_fd > 0) + zlog_info("Initialized gratuitous ARP socket"); + else { + zlog_err("Error initializing gratuitous ARP socket"); + return; + } +} + +void vrrp_garp_fini(void) +{ + close(garp_fd); + garp_fd = -1; +} diff --git a/vrrpd/vrrp_arp.h b/vrrpd/vrrp_arp.h new file mode 100644 index 0000000000..d3c5651ab5 --- /dev/null +++ b/vrrpd/vrrp_arp.h @@ -0,0 +1,35 @@ +/* + * VRRPD global definitions + * Copyright (C) 2018 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 + */ +#ifndef _VRRP_ARP_H +#define _VRRP_ARP_H + +#include + +#include "vrrp.h" + +/* FIXME: Use the kernel define for this */ +#define HWTYPE_ETHER 1 + +/* prototypes */ +extern void vrrp_garp_init(void); +extern void vrrp_garp_fini(void); +extern void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4); +extern void vrrp_garp_send_all(struct vrrp_vrouter *vr); +#endif -- 2.39.5