]> Git Repo - qemu.git/blob - slirp/ip6_icmp.c
slirp: add tftp tracing
[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_full(NULL, QEMU_CLOCK_VIRTUAL,
31                                      SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
32                                      ra_timer_handler, slirp);
33     timer_mod(slirp->ra_timer,
34               qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
35 }
36
37 void icmp6_cleanup(Slirp *slirp)
38 {
39     if (!slirp->in6_enabled) {
40         return;
41     }
42
43     timer_del(slirp->ra_timer);
44     timer_free(slirp->ra_timer);
45 }
46
47 static 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
72 void 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) ||
82             in6_zero(&ip->ip_src)) {
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;
100     const int error_data_len = MIN(m->m_len,
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
138 /*
139  * Send NDP Router Advertisement
140  */
141 void 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 *);
148     size_t pl_size = 0;
149     struct in6_addr addr;
150     uint32_t scope_id;
151
152     rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
153     rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
154     rip->ip_nh = IPPROTO_ICMPV6;
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);
171     t->m_data += ICMP6_NDP_RA_MINLEN;
172     pl_size += ICMP6_NDP_RA_MINLEN;
173
174     /* Source link-layer address (NDP option) */
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);
179     t->m_data += NDPOPT_LINKLAYER_LEN;
180     pl_size += 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     pl_size += NDPOPT_PREFIXINFO_LEN;
196
197     /* Prefix information (NDP option) */
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     }
209
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
214     /* ICMPv6 Checksum */
215     ricmp->icmp6_cksum = ip6_cksum(t);
216
217     ip6_output(NULL, t, 0);
218 }
219
220 /*
221  * Send NDP Neighbor Solitication
222  */
223 void 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  */
271 static 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;
277     if (in6_zero(&ip->ip_src)) {
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  */
321 static 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
355                 && (!in6_zero(&ip->ip_src)
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  */
388 void 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
441 end:
442     m_free(m);
443 }
This page took 0.047822 seconds and 4 git commands to generate.