]> Git Repo - qemu.git/blob - slirp/ip6_icmp.c
slirp: Adding IPv6, ICMPv6 Echo and NDP autoconfiguration
[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 #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     slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ra_timer_handler, slirp);
28     timer_mod(slirp->ra_timer,
29               qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
30 }
31
32 void icmp6_cleanup(Slirp *slirp)
33 {
34     timer_del(slirp->ra_timer);
35     timer_free(slirp->ra_timer);
36 }
37
38 static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
39         struct icmp6 *icmp)
40 {
41     struct mbuf *t = m_get(slirp);
42     t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
43     memcpy(t->m_data, m->m_data, t->m_len);
44
45     /* IPv6 Packet */
46     struct ip6 *rip = mtod(t, struct ip6 *);
47     rip->ip_dst = ip->ip_src;
48     rip->ip_src = ip->ip_dst;
49
50     /* ICMPv6 packet */
51     t->m_data += sizeof(struct ip6);
52     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
53     ricmp->icmp6_type = ICMP6_ECHO_REPLY;
54     ricmp->icmp6_cksum = 0;
55
56     /* Checksum */
57     t->m_data -= sizeof(struct ip6);
58     ricmp->icmp6_cksum = ip6_cksum(t);
59
60     ip6_output(NULL, t, 0);
61 }
62
63 /*
64  * Send NDP Router Advertisement
65  */
66 void ndp_send_ra(Slirp *slirp)
67 {
68     DEBUG_CALL("ndp_send_ra");
69
70     /* Build IPv6 packet */
71     struct mbuf *t = m_get(slirp);
72     struct ip6 *rip = mtod(t, struct ip6 *);
73     rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
74     rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
75     rip->ip_nh = IPPROTO_ICMPV6;
76     rip->ip_pl = htons(ICMP6_NDP_RA_MINLEN
77                         + NDPOPT_LINKLAYER_LEN
78                         + NDPOPT_PREFIXINFO_LEN);
79     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
80
81     /* Build ICMPv6 packet */
82     t->m_data += sizeof(struct ip6);
83     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
84     ricmp->icmp6_type = ICMP6_NDP_RA;
85     ricmp->icmp6_code = 0;
86     ricmp->icmp6_cksum = 0;
87
88     /* NDP */
89     ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
90     ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
91     ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
92     ricmp->icmp6_nra.reserved = 0;
93     ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
94     ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
95     ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
96
97     /* Source link-layer address (NDP option) */
98     t->m_data += ICMP6_NDP_RA_MINLEN;
99     struct ndpopt *opt = mtod(t, struct ndpopt *);
100     opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
101     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
102     in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
103
104     /* Prefix information (NDP option) */
105     t->m_data += NDPOPT_LINKLAYER_LEN;
106     struct ndpopt *opt2 = mtod(t, struct ndpopt *);
107     opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
108     opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
109     opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
110     opt2->ndpopt_prefixinfo.L = 1;
111     opt2->ndpopt_prefixinfo.A = 1;
112     opt2->ndpopt_prefixinfo.reserved1 = 0;
113     opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
114     opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
115     opt2->ndpopt_prefixinfo.reserved2 = 0;
116     opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
117
118     /* ICMPv6 Checksum */
119     t->m_data -= NDPOPT_LINKLAYER_LEN;
120     t->m_data -= ICMP6_NDP_RA_MINLEN;
121     t->m_data -= sizeof(struct ip6);
122     ricmp->icmp6_cksum = ip6_cksum(t);
123
124     ip6_output(NULL, t, 0);
125 }
126
127 /*
128  * Send NDP Neighbor Solitication
129  */
130 void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
131 {
132     DEBUG_CALL("ndp_send_ns");
133 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
134     char addrstr[INET6_ADDRSTRLEN];
135     inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
136     DEBUG_ARG("target = %s", addrstr);
137 #endif
138
139     /* Build IPv6 packet */
140     struct mbuf *t = m_get(slirp);
141     struct ip6 *rip = mtod(t, struct ip6 *);
142     rip->ip_src = slirp->vhost_addr6;
143     rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
144     memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
145     rip->ip_nh = IPPROTO_ICMPV6;
146     rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
147     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
148
149     /* Build ICMPv6 packet */
150     t->m_data += sizeof(struct ip6);
151     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
152     ricmp->icmp6_type = ICMP6_NDP_NS;
153     ricmp->icmp6_code = 0;
154     ricmp->icmp6_cksum = 0;
155
156     /* NDP */
157     ricmp->icmp6_nns.reserved = 0;
158     ricmp->icmp6_nns.target = addr;
159
160     /* Build NDP option */
161     t->m_data += ICMP6_NDP_NS_MINLEN;
162     struct ndpopt *opt = mtod(t, struct ndpopt *);
163     opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
164     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
165     in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
166
167     /* ICMPv6 Checksum */
168     t->m_data -= ICMP6_NDP_NA_MINLEN;
169     t->m_data -= sizeof(struct ip6);
170     ricmp->icmp6_cksum = ip6_cksum(t);
171
172     ip6_output(NULL, t, 1);
173 }
174
175 /*
176  * Send NDP Neighbor Advertisement
177  */
178 static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
179 {
180     /* Build IPv6 packet */
181     struct mbuf *t = m_get(slirp);
182     struct ip6 *rip = mtod(t, struct ip6 *);
183     rip->ip_src = icmp->icmp6_nns.target;
184     if (IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
185         rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
186     } else {
187         rip->ip_dst = ip->ip_src;
188     }
189     rip->ip_nh = IPPROTO_ICMPV6;
190     rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
191                         + NDPOPT_LINKLAYER_LEN);
192     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
193
194     /* Build ICMPv6 packet */
195     t->m_data += sizeof(struct ip6);
196     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
197     ricmp->icmp6_type = ICMP6_NDP_NA;
198     ricmp->icmp6_code = 0;
199     ricmp->icmp6_cksum = 0;
200
201     /* NDP */
202     ricmp->icmp6_nna.R = NDP_IsRouter;
203     ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
204     ricmp->icmp6_nna.O = 1;
205     ricmp->icmp6_nna.reserved_hi = 0;
206     ricmp->icmp6_nna.reserved_lo = 0;
207     ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
208
209     /* Build NDP option */
210     t->m_data += ICMP6_NDP_NA_MINLEN;
211     struct ndpopt *opt = mtod(t, struct ndpopt *);
212     opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
213     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
214     in6_compute_ethaddr(ricmp->icmp6_nna.target,
215                     opt->ndpopt_linklayer);
216
217     /* ICMPv6 Checksum */
218     t->m_data -= ICMP6_NDP_NA_MINLEN;
219     t->m_data -= sizeof(struct ip6);
220     ricmp->icmp6_cksum = ip6_cksum(t);
221
222     ip6_output(NULL, t, 0);
223 }
224
225 /*
226  * Process a NDP message
227  */
228 static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
229         struct icmp6 *icmp)
230 {
231     m->m_len += ETH_HLEN;
232     m->m_data -= ETH_HLEN;
233     struct ethhdr *eth = mtod(m, struct ethhdr *);
234     m->m_len -= ETH_HLEN;
235     m->m_data += ETH_HLEN;
236
237     switch (icmp->icmp6_type) {
238     case ICMP6_NDP_RS:
239         DEBUG_CALL(" type = Router Solicitation");
240         if (ip->ip_hl == 255
241                 && icmp->icmp6_code == 0
242                 && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
243             /* Gratuitous NDP */
244             ndp_table_add(slirp, ip->ip_src, eth->h_source);
245
246             ndp_send_ra(slirp);
247         }
248         break;
249
250     case ICMP6_NDP_RA:
251         DEBUG_CALL(" type = Router Advertisement");
252         qemu_log_mask(LOG_GUEST_ERROR,
253                 "Warning: guest sent NDP RA, but shouldn't");
254         break;
255
256     case ICMP6_NDP_NS:
257         DEBUG_CALL(" type = Neighbor Solicitation");
258         if (ip->ip_hl == 255
259                 && icmp->icmp6_code == 0
260                 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
261                 && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
262                 && (!IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)
263                     || in6_solicitednode_multicast(&ip->ip_dst))) {
264             if (in6_equal_host(&icmp->icmp6_nns.target)) {
265                 /* Gratuitous NDP */
266                 ndp_table_add(slirp, ip->ip_src, eth->h_source);
267                 ndp_send_na(slirp, ip, icmp);
268             }
269         }
270         break;
271
272     case ICMP6_NDP_NA:
273         DEBUG_CALL(" type = Neighbor Advertisement");
274         if (ip->ip_hl == 255
275                 && icmp->icmp6_code == 0
276                 && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
277                 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
278                 && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
279                     || icmp->icmp6_nna.S == 0)) {
280             ndp_table_add(slirp, ip->ip_src, eth->h_source);
281         }
282         break;
283
284     case ICMP6_NDP_REDIRECT:
285         DEBUG_CALL(" type = Redirect");
286         qemu_log_mask(LOG_GUEST_ERROR,
287                 "Warning: guest sent NDP REDIRECT, but shouldn't");
288         break;
289     }
290 }
291
292 /*
293  * Process a received ICMPv6 message.
294  */
295 void icmp6_input(struct mbuf *m)
296 {
297     struct icmp6 *icmp;
298     struct ip6 *ip = mtod(m, struct ip6 *);
299     Slirp *slirp = m->slirp;
300     int hlen = sizeof(struct ip6);
301
302     DEBUG_CALL("icmp6_input");
303     DEBUG_ARG("m = %lx", (long) m);
304     DEBUG_ARG("m_len = %d", m->m_len);
305
306     if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
307         goto end;
308     }
309
310     if (ip6_cksum(m)) {
311         goto end;
312     }
313
314     m->m_len -= hlen;
315     m->m_data += hlen;
316     icmp = mtod(m, struct icmp6 *);
317     m->m_len += hlen;
318     m->m_data -= hlen;
319
320     DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
321     switch (icmp->icmp6_type) {
322     case ICMP6_ECHO_REQUEST:
323         if (in6_equal_host(&ip->ip_dst)) {
324             icmp6_send_echoreply(m, slirp, ip, icmp);
325         } else {
326             /* TODO */
327             error_report("external icmpv6 not supported yet");
328         }
329         break;
330
331     case ICMP6_NDP_RS:
332     case ICMP6_NDP_RA:
333     case ICMP6_NDP_NS:
334     case ICMP6_NDP_NA:
335     case ICMP6_NDP_REDIRECT:
336         ndp_input(m, slirp, ip, icmp);
337         break;
338
339     case ICMP6_UNREACH:
340     case ICMP6_TOOBIG:
341     case ICMP6_TIMXCEED:
342     case ICMP6_PARAMPROB:
343         /* XXX? report error? close socket? */
344     default:
345         break;
346     }
347
348 end:
349     m_free(m);
350 }
This page took 0.043474 seconds and 4 git commands to generate.