These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / 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     if (!slirp->in6_enabled) {
28         return;
29     }
30
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
36 void icmp6_cleanup(Slirp *slirp)
37 {
38     if (!slirp->in6_enabled) {
39         return;
40     }
41
42     timer_del(slirp->ra_timer);
43     timer_free(slirp->ra_timer);
44 }
45
46 static 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
71 void 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
137 /*
138  * Send NDP Router Advertisement
139  */
140 void 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  */
204 void 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  */
252 static 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  */
302 static 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  */
369 void 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
422 end:
423     m_free(m);
424 }