2 * VRRP Neighbor Discovery.
3 * Copyright (C) 2019 Cumulus Networks, Inc.
7 * Copyright (C) 2001-2017 Alexandre Cassen
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)
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
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
25 #include <linux/if_packet.h>
26 #include <net/ethernet.h>
27 #include <netinet/icmp6.h>
28 #include <netinet/in.h>
30 #include "lib/checksum.h"
32 #include "lib/ipaddr.h"
35 #include "vrrp_debug.h"
36 #include "vrrp_ndisc.h"
38 #define VRRP_LOGPFX "[NDISC] "
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
47 static int ndisc_fd
= -1;
50 * Build an unsolicited Neighbour Advertisement.
53 * Interface to send Neighbor Advertisement on
56 * IP address to send Neighbor Advertisement for
59 * Buffer to fill with IPv6 Neighbor Advertisement message. Includes
66 * -1 if bufsiz is too small
69 static int vrrp_ndisc_una_build(struct interface
*ifp
, struct ipaddr
*ip
,
70 uint8_t *buf
, size_t bufsiz
)
72 if (bufsiz
< VRRP_NDISC_SIZE
)
75 memset(buf
, 0x00, bufsiz
);
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
));
87 (char *)((char *)nd_opt_h
+ sizeof(struct nd_opt_hdr
));
88 char *lladdr
= (char *)ifp
->hw_addr
;
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
98 * In this case we are sending to the all nodes multicast address, so
99 * the last four octets are 0x00 0x00 0x00 0x01.
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;
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
);
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;
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
));
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
);
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
= {};
138 ph
.src
= ip6h
->ip6_src
;
139 ph
.dst
= ip6h
->ip6_dst
;
140 ph
.ulpl
= htonl(len
);
141 ph
.next_hdr
= IPPROTO_ICMPV6
;
143 /* Suppress static analysis warnings about accessing icmp6 oob */
144 void *offset
= icmp6h
;
145 icmp6h
->icmp6_cksum
= in_cksum_with_ph6(&ph
, offset
, len
);
150 int vrrp_ndisc_una_send(struct vrrp_router
*r
, struct ipaddr
*ip
)
152 assert(r
->family
== AF_INET6
);
155 struct interface
*ifp
= r
->mvl_ifp
;
156 uint8_t buf
[VRRP_NDISC_SIZE
];
158 ret
= vrrp_ndisc_una_build(ifp
, ip
, buf
, sizeof(buf
));
163 struct sockaddr_ll sll
;
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
;
173 char ipbuf
[INET6_ADDRSTRLEN
];
175 ipaddr2str(ip
, ipbuf
, sizeof(ipbuf
));
177 DEBUGD(&vrrp_dbg_ndisc
,
178 VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
179 "Sending unsolicited Neighbor Advertisement on %s for %s",
180 r
->vr
->vrid
, family2str(r
->family
), ifp
->name
, ipbuf
);
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
);
186 len
= sendto(ndisc_fd
, buf
, VRRP_NDISC_SIZE
, 0, (struct sockaddr
*)&sll
,
191 VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
192 "Error sending unsolicited Neighbor Advertisement on %s for %s",
193 r
->vr
->vrid
, family2str(r
->family
), ifp
->name
, ipbuf
);
196 ++r
->stats
.una_tx_cnt
;
202 int vrrp_ndisc_una_send_all(struct vrrp_router
*r
)
204 assert(r
->family
== AF_INET6
);
209 for (ALL_LIST_ELEMENTS_RO(r
->addrs
, ln
, ip
))
210 vrrp_ndisc_una_send(r
, ip
);
215 void vrrp_ndisc_init(void)
217 frr_elevate_privs(&vrrp_privs
)
219 ndisc_fd
= socket(AF_PACKET
, SOCK_RAW
, htons(ETH_P_IPV6
));
223 DEBUGD(&vrrp_dbg_sock
,
224 VRRP_LOGPFX
"Initialized Neighbor Discovery socket");
225 DEBUGD(&vrrp_dbg_ndisc
,
226 VRRP_LOGPFX
"Initialized Neighbor Discovery subsystem");
229 "Error initializing Neighbor Discovery socket");
233 void vrrp_ndisc_fini(void)
238 DEBUGD(&vrrp_dbg_ndisc
,
239 VRRP_LOGPFX
"Deinitialized Neighbor Discovery subsystem");
242 bool vrrp_ndisc_is_init(void)