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