diff options
author | Quentin Young <qlyoung@cumulusnetworks.com> | 2019-01-28 00:08:01 +0100 |
---|---|---|
committer | Quentin Young <qlyoung@cumulusnetworks.com> | 2019-05-17 02:27:08 +0200 |
commit | 4f52e9a685177d34486aff19d9f3c486443c9b41 (patch) | |
tree | 9b5ffcc34e48b2ab2e0a91686f27624a9b81176d /vrrpd | |
parent | lib: add function to get iface link-local (diff) | |
download | frr-4f52e9a685177d34486aff19d9f3c486443c9b41.tar.xz frr-4f52e9a685177d34486aff19d9f3c486443c9b41.zip |
vrrpd: send ICMPv6 Neighbor Advertisements
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
Diffstat (limited to 'vrrpd')
-rw-r--r-- | vrrpd/subdir.am | 2 | ||||
-rw-r--r-- | vrrpd/vrrp.c | 10 | ||||
-rw-r--r-- | vrrpd/vrrp_ndisc.c | 228 | ||||
-rw-r--r-- | vrrpd/vrrp_ndisc.h | 74 |
4 files changed, 314 insertions, 0 deletions
diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index 5c040932e..633a4a8e7 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -14,6 +14,7 @@ vrrpd_libvrrp_a_SOURCES = \ vrrpd/vrrp.c \ vrrpd/vrrp_arp.c \ vrrpd/vrrp_memory.c \ + vrrpd/vrrp_ndisc.c \ vrrpd/vrrp_packet.c \ vrrpd/vrrp_vty.c \ vrrpd/vrrp_zebra.c \ @@ -23,6 +24,7 @@ noinst_HEADERS += \ vrrpd/vrrp.h \ vrrpd/vrrp_arp.h \ vrrpd/vrrp_memory.h \ + vrrpd/vrrp_ndisc.h \ vrrpd/vrrp_vty.h \ vrrpd/vrrp_zebra.h \ # end diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 0cc9ed0e9..1e7117eaf 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -32,6 +32,7 @@ #include "vrrp.h" #include "vrrp_arp.h" +#include "vrrp_ndisc.h" #include "vrrp_packet.h" #define VRRP_LOGPFX "[CORE] " @@ -181,6 +182,9 @@ void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) } listnode_add(vr->v6->addrs, v6_ins); + + if (vr->v6->fsm.state == VRRP_STATE_MASTER) + vrrp_ndisc_una_send(vr->v6, v6_ins); } void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip) @@ -996,6 +1000,8 @@ static int vrrp_master_down_timer_expire(struct thread *thread) vrrp_send_advertisement(r); if (r->family == AF_INET) vrrp_garp_send_all(r); + if (r->family == AF_INET6) + vrrp_ndisc_una_send_all(r); thread_add_timer_msec(master, vrrp_adver_timer_expire, r, r->vr->advertisement_interval * 10, &r->t_adver_timer); @@ -1038,6 +1044,8 @@ static int vrrp_startup(struct vrrp_router *r) /* Initialize global gratuitous ARP socket if necessary */ if (r->family == AF_INET && !vrrp_garp_is_init()) vrrp_garp_init(); + if (r->family == AF_INET6 && !vrrp_ndisc_is_init()) + vrrp_ndisc_init(); /* Create socket */ if (r->sock_rx < 0 || r->sock_tx < 0) { @@ -1070,6 +1078,8 @@ static int vrrp_startup(struct vrrp_router *r) if (r->family == AF_INET) vrrp_garp_send_all(r); + if (r->family == AF_INET6) + vrrp_ndisc_una_send_all(r); thread_add_timer_msec(master, vrrp_adver_timer_expire, r, r->vr->advertisement_interval * 10, diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c new file mode 100644 index 000000000..ce6c62c07 --- /dev/null +++ b/vrrpd/vrrp_ndisc.c @@ -0,0 +1,228 @@ +/* + * VRRP Neighbor Discovery. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * Portions: + * Copyright (C) 2001-2017 Alexandre Cassen + * + * 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/ethernet.h> +#include <netinet/icmp6.h> +#include <netinet/in.h> + +#include "lib/checksum.h" +#include "lib/if.h" +#include "lib/ipaddr.h" +#include "lib/log.h" + +#include "vrrp_ndisc.h" + +#define VRRP_LOGPFX "[NDISC] " + +#define VRRP_NDISC_HOPLIMIT 255 +#define VRRP_NDISC_SIZE \ + ETHER_HDR_LEN + sizeof(struct ip6_hdr) \ + + sizeof(struct nd_neighbor_advert) \ + + sizeof(struct nd_opt_hdr) + ETH_ALEN + +/* static vars */ +static int ndisc_fd = -1; + +/* + * Build an unsolicited Neighbour Advertisement. + * + * ifp + * Interface to send Neighbor Advertisement on + * + * ip + * IP address to send Neighbor Advertisement for + * + * buf + * Buffer to fill with IPv6 Neighbor Advertisement message. Includes + * Ethernet header. + * + * bufsiz + * Size of buf. + * + * Returns; + * -1 if bufsiz is too small + * 0 otherwise + */ +static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip, + uint8_t *buf, size_t bufsiz) +{ + if (bufsiz < VRRP_NDISC_SIZE) + return -1; + + memset(buf, 0x00, bufsiz); + + struct ether_header *eth = (struct ether_header *)buf; + struct ip6_hdr *ip6h = (struct ip6_hdr *)((char *)eth + ETHER_HDR_LEN); + struct nd_neighbor_advert *ndh = + (struct nd_neighbor_advert *)((char *)ip6h + + sizeof(struct ip6_hdr)); + struct icmp6_hdr *icmp6h = &ndh->nd_na_hdr; + struct nd_opt_hdr *nd_opt_h = + (struct nd_opt_hdr *)((char *)ndh + + sizeof(struct nd_neighbor_advert)); + char *nd_opt_lladdr = + (char *)((char *)nd_opt_h + sizeof(struct nd_opt_hdr)); + char *lladdr = (char *)ifp->hw_addr; + + /* + * An IPv6 packet with a multicast destination address DST, consisting + * of the sixteen octets DST[1] through DST[16], is transmitted to the + * Ethernet multicast address whose first two octets are the value 3333 + * hexadecimal and whose last four octets are the last four octets of + * DST. + * - RFC2464.7 + * + * In this case we are sending to the all nodes multicast address, so + * the last four octets are 0x00 0x00 0x00 0x01. + */ + memset(eth->ether_dhost, 0, ETH_ALEN); + eth->ether_dhost[0] = 0x33; + eth->ether_dhost[1] = 0x33; + eth->ether_dhost[5] = 1; + + /* Set source Ethernet address to interface link layer address */ + memcpy(eth->ether_shost, lladdr, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_IPV6); + + /* IPv6 Header */ + ip6h->ip6_vfc = 6 << 4; + ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert) + + sizeof(struct nd_opt_hdr) + ETH_ALEN); + ip6h->ip6_nxt = IPPROTO_ICMPV6; + ip6h->ip6_hlim = VRRP_NDISC_HOPLIMIT; + memcpy(&ip6h->ip6_src, &ip->ipaddr_v6, sizeof(struct in6_addr)); + /* All nodes multicast address */ + ip6h->ip6_dst.s6_addr[0] = 0xFF; + ip6h->ip6_dst.s6_addr[1] = 0x02; + ip6h->ip6_dst.s6_addr[15] = 0x01; + + /* ICMPv6 Header */ + ndh->nd_na_type = ND_NEIGHBOR_ADVERT; + ndh->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; + ndh->nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE; + memcpy(&ndh->nd_na_target, &ip->ipaddr_v6, sizeof(struct in6_addr)); + + /* NDISC Option header */ + nd_opt_h->nd_opt_type = ND_OPT_TARGET_LINKADDR; + nd_opt_h->nd_opt_len = 1; + memcpy(nd_opt_lladdr, lladdr, ETH_ALEN); + + /* Compute checksum */ + uint32_t len = sizeof(struct nd_neighbor_advert) + + sizeof(struct nd_opt_hdr) + ETH_ALEN; + struct ipv6_ph ph = {}; + ph.src = ip6h->ip6_src; + ph.dst = ip6h->ip6_dst; + ph.ulpl = htonl(len); + ph.next_hdr = IPPROTO_ICMPV6; + icmp6h->icmp6_cksum = in_cksum_with_ph6(&ph, (void *)icmp6h, len); + + return 0; +} + +int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) +{ + assert(r->family == AF_INET6); + + int ret = 0; + struct interface *ifp = r->mvl_ifp; + + uint8_t buf[VRRP_NDISC_SIZE]; + ret = vrrp_ndisc_una_build(ifp, ip, buf, sizeof(buf)); + + if (ret == -1) + return ret; + + struct sockaddr_ll sll; + ssize_t len; + + /* Build the dst device */ + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + memcpy(sll.sll_addr, ifp->hw_addr, ETH_ALEN); + sll.sll_halen = ETH_ALEN; + sll.sll_ifindex = (int)ifp->ifindex; + + char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); + + zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, ifp->name, ipbuf); + + len = sendto(ndisc_fd, buf, VRRP_NDISC_SIZE, 0, (struct sockaddr *)&sll, + sizeof(sll)); + + if (len < 0) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Error sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, ifp->name, ipbuf); + ret = -1; + } + + return ret; +} + +int vrrp_ndisc_una_send_all(struct vrrp_router *r) +{ + assert(r->family == AF_INET6); + + struct listnode *ln; + struct ipaddr *ip; + + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip)) + vrrp_ndisc_una_send(r, ip); + + return 0; +} + +void vrrp_ndisc_init(void) +{ + vrrp_privs.change(ZPRIVS_RAISE); + { + ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6)); + } + vrrp_privs.change(ZPRIVS_LOWER); + + if (ndisc_fd > 0) + zlog_info( + VRRP_LOGPFX + "Initialized unsolicited neighbor advertisement socket"); + else + zlog_err( + VRRP_LOGPFX + "Error initializing unsolicited neighbor advertisement socket"); +} + +void vrrp_ndisc_fini(void) +{ + close(ndisc_fd); + ndisc_fd = -1; +} + +bool vrrp_ndisc_is_init(void) +{ + return ndisc_fd > 0; +} diff --git a/vrrpd/vrrp_ndisc.h b/vrrpd/vrrp_ndisc.h new file mode 100644 index 000000000..262d3db03 --- /dev/null +++ b/vrrpd/vrrp_ndisc.h @@ -0,0 +1,74 @@ +/* + * VRRP Neighbor Discovery. + * Copyright (C) 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 + */ +#ifndef _VRRP_NDISC_H +#define _VRRP_NDISC_H + +#include <netinet/icmp6.h> +#include <netinet/in.h> +#include <netinet/ip6.h> + +#include "vrrp.h" + +/* + * Initialize VRRP neighbor discovery. + */ +extern void vrrp_ndisc_init(void); + +/* + * Check whether VRRP Neighbor Discovery is initialized. + * + * Returns: + * True if initialized, false otherwise + */ +extern bool vrrp_ndisc_is_init(void); + +/* + * Finish VRRP Neighbor Discovery. + */ +extern void vrrp_ndisc_fini(void); + +/* + * Send VRRP Neighbor Advertisement. + * + * ifp + * Interface to transmit on + * + * ip + * IPv6 address to send Neighbor Advertisement for + * + * Returns: + * -1 on failure + * 0 otherwise + */ +extern int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip); + +/* + * Send VRRP Neighbor Advertisements for all virtual IPs. + * + * r + * Virtual Router to send NA's for + * + * Returns: + * -1 on failure + * 0 otherwise + */ +extern int vrrp_ndisc_una_send_all(struct vrrp_router *r); + +#endif |