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