]>
Commit | Line | Data |
---|---|---|
0d6ff71a GS |
1 | /* |
2 | * Copyright (c) 2013 | |
3 | * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. | |
4 | */ | |
5 | ||
0d6ff71a GS |
6 | #include "slirp.h" |
7 | #include "ip6_icmp.h" | |
0d6ff71a GS |
8 | |
9 | #define NDP_Interval g_rand_int_range(slirp->grand, \ | |
10 | NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval) | |
11 | ||
12 | static void ra_timer_handler(void *opaque) | |
13 | { | |
14 | Slirp *slirp = opaque; | |
07abf6d4 MAL |
15 | |
16 | slirp->cb->timer_mod(slirp->ra_timer, | |
3e0fad3a MAL |
17 | slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval, |
18 | slirp->opaque); | |
0d6ff71a GS |
19 | ndp_send_ra(slirp); |
20 | } | |
21 | ||
22 | void icmp6_init(Slirp *slirp) | |
23 | { | |
0b11c036 ST |
24 | if (!slirp->in6_enabled) { |
25 | return; | |
26 | } | |
27 | ||
3e0fad3a | 28 | slirp->ra_timer = slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque); |
07abf6d4 | 29 | slirp->cb->timer_mod(slirp->ra_timer, |
3e0fad3a MAL |
30 | slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval, |
31 | slirp->opaque); | |
0d6ff71a GS |
32 | } |
33 | ||
34 | void icmp6_cleanup(Slirp *slirp) | |
35 | { | |
0b11c036 ST |
36 | if (!slirp->in6_enabled) { |
37 | return; | |
38 | } | |
39 | ||
3e0fad3a | 40 | slirp->cb->timer_free(slirp->ra_timer, slirp->opaque); |
0d6ff71a GS |
41 | } |
42 | ||
43 | static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip, | |
44 | struct icmp6 *icmp) | |
45 | { | |
46 | struct mbuf *t = m_get(slirp); | |
47 | t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl); | |
48 | memcpy(t->m_data, m->m_data, t->m_len); | |
49 | ||
50 | /* IPv6 Packet */ | |
51 | struct ip6 *rip = mtod(t, struct ip6 *); | |
52 | rip->ip_dst = ip->ip_src; | |
53 | rip->ip_src = ip->ip_dst; | |
54 | ||
55 | /* ICMPv6 packet */ | |
56 | t->m_data += sizeof(struct ip6); | |
57 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
58 | ricmp->icmp6_type = ICMP6_ECHO_REPLY; | |
59 | ricmp->icmp6_cksum = 0; | |
60 | ||
61 | /* Checksum */ | |
62 | t->m_data -= sizeof(struct ip6); | |
63 | ricmp->icmp6_cksum = ip6_cksum(t); | |
64 | ||
65 | ip6_output(NULL, t, 0); | |
66 | } | |
67 | ||
fc6c9257 YB |
68 | void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code) |
69 | { | |
70 | Slirp *slirp = m->slirp; | |
71 | struct mbuf *t; | |
72 | struct ip6 *ip = mtod(m, struct ip6 *); | |
df2ad332 | 73 | char addrstr[INET6_ADDRSTRLEN]; |
fc6c9257 YB |
74 | |
75 | DEBUG_CALL("icmp6_send_error"); | |
a857d91d | 76 | DEBUG_ARG("type = %d, code = %d", type, code); |
fc6c9257 YB |
77 | |
78 | if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) || | |
1120fae0 | 79 | in6_zero(&ip->ip_src)) { |
fc6c9257 YB |
80 | /* TODO icmp error? */ |
81 | return; | |
82 | } | |
83 | ||
84 | t = m_get(slirp); | |
85 | ||
86 | /* IPv6 packet */ | |
87 | struct ip6 *rip = mtod(t, struct ip6 *); | |
88 | rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR; | |
89 | rip->ip_dst = ip->ip_src; | |
fc6c9257 YB |
90 | inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN); |
91 | DEBUG_ARG("target = %s", addrstr); | |
fc6c9257 YB |
92 | |
93 | rip->ip_nh = IPPROTO_ICMPV6; | |
893dcdbf | 94 | const int error_data_len = MIN(m->m_len, |
fc6c9257 YB |
95 | IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN)); |
96 | rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len); | |
97 | t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); | |
98 | ||
99 | /* ICMPv6 packet */ | |
100 | t->m_data += sizeof(struct ip6); | |
101 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
102 | ricmp->icmp6_type = type; | |
103 | ricmp->icmp6_code = code; | |
104 | ricmp->icmp6_cksum = 0; | |
105 | ||
106 | switch (type) { | |
107 | case ICMP6_UNREACH: | |
108 | case ICMP6_TIMXCEED: | |
109 | ricmp->icmp6_err.unused = 0; | |
110 | break; | |
111 | case ICMP6_TOOBIG: | |
112 | ricmp->icmp6_err.mtu = htonl(IF_MTU); | |
113 | break; | |
114 | case ICMP6_PARAMPROB: | |
115 | /* TODO: Handle this case */ | |
116 | break; | |
117 | default: | |
118 | g_assert_not_reached(); | |
119 | break; | |
120 | } | |
121 | t->m_data += ICMP6_ERROR_MINLEN; | |
122 | memcpy(t->m_data, m->m_data, error_data_len); | |
123 | ||
124 | /* Checksum */ | |
125 | t->m_data -= ICMP6_ERROR_MINLEN; | |
126 | t->m_data -= sizeof(struct ip6); | |
127 | ricmp->icmp6_cksum = ip6_cksum(t); | |
128 | ||
129 | ip6_output(NULL, t, 0); | |
130 | } | |
131 | ||
0d6ff71a GS |
132 | /* |
133 | * Send NDP Router Advertisement | |
134 | */ | |
135 | void ndp_send_ra(Slirp *slirp) | |
136 | { | |
137 | DEBUG_CALL("ndp_send_ra"); | |
138 | ||
139 | /* Build IPv6 packet */ | |
140 | struct mbuf *t = m_get(slirp); | |
141 | struct ip6 *rip = mtod(t, struct ip6 *); | |
e42f869b | 142 | size_t pl_size = 0; |
a2f80fdf ST |
143 | struct in6_addr addr; |
144 | uint32_t scope_id; | |
145 | ||
0d6ff71a GS |
146 | rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR; |
147 | rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST; | |
148 | rip->ip_nh = IPPROTO_ICMPV6; | |
0d6ff71a GS |
149 | |
150 | /* Build ICMPv6 packet */ | |
151 | t->m_data += sizeof(struct ip6); | |
152 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
153 | ricmp->icmp6_type = ICMP6_NDP_RA; | |
154 | ricmp->icmp6_code = 0; | |
155 | ricmp->icmp6_cksum = 0; | |
156 | ||
157 | /* NDP */ | |
158 | ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit; | |
159 | ricmp->icmp6_nra.M = NDP_AdvManagedFlag; | |
160 | ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag; | |
161 | ricmp->icmp6_nra.reserved = 0; | |
162 | ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime); | |
163 | ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime); | |
164 | ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime); | |
f7725df3 | 165 | t->m_data += ICMP6_NDP_RA_MINLEN; |
e42f869b | 166 | pl_size += ICMP6_NDP_RA_MINLEN; |
0d6ff71a GS |
167 | |
168 | /* Source link-layer address (NDP option) */ | |
0d6ff71a GS |
169 | struct ndpopt *opt = mtod(t, struct ndpopt *); |
170 | opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE; | |
171 | opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; | |
172 | in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer); | |
f7725df3 | 173 | t->m_data += NDPOPT_LINKLAYER_LEN; |
e42f869b | 174 | pl_size += NDPOPT_LINKLAYER_LEN; |
0d6ff71a GS |
175 | |
176 | /* Prefix information (NDP option) */ | |
0d6ff71a GS |
177 | struct ndpopt *opt2 = mtod(t, struct ndpopt *); |
178 | opt2->ndpopt_type = NDPOPT_PREFIX_INFO; | |
179 | opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8; | |
180 | opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len; | |
181 | opt2->ndpopt_prefixinfo.L = 1; | |
182 | opt2->ndpopt_prefixinfo.A = 1; | |
183 | opt2->ndpopt_prefixinfo.reserved1 = 0; | |
184 | opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime); | |
185 | opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime); | |
186 | opt2->ndpopt_prefixinfo.reserved2 = 0; | |
187 | opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6; | |
f7725df3 | 188 | t->m_data += NDPOPT_PREFIXINFO_LEN; |
e42f869b | 189 | pl_size += NDPOPT_PREFIXINFO_LEN; |
f7725df3 | 190 | |
f7725df3 | 191 | /* Prefix information (NDP option) */ |
a2f80fdf ST |
192 | if (get_dns6_addr(&addr, &scope_id) >= 0) { |
193 | /* Host system does have an IPv6 DNS server, announce our proxy. */ | |
194 | struct ndpopt *opt3 = mtod(t, struct ndpopt *); | |
195 | opt3->ndpopt_type = NDPOPT_RDNSS; | |
196 | opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8; | |
197 | opt3->ndpopt_rdnss.reserved = 0; | |
198 | opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval); | |
199 | opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6; | |
200 | t->m_data += NDPOPT_RDNSS_LEN; | |
201 | pl_size += NDPOPT_RDNSS_LEN; | |
202 | } | |
0d6ff71a | 203 | |
e42f869b ST |
204 | rip->ip_pl = htons(pl_size); |
205 | t->m_data -= sizeof(struct ip6) + pl_size; | |
206 | t->m_len = sizeof(struct ip6) + pl_size; | |
207 | ||
0d6ff71a | 208 | /* ICMPv6 Checksum */ |
0d6ff71a GS |
209 | ricmp->icmp6_cksum = ip6_cksum(t); |
210 | ||
211 | ip6_output(NULL, t, 0); | |
212 | } | |
213 | ||
214 | /* | |
215 | * Send NDP Neighbor Solitication | |
216 | */ | |
217 | void ndp_send_ns(Slirp *slirp, struct in6_addr addr) | |
218 | { | |
0d6ff71a | 219 | char addrstr[INET6_ADDRSTRLEN]; |
df2ad332 | 220 | |
0d6ff71a | 221 | inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN); |
df2ad332 MAL |
222 | |
223 | DEBUG_CALL("ndp_send_ns"); | |
0d6ff71a | 224 | DEBUG_ARG("target = %s", addrstr); |
0d6ff71a GS |
225 | |
226 | /* Build IPv6 packet */ | |
227 | struct mbuf *t = m_get(slirp); | |
228 | struct ip6 *rip = mtod(t, struct ip6 *); | |
229 | rip->ip_src = slirp->vhost_addr6; | |
230 | rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX; | |
231 | memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3); | |
232 | rip->ip_nh = IPPROTO_ICMPV6; | |
233 | rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN); | |
234 | t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); | |
235 | ||
236 | /* Build ICMPv6 packet */ | |
237 | t->m_data += sizeof(struct ip6); | |
238 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
239 | ricmp->icmp6_type = ICMP6_NDP_NS; | |
240 | ricmp->icmp6_code = 0; | |
241 | ricmp->icmp6_cksum = 0; | |
242 | ||
243 | /* NDP */ | |
244 | ricmp->icmp6_nns.reserved = 0; | |
245 | ricmp->icmp6_nns.target = addr; | |
246 | ||
247 | /* Build NDP option */ | |
248 | t->m_data += ICMP6_NDP_NS_MINLEN; | |
249 | struct ndpopt *opt = mtod(t, struct ndpopt *); | |
250 | opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE; | |
251 | opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; | |
252 | in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer); | |
253 | ||
254 | /* ICMPv6 Checksum */ | |
255 | t->m_data -= ICMP6_NDP_NA_MINLEN; | |
256 | t->m_data -= sizeof(struct ip6); | |
257 | ricmp->icmp6_cksum = ip6_cksum(t); | |
258 | ||
259 | ip6_output(NULL, t, 1); | |
260 | } | |
261 | ||
262 | /* | |
263 | * Send NDP Neighbor Advertisement | |
264 | */ | |
265 | static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp) | |
266 | { | |
267 | /* Build IPv6 packet */ | |
268 | struct mbuf *t = m_get(slirp); | |
269 | struct ip6 *rip = mtod(t, struct ip6 *); | |
270 | rip->ip_src = icmp->icmp6_nns.target; | |
1120fae0 | 271 | if (in6_zero(&ip->ip_src)) { |
0d6ff71a GS |
272 | rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST; |
273 | } else { | |
274 | rip->ip_dst = ip->ip_src; | |
275 | } | |
276 | rip->ip_nh = IPPROTO_ICMPV6; | |
277 | rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN | |
278 | + NDPOPT_LINKLAYER_LEN); | |
279 | t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); | |
280 | ||
281 | /* Build ICMPv6 packet */ | |
282 | t->m_data += sizeof(struct ip6); | |
283 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
284 | ricmp->icmp6_type = ICMP6_NDP_NA; | |
285 | ricmp->icmp6_code = 0; | |
286 | ricmp->icmp6_cksum = 0; | |
287 | ||
288 | /* NDP */ | |
289 | ricmp->icmp6_nna.R = NDP_IsRouter; | |
290 | ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst); | |
291 | ricmp->icmp6_nna.O = 1; | |
292 | ricmp->icmp6_nna.reserved_hi = 0; | |
293 | ricmp->icmp6_nna.reserved_lo = 0; | |
294 | ricmp->icmp6_nna.target = icmp->icmp6_nns.target; | |
295 | ||
296 | /* Build NDP option */ | |
297 | t->m_data += ICMP6_NDP_NA_MINLEN; | |
298 | struct ndpopt *opt = mtod(t, struct ndpopt *); | |
299 | opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET; | |
300 | opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; | |
301 | in6_compute_ethaddr(ricmp->icmp6_nna.target, | |
302 | opt->ndpopt_linklayer); | |
303 | ||
304 | /* ICMPv6 Checksum */ | |
305 | t->m_data -= ICMP6_NDP_NA_MINLEN; | |
306 | t->m_data -= sizeof(struct ip6); | |
307 | ricmp->icmp6_cksum = ip6_cksum(t); | |
308 | ||
309 | ip6_output(NULL, t, 0); | |
310 | } | |
311 | ||
312 | /* | |
313 | * Process a NDP message | |
314 | */ | |
315 | static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip, | |
316 | struct icmp6 *icmp) | |
317 | { | |
318 | m->m_len += ETH_HLEN; | |
319 | m->m_data -= ETH_HLEN; | |
320 | struct ethhdr *eth = mtod(m, struct ethhdr *); | |
321 | m->m_len -= ETH_HLEN; | |
322 | m->m_data += ETH_HLEN; | |
323 | ||
324 | switch (icmp->icmp6_type) { | |
325 | case ICMP6_NDP_RS: | |
326 | DEBUG_CALL(" type = Router Solicitation"); | |
327 | if (ip->ip_hl == 255 | |
328 | && icmp->icmp6_code == 0 | |
329 | && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) { | |
330 | /* Gratuitous NDP */ | |
331 | ndp_table_add(slirp, ip->ip_src, eth->h_source); | |
332 | ||
333 | ndp_send_ra(slirp); | |
334 | } | |
335 | break; | |
336 | ||
337 | case ICMP6_NDP_RA: | |
338 | DEBUG_CALL(" type = Router Advertisement"); | |
3e0fad3a MAL |
339 | slirp->cb->guest_error("Warning: guest sent NDP RA, but shouldn't", |
340 | slirp->opaque); | |
0d6ff71a GS |
341 | break; |
342 | ||
343 | case ICMP6_NDP_NS: | |
344 | DEBUG_CALL(" type = Neighbor Solicitation"); | |
345 | if (ip->ip_hl == 255 | |
346 | && icmp->icmp6_code == 0 | |
347 | && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target) | |
348 | && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN | |
1120fae0 | 349 | && (!in6_zero(&ip->ip_src) |
0d6ff71a GS |
350 | || in6_solicitednode_multicast(&ip->ip_dst))) { |
351 | if (in6_equal_host(&icmp->icmp6_nns.target)) { | |
352 | /* Gratuitous NDP */ | |
353 | ndp_table_add(slirp, ip->ip_src, eth->h_source); | |
354 | ndp_send_na(slirp, ip, icmp); | |
355 | } | |
356 | } | |
357 | break; | |
358 | ||
359 | case ICMP6_NDP_NA: | |
360 | DEBUG_CALL(" type = Neighbor Advertisement"); | |
361 | if (ip->ip_hl == 255 | |
362 | && icmp->icmp6_code == 0 | |
363 | && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN | |
364 | && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target) | |
365 | && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst) | |
366 | || icmp->icmp6_nna.S == 0)) { | |
367 | ndp_table_add(slirp, ip->ip_src, eth->h_source); | |
368 | } | |
369 | break; | |
370 | ||
371 | case ICMP6_NDP_REDIRECT: | |
372 | DEBUG_CALL(" type = Redirect"); | |
2addc8fb | 373 | slirp->cb->guest_error( |
3e0fad3a | 374 | "Warning: guest sent NDP REDIRECT, but shouldn't", slirp->opaque); |
0d6ff71a GS |
375 | break; |
376 | } | |
377 | } | |
378 | ||
379 | /* | |
380 | * Process a received ICMPv6 message. | |
381 | */ | |
382 | void icmp6_input(struct mbuf *m) | |
383 | { | |
384 | struct icmp6 *icmp; | |
385 | struct ip6 *ip = mtod(m, struct ip6 *); | |
386 | Slirp *slirp = m->slirp; | |
387 | int hlen = sizeof(struct ip6); | |
388 | ||
389 | DEBUG_CALL("icmp6_input"); | |
3402618c | 390 | DEBUG_ARG("m = %p", m); |
0d6ff71a GS |
391 | DEBUG_ARG("m_len = %d", m->m_len); |
392 | ||
393 | if (ntohs(ip->ip_pl) < ICMP6_MINLEN) { | |
394 | goto end; | |
395 | } | |
396 | ||
397 | if (ip6_cksum(m)) { | |
398 | goto end; | |
399 | } | |
400 | ||
401 | m->m_len -= hlen; | |
402 | m->m_data += hlen; | |
403 | icmp = mtod(m, struct icmp6 *); | |
404 | m->m_len += hlen; | |
405 | m->m_data -= hlen; | |
406 | ||
407 | DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type); | |
408 | switch (icmp->icmp6_type) { | |
409 | case ICMP6_ECHO_REQUEST: | |
410 | if (in6_equal_host(&ip->ip_dst)) { | |
411 | icmp6_send_echoreply(m, slirp, ip, icmp); | |
412 | } else { | |
413 | /* TODO */ | |
90dfa278 | 414 | g_critical("external icmpv6 not supported yet"); |
0d6ff71a GS |
415 | } |
416 | break; | |
417 | ||
418 | case ICMP6_NDP_RS: | |
419 | case ICMP6_NDP_RA: | |
420 | case ICMP6_NDP_NS: | |
421 | case ICMP6_NDP_NA: | |
422 | case ICMP6_NDP_REDIRECT: | |
423 | ndp_input(m, slirp, ip, icmp); | |
424 | break; | |
425 | ||
426 | case ICMP6_UNREACH: | |
427 | case ICMP6_TOOBIG: | |
428 | case ICMP6_TIMXCEED: | |
429 | case ICMP6_PARAMPROB: | |
430 | /* XXX? report error? close socket? */ | |
431 | default: | |
432 | break; | |
433 | } | |
434 | ||
435 | end: | |
436 | m_free(m); | |
437 | } |