]> git.puffer.fish Git - mirror/frr.git/commitdiff
vrrpd: send ICMPv6 Neighbor Advertisements
authorQuentin Young <qlyoung@cumulusnetworks.com>
Sun, 27 Jan 2019 23:08:01 +0000 (23:08 +0000)
committerQuentin Young <qlyoung@cumulusnetworks.com>
Fri, 17 May 2019 00:27:08 +0000 (00:27 +0000)
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
vrrpd/subdir.am
vrrpd/vrrp.c
vrrpd/vrrp_ndisc.c [new file with mode: 0644]
vrrpd/vrrp_ndisc.h [new file with mode: 0644]

index 5c040932e6960313f015f0fe87cee4d4211f26e4..633a4a8e7345f2c0c31271396b6ca716fb3ebcdc 100644 (file)
@@ -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
index 0cc9ed0e938785d2caaf27add56ed053bafd85ac..1e7117eaffed58d0d2e5d5f01c251fb5dc36bfdb 100644 (file)
@@ -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 (file)
index 0000000..ce6c62c
--- /dev/null
@@ -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 (file)
index 0000000..262d3db
--- /dev/null
@@ -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