]> git.proxmox.com Git - mirror_qemu.git/blame - slirp/ip6_icmp.c
block: Require auto-read-only for existing fallbacks
[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,
05ff8dc3 20 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
0d6ff71a
GS
21 ndp_send_ra(slirp);
22}
23
24void icmp6_init(Slirp *slirp)
25{
0b11c036
ST
26 if (!slirp->in6_enabled) {
27 return;
28 }
29
e81f8679
AP
30 slirp->ra_timer = timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
31 SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
32 ra_timer_handler, slirp);
0d6ff71a 33 timer_mod(slirp->ra_timer,
05ff8dc3 34 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
0d6ff71a
GS
35}
36
37void icmp6_cleanup(Slirp *slirp)
38{
0b11c036
ST
39 if (!slirp->in6_enabled) {
40 return;
41 }
42
0d6ff71a
GS
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 /* IPv6 Packet */
55 struct ip6 *rip = mtod(t, struct ip6 *);
56 rip->ip_dst = ip->ip_src;
57 rip->ip_src = ip->ip_dst;
58
59 /* ICMPv6 packet */
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 /* Checksum */
66 t->m_data -= sizeof(struct ip6);
67 ricmp->icmp6_cksum = ip6_cksum(t);
68
69 ip6_output(NULL, t, 0);
70}
71
fc6c9257
YB
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) ||
1120fae0 82 in6_zero(&ip->ip_src)) {
fc6c9257
YB
83 /* TODO icmp error? */
84 return;
85 }
86
87 t = m_get(slirp);
88
89 /* IPv6 packet */
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;
893dcdbf 100 const int error_data_len = MIN(m->m_len,
fc6c9257
YB
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 /* ICMPv6 packet */
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 /* TODO: Handle this case */
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 /* Checksum */
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
0d6ff71a
GS
138/*
139 * Send NDP Router Advertisement
140 */
141void ndp_send_ra(Slirp *slirp)
142{
143 DEBUG_CALL("ndp_send_ra");
144
145 /* Build IPv6 packet */
146 struct mbuf *t = m_get(slirp);
147 struct ip6 *rip = mtod(t, struct ip6 *);
e42f869b 148 size_t pl_size = 0;
a2f80fdf
ST
149 struct in6_addr addr;
150 uint32_t scope_id;
151
0d6ff71a
GS
152 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
153 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
154 rip->ip_nh = IPPROTO_ICMPV6;
0d6ff71a
GS
155
156 /* Build ICMPv6 packet */
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 /* NDP */
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);
f7725df3 171 t->m_data += ICMP6_NDP_RA_MINLEN;
e42f869b 172 pl_size += ICMP6_NDP_RA_MINLEN;
0d6ff71a
GS
173
174 /* Source link-layer address (NDP option) */
0d6ff71a
GS
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);
f7725df3 179 t->m_data += NDPOPT_LINKLAYER_LEN;
e42f869b 180 pl_size += 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 194 t->m_data += NDPOPT_PREFIXINFO_LEN;
e42f869b 195 pl_size += NDPOPT_PREFIXINFO_LEN;
f7725df3 196
f7725df3 197 /* Prefix information (NDP option) */
a2f80fdf
ST
198 if (get_dns6_addr(&addr, &scope_id) >= 0) {
199 /* Host system does have an IPv6 DNS server, announce our proxy. */
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 }
0d6ff71a 209
e42f869b
ST
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
0d6ff71a 214 /* ICMPv6 Checksum */
0d6ff71a
GS
215 ricmp->icmp6_cksum = ip6_cksum(t);
216
217 ip6_output(NULL, t, 0);
218}
219
220/*
221 * Send NDP Neighbor Solitication
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 /* Build IPv6 packet */
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 /* Build ICMPv6 packet */
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 /* NDP */
250 ricmp->icmp6_nns.reserved = 0;
251 ricmp->icmp6_nns.target = addr;
252
253 /* Build NDP option */
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 /* ICMPv6 Checksum */
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 * Send NDP Neighbor Advertisement
270 */
271static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
272{
273 /* Build IPv6 packet */
274 struct mbuf *t = m_get(slirp);
275 struct ip6 *rip = mtod(t, struct ip6 *);
276 rip->ip_src = icmp->icmp6_nns.target;
1120fae0 277 if (in6_zero(&ip->ip_src)) {
0d6ff71a
GS
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 /* Build ICMPv6 packet */
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 /* NDP */
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 /* Build NDP option */
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 /* ICMPv6 Checksum */
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 * Process a NDP message
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 /* Gratuitous NDP */
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
1120fae0 355 && (!in6_zero(&ip->ip_src)
0d6ff71a
GS
356 || in6_solicitednode_multicast(&ip->ip_dst))) {
357 if (in6_equal_host(&icmp->icmp6_nns.target)) {
358 /* Gratuitous NDP */
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 * Process a received ICMPv6 message.
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 /* TODO */
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 /* XXX? report error? close socket? */
437 default:
438 break;
439 }
440
441end:
442 m_free(m);
443}