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