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