]> Git Repo - qemu.git/blob - slirp/ip6_icmp.c
Merge remote-tracking branch 'remotes/thibault/tags/samuel-thibault' into staging
[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     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
153
154     /* Build ICMPv6 packet */
155     t->m_data += sizeof(struct ip6);
156     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
157     ricmp->icmp6_type = ICMP6_NDP_RA;
158     ricmp->icmp6_code = 0;
159     ricmp->icmp6_cksum = 0;
160
161     /* NDP */
162     ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
163     ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
164     ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
165     ricmp->icmp6_nra.reserved = 0;
166     ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
167     ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
168     ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
169
170     /* Source link-layer address (NDP option) */
171     t->m_data += ICMP6_NDP_RA_MINLEN;
172     struct ndpopt *opt = mtod(t, struct ndpopt *);
173     opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
174     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
175     in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
176
177     /* Prefix information (NDP option) */
178     t->m_data += NDPOPT_LINKLAYER_LEN;
179     struct ndpopt *opt2 = mtod(t, struct ndpopt *);
180     opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
181     opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
182     opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
183     opt2->ndpopt_prefixinfo.L = 1;
184     opt2->ndpopt_prefixinfo.A = 1;
185     opt2->ndpopt_prefixinfo.reserved1 = 0;
186     opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
187     opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
188     opt2->ndpopt_prefixinfo.reserved2 = 0;
189     opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
190
191     /* ICMPv6 Checksum */
192     t->m_data -= NDPOPT_LINKLAYER_LEN;
193     t->m_data -= ICMP6_NDP_RA_MINLEN;
194     t->m_data -= sizeof(struct ip6);
195     ricmp->icmp6_cksum = ip6_cksum(t);
196
197     ip6_output(NULL, t, 0);
198 }
199
200 /*
201  * Send NDP Neighbor Solitication
202  */
203 void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
204 {
205     DEBUG_CALL("ndp_send_ns");
206 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
207     char addrstr[INET6_ADDRSTRLEN];
208     inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
209     DEBUG_ARG("target = %s", addrstr);
210 #endif
211
212     /* Build IPv6 packet */
213     struct mbuf *t = m_get(slirp);
214     struct ip6 *rip = mtod(t, struct ip6 *);
215     rip->ip_src = slirp->vhost_addr6;
216     rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
217     memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
218     rip->ip_nh = IPPROTO_ICMPV6;
219     rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
220     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
221
222     /* Build ICMPv6 packet */
223     t->m_data += sizeof(struct ip6);
224     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
225     ricmp->icmp6_type = ICMP6_NDP_NS;
226     ricmp->icmp6_code = 0;
227     ricmp->icmp6_cksum = 0;
228
229     /* NDP */
230     ricmp->icmp6_nns.reserved = 0;
231     ricmp->icmp6_nns.target = addr;
232
233     /* Build NDP option */
234     t->m_data += ICMP6_NDP_NS_MINLEN;
235     struct ndpopt *opt = mtod(t, struct ndpopt *);
236     opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
237     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
238     in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
239
240     /* ICMPv6 Checksum */
241     t->m_data -= ICMP6_NDP_NA_MINLEN;
242     t->m_data -= sizeof(struct ip6);
243     ricmp->icmp6_cksum = ip6_cksum(t);
244
245     ip6_output(NULL, t, 1);
246 }
247
248 /*
249  * Send NDP Neighbor Advertisement
250  */
251 static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
252 {
253     /* Build IPv6 packet */
254     struct mbuf *t = m_get(slirp);
255     struct ip6 *rip = mtod(t, struct ip6 *);
256     rip->ip_src = icmp->icmp6_nns.target;
257     if (IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
258         rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
259     } else {
260         rip->ip_dst = ip->ip_src;
261     }
262     rip->ip_nh = IPPROTO_ICMPV6;
263     rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
264                         + NDPOPT_LINKLAYER_LEN);
265     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
266
267     /* Build ICMPv6 packet */
268     t->m_data += sizeof(struct ip6);
269     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
270     ricmp->icmp6_type = ICMP6_NDP_NA;
271     ricmp->icmp6_code = 0;
272     ricmp->icmp6_cksum = 0;
273
274     /* NDP */
275     ricmp->icmp6_nna.R = NDP_IsRouter;
276     ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
277     ricmp->icmp6_nna.O = 1;
278     ricmp->icmp6_nna.reserved_hi = 0;
279     ricmp->icmp6_nna.reserved_lo = 0;
280     ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
281
282     /* Build NDP option */
283     t->m_data += ICMP6_NDP_NA_MINLEN;
284     struct ndpopt *opt = mtod(t, struct ndpopt *);
285     opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
286     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
287     in6_compute_ethaddr(ricmp->icmp6_nna.target,
288                     opt->ndpopt_linklayer);
289
290     /* ICMPv6 Checksum */
291     t->m_data -= ICMP6_NDP_NA_MINLEN;
292     t->m_data -= sizeof(struct ip6);
293     ricmp->icmp6_cksum = ip6_cksum(t);
294
295     ip6_output(NULL, t, 0);
296 }
297
298 /*
299  * Process a NDP message
300  */
301 static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
302         struct icmp6 *icmp)
303 {
304     m->m_len += ETH_HLEN;
305     m->m_data -= ETH_HLEN;
306     struct ethhdr *eth = mtod(m, struct ethhdr *);
307     m->m_len -= ETH_HLEN;
308     m->m_data += ETH_HLEN;
309
310     switch (icmp->icmp6_type) {
311     case ICMP6_NDP_RS:
312         DEBUG_CALL(" type = Router Solicitation");
313         if (ip->ip_hl == 255
314                 && icmp->icmp6_code == 0
315                 && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
316             /* Gratuitous NDP */
317             ndp_table_add(slirp, ip->ip_src, eth->h_source);
318
319             ndp_send_ra(slirp);
320         }
321         break;
322
323     case ICMP6_NDP_RA:
324         DEBUG_CALL(" type = Router Advertisement");
325         qemu_log_mask(LOG_GUEST_ERROR,
326                 "Warning: guest sent NDP RA, but shouldn't");
327         break;
328
329     case ICMP6_NDP_NS:
330         DEBUG_CALL(" type = Neighbor Solicitation");
331         if (ip->ip_hl == 255
332                 && icmp->icmp6_code == 0
333                 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
334                 && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
335                 && (!IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)
336                     || in6_solicitednode_multicast(&ip->ip_dst))) {
337             if (in6_equal_host(&icmp->icmp6_nns.target)) {
338                 /* Gratuitous NDP */
339                 ndp_table_add(slirp, ip->ip_src, eth->h_source);
340                 ndp_send_na(slirp, ip, icmp);
341             }
342         }
343         break;
344
345     case ICMP6_NDP_NA:
346         DEBUG_CALL(" type = Neighbor Advertisement");
347         if (ip->ip_hl == 255
348                 && icmp->icmp6_code == 0
349                 && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
350                 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
351                 && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
352                     || icmp->icmp6_nna.S == 0)) {
353             ndp_table_add(slirp, ip->ip_src, eth->h_source);
354         }
355         break;
356
357     case ICMP6_NDP_REDIRECT:
358         DEBUG_CALL(" type = Redirect");
359         qemu_log_mask(LOG_GUEST_ERROR,
360                 "Warning: guest sent NDP REDIRECT, but shouldn't");
361         break;
362     }
363 }
364
365 /*
366  * Process a received ICMPv6 message.
367  */
368 void icmp6_input(struct mbuf *m)
369 {
370     struct icmp6 *icmp;
371     struct ip6 *ip = mtod(m, struct ip6 *);
372     Slirp *slirp = m->slirp;
373     int hlen = sizeof(struct ip6);
374
375     DEBUG_CALL("icmp6_input");
376     DEBUG_ARG("m = %lx", (long) m);
377     DEBUG_ARG("m_len = %d", m->m_len);
378
379     if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
380         goto end;
381     }
382
383     if (ip6_cksum(m)) {
384         goto end;
385     }
386
387     m->m_len -= hlen;
388     m->m_data += hlen;
389     icmp = mtod(m, struct icmp6 *);
390     m->m_len += hlen;
391     m->m_data -= hlen;
392
393     DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
394     switch (icmp->icmp6_type) {
395     case ICMP6_ECHO_REQUEST:
396         if (in6_equal_host(&ip->ip_dst)) {
397             icmp6_send_echoreply(m, slirp, ip, icmp);
398         } else {
399             /* TODO */
400             error_report("external icmpv6 not supported yet");
401         }
402         break;
403
404     case ICMP6_NDP_RS:
405     case ICMP6_NDP_RA:
406     case ICMP6_NDP_NS:
407     case ICMP6_NDP_NA:
408     case ICMP6_NDP_REDIRECT:
409         ndp_input(m, slirp, ip, icmp);
410         break;
411
412     case ICMP6_UNREACH:
413     case ICMP6_TOOBIG:
414     case ICMP6_TIMXCEED:
415     case ICMP6_PARAMPROB:
416         /* XXX? report error? close socket? */
417     default:
418         break;
419     }
420
421 end:
422     m_free(m);
423 }
This page took 0.049967 seconds and 4 git commands to generate.