]> git.proxmox.com Git - mirror_frr.git/blame - vrrpd/vrrp_ndisc.c
Merge pull request #4877 from mjstapp/dplane_neighs
[mirror_frr.git] / vrrpd / vrrp_ndisc.c
CommitLineData
4f52e9a6
QY
1/*
2 * VRRP Neighbor Discovery.
3 * Copyright (C) 2019 Cumulus Networks, Inc.
63d4bd12
QY
4 * Quentin Young
5 *
4f52e9a6 6 * Portions:
2fff50ec 7 * Copyright (C) 2001-2017 Alexandre Cassen
4f52e9a6
QY
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program; see the file COPYING; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23#include <zebra.h>
24
25#include <linux/if_packet.h>
26#include <net/ethernet.h>
27#include <netinet/icmp6.h>
28#include <netinet/in.h>
29
30#include "lib/checksum.h"
31#include "lib/if.h"
32#include "lib/ipaddr.h"
33#include "lib/log.h"
34
b637bcd4 35#include "vrrp_debug.h"
4f52e9a6
QY
36#include "vrrp_ndisc.h"
37
38#define VRRP_LOGPFX "[NDISC] "
39
40#define VRRP_NDISC_HOPLIMIT 255
41#define VRRP_NDISC_SIZE \
42 ETHER_HDR_LEN + sizeof(struct ip6_hdr) \
43 + sizeof(struct nd_neighbor_advert) \
44 + sizeof(struct nd_opt_hdr) + ETH_ALEN
45
46/* static vars */
47static int ndisc_fd = -1;
48
49/*
50 * Build an unsolicited Neighbour Advertisement.
51 *
52 * ifp
53 * Interface to send Neighbor Advertisement on
54 *
55 * ip
56 * IP address to send Neighbor Advertisement for
57 *
58 * buf
59 * Buffer to fill with IPv6 Neighbor Advertisement message. Includes
60 * Ethernet header.
61 *
62 * bufsiz
63 * Size of buf.
64 *
65 * Returns;
66 * -1 if bufsiz is too small
67 * 0 otherwise
68 */
69static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip,
354b49d6 70 uint8_t *buf, size_t bufsiz)
4f52e9a6
QY
71{
72 if (bufsiz < VRRP_NDISC_SIZE)
73 return -1;
74
75 memset(buf, 0x00, bufsiz);
76
77 struct ether_header *eth = (struct ether_header *)buf;
78 struct ip6_hdr *ip6h = (struct ip6_hdr *)((char *)eth + ETHER_HDR_LEN);
79 struct nd_neighbor_advert *ndh =
80 (struct nd_neighbor_advert *)((char *)ip6h
81 + sizeof(struct ip6_hdr));
82 struct icmp6_hdr *icmp6h = &ndh->nd_na_hdr;
83 struct nd_opt_hdr *nd_opt_h =
84 (struct nd_opt_hdr *)((char *)ndh
85 + sizeof(struct nd_neighbor_advert));
86 char *nd_opt_lladdr =
87 (char *)((char *)nd_opt_h + sizeof(struct nd_opt_hdr));
88 char *lladdr = (char *)ifp->hw_addr;
89
90 /*
91 * An IPv6 packet with a multicast destination address DST, consisting
92 * of the sixteen octets DST[1] through DST[16], is transmitted to the
93 * Ethernet multicast address whose first two octets are the value 3333
94 * hexadecimal and whose last four octets are the last four octets of
95 * DST.
96 * - RFC2464.7
97 *
98 * In this case we are sending to the all nodes multicast address, so
99 * the last four octets are 0x00 0x00 0x00 0x01.
100 */
101 memset(eth->ether_dhost, 0, ETH_ALEN);
102 eth->ether_dhost[0] = 0x33;
103 eth->ether_dhost[1] = 0x33;
104 eth->ether_dhost[5] = 1;
105
106 /* Set source Ethernet address to interface link layer address */
107 memcpy(eth->ether_shost, lladdr, ETH_ALEN);
108 eth->ether_type = htons(ETHERTYPE_IPV6);
109
110 /* IPv6 Header */
111 ip6h->ip6_vfc = 6 << 4;
112 ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert)
113 + sizeof(struct nd_opt_hdr) + ETH_ALEN);
114 ip6h->ip6_nxt = IPPROTO_ICMPV6;
115 ip6h->ip6_hlim = VRRP_NDISC_HOPLIMIT;
116 memcpy(&ip6h->ip6_src, &ip->ipaddr_v6, sizeof(struct in6_addr));
117 /* All nodes multicast address */
118 ip6h->ip6_dst.s6_addr[0] = 0xFF;
119 ip6h->ip6_dst.s6_addr[1] = 0x02;
120 ip6h->ip6_dst.s6_addr[15] = 0x01;
121
122 /* ICMPv6 Header */
123 ndh->nd_na_type = ND_NEIGHBOR_ADVERT;
124 ndh->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
125 ndh->nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE;
126 memcpy(&ndh->nd_na_target, &ip->ipaddr_v6, sizeof(struct in6_addr));
127
128 /* NDISC Option header */
129 nd_opt_h->nd_opt_type = ND_OPT_TARGET_LINKADDR;
130 nd_opt_h->nd_opt_len = 1;
131 memcpy(nd_opt_lladdr, lladdr, ETH_ALEN);
132
133 /* Compute checksum */
134 uint32_t len = sizeof(struct nd_neighbor_advert)
135 + sizeof(struct nd_opt_hdr) + ETH_ALEN;
136 struct ipv6_ph ph = {};
2fff50ec 137
4f52e9a6
QY
138 ph.src = ip6h->ip6_src;
139 ph.dst = ip6h->ip6_dst;
140 ph.ulpl = htonl(len);
141 ph.next_hdr = IPPROTO_ICMPV6;
ab059def
QY
142
143 /* Suppress static analysis warnings about accessing icmp6 oob */
144 void *offset = icmp6h;
145 icmp6h->icmp6_cksum = in_cksum_with_ph6(&ph, offset, len);
4f52e9a6
QY
146
147 return 0;
148}
149
150int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip)
151{
152 assert(r->family == AF_INET6);
153
154 int ret = 0;
155 struct interface *ifp = r->mvl_ifp;
4f52e9a6 156 uint8_t buf[VRRP_NDISC_SIZE];
2fff50ec 157
4f52e9a6
QY
158 ret = vrrp_ndisc_una_build(ifp, ip, buf, sizeof(buf));
159
160 if (ret == -1)
161 return ret;
162
163 struct sockaddr_ll sll;
164 ssize_t len;
165
166 /* Build the dst device */
167 memset(&sll, 0, sizeof(sll));
168 sll.sll_family = AF_PACKET;
169 memcpy(sll.sll_addr, ifp->hw_addr, ETH_ALEN);
170 sll.sll_halen = ETH_ALEN;
171 sll.sll_ifindex = (int)ifp->ifindex;
172
173 char ipbuf[INET6_ADDRSTRLEN];
2fff50ec 174
4f52e9a6
QY
175 ipaddr2str(ip, ipbuf, sizeof(ipbuf));
176
b637bcd4 177 DEBUGD(&vrrp_dbg_ndisc,
613b45b0 178 VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
b637bcd4 179 "Sending unsolicited Neighbor Advertisement on %s for %s",
613b45b0 180 r->vr->vrid, family2str(r->family), ifp->name, ipbuf);
b637bcd4
QY
181
182 if (DEBUG_MODE_CHECK(&vrrp_dbg_ndisc, DEBUG_MODE_ALL)
183 && DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL))
184 zlog_hexdump(buf, VRRP_NDISC_SIZE);
4f52e9a6
QY
185
186 len = sendto(ndisc_fd, buf, VRRP_NDISC_SIZE, 0, (struct sockaddr *)&sll,
187 sizeof(sll));
188
189 if (len < 0) {
190 zlog_err(
613b45b0 191 VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
4f52e9a6 192 "Error sending unsolicited Neighbor Advertisement on %s for %s",
613b45b0 193 r->vr->vrid, family2str(r->family), ifp->name, ipbuf);
4f52e9a6 194 ret = -1;
6332c77f
QY
195 } else {
196 ++r->stats.una_tx_cnt;
4f52e9a6
QY
197 }
198
199 return ret;
200}
201
202int vrrp_ndisc_una_send_all(struct vrrp_router *r)
203{
204 assert(r->family == AF_INET6);
205
206 struct listnode *ln;
207 struct ipaddr *ip;
208
209 for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip))
210 vrrp_ndisc_una_send(r, ip);
211
212 return 0;
213}
214
215void vrrp_ndisc_init(void)
216{
0cf6db21 217 frr_with_privs(&vrrp_privs) {
4f52e9a6
QY
218 ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6));
219 }
4f52e9a6 220
b637bcd4
QY
221 if (ndisc_fd > 0) {
222 DEBUGD(&vrrp_dbg_sock,
223 VRRP_LOGPFX "Initialized Neighbor Discovery socket");
224 DEBUGD(&vrrp_dbg_ndisc,
225 VRRP_LOGPFX "Initialized Neighbor Discovery subsystem");
226 } else {
227 zlog_err(VRRP_LOGPFX
228 "Error initializing Neighbor Discovery socket");
229 }
4f52e9a6
QY
230}
231
232void vrrp_ndisc_fini(void)
233{
234 close(ndisc_fd);
235 ndisc_fd = -1;
b637bcd4
QY
236
237 DEBUGD(&vrrp_dbg_ndisc,
238 VRRP_LOGPFX "Deinitialized Neighbor Discovery subsystem");
4f52e9a6
QY
239}
240
241bool vrrp_ndisc_is_init(void)
242{
243 return ndisc_fd > 0;
244}