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