package: update version (2.8.0.6)
[sdk/emulator/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
13 #define NDP_Interval g_rand_int_range(slirp->grand, \
14         NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
15
16 static 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
24 void icmp6_init(Slirp *slirp)
25 {
26     if (!slirp->in6_enabled) {
27         return;
28     }
29
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
35 void icmp6_cleanup(Slirp *slirp)
36 {
37     if (!slirp->in6_enabled) {
38         return;
39     }
40
41     timer_del(slirp->ra_timer);
42     timer_free(slirp->ra_timer);
43 }
44
45 static 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
70 void 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;
98     const int error_data_len = min(m->m_len,
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
136 /*
137  * Send NDP Router Advertisement
138  */
139 void 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
151                         + NDPOPT_PREFIXINFO_LEN
152 #ifndef _WIN32
153                         + NDPOPT_RDNSS_LEN
154 #endif
155                         );
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);
173     t->m_data += ICMP6_NDP_RA_MINLEN;
174
175     /* Source link-layer address (NDP option) */
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);
180     t->m_data += NDPOPT_LINKLAYER_LEN;
181
182     /* Prefix information (NDP option) */
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
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
207
208     /* ICMPv6 Checksum */
209 #ifndef _WIN32
210     t->m_data -= NDPOPT_RDNSS_LEN;
211 #endif
212     t->m_data -= NDPOPT_PREFIXINFO_LEN;
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  */
224 void 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  */
272 static 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  */
322 static 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  */
389 void 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
442 end:
443     m_free(m);
444 }