1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * VRRP Neighbor Discovery.
4 * Copyright (C) 2019 Cumulus Networks, Inc.
8 * Copyright (C) 2001-2017 Alexandre Cassen
12 #include <linux/if_packet.h>
13 #include <net/ethernet.h>
14 #include <netinet/icmp6.h>
15 #include <netinet/in.h>
17 #include "lib/checksum.h"
19 #include "lib/ipaddr.h"
22 #include "vrrp_debug.h"
23 #include "vrrp_ndisc.h"
25 #define VRRP_LOGPFX "[NDISC] "
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
34 static int ndisc_fd
= -1;
37 * Build an unsolicited Neighbour Advertisement.
40 * Interface to send Neighbor Advertisement on
43 * IP address to send Neighbor Advertisement for
46 * Buffer to fill with IPv6 Neighbor Advertisement message. Includes
53 * -1 if bufsiz is too small
56 static int vrrp_ndisc_una_build(struct interface
*ifp
, struct ipaddr
*ip
,
57 uint8_t *buf
, size_t bufsiz
)
59 if (bufsiz
< VRRP_NDISC_SIZE
)
62 memset(buf
, 0x00, bufsiz
);
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
));
73 char *nd_opt_lladdr
= ((char *)nd_opt_h
+ sizeof(struct nd_opt_hdr
));
74 char *lladdr
= (char *)ifp
->hw_addr
;
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
84 * In this case we are sending to the all nodes multicast address, so
85 * the last four octets are 0x00 0x00 0x00 0x01.
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;
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
);
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;
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
));
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
);
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
= {};
124 ph
.src
= ip6h
->ip6_src
;
125 ph
.dst
= ip6h
->ip6_dst
;
126 ph
.ulpl
= htonl(len
);
127 ph
.next_hdr
= IPPROTO_ICMPV6
;
129 /* Suppress static analysis warnings about accessing icmp6 oob */
130 void *offset
= icmp6h
;
131 icmp6h
->icmp6_cksum
= in_cksum_with_ph6(&ph
, offset
, len
);
136 int vrrp_ndisc_una_send(struct vrrp_router
*r
, struct ipaddr
*ip
)
138 assert(r
->family
== AF_INET6
);
141 struct interface
*ifp
= r
->mvl_ifp
;
142 uint8_t buf
[VRRP_NDISC_SIZE
];
144 ret
= vrrp_ndisc_una_build(ifp
, ip
, buf
, sizeof(buf
));
149 struct sockaddr_ll sll
;
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
;
159 char ipbuf
[INET6_ADDRSTRLEN
];
161 ipaddr2str(ip
, ipbuf
, sizeof(ipbuf
));
163 DEBUGD(&vrrp_dbg_ndisc
,
164 VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
165 "Sending unsolicited Neighbor Advertisement on %s for %s",
166 r
->vr
->vrid
, family2str(r
->family
), ifp
->name
, ipbuf
);
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
);
172 len
= sendto(ndisc_fd
, buf
, VRRP_NDISC_SIZE
, 0, (struct sockaddr
*)&sll
,
177 VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
178 "Error sending unsolicited Neighbor Advertisement on %s for %s",
179 r
->vr
->vrid
, family2str(r
->family
), ifp
->name
, ipbuf
);
182 ++r
->stats
.una_tx_cnt
;
188 int vrrp_ndisc_una_send_all(struct vrrp_router
*r
)
190 assert(r
->family
== AF_INET6
);
195 for (ALL_LIST_ELEMENTS_RO(r
->addrs
, ln
, ip
))
196 vrrp_ndisc_una_send(r
, ip
);
201 void vrrp_ndisc_init(void)
203 frr_with_privs(&vrrp_privs
) {
204 ndisc_fd
= socket(AF_PACKET
, SOCK_RAW
, htons(ETH_P_IPV6
));
208 DEBUGD(&vrrp_dbg_sock
,
209 VRRP_LOGPFX
"Initialized Neighbor Discovery socket");
210 DEBUGD(&vrrp_dbg_ndisc
,
211 VRRP_LOGPFX
"Initialized Neighbor Discovery subsystem");
214 "Error initializing Neighbor Discovery socket");
218 void vrrp_ndisc_fini(void)
223 DEBUGD(&vrrp_dbg_ndisc
,
224 VRRP_LOGPFX
"Deinitialized Neighbor Discovery subsystem");
227 bool vrrp_ndisc_is_init(void)