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