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