]> Git Repo - qemu.git/blob - slirp/ip6_icmp.c
sd: express dependencies with kconfig
[qemu.git] / slirp / ip6_icmp.c
1 /*
2  * Copyright (c) 2013
3  * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
4  */
5
6 #include "slirp.h"
7 #include "ip6_icmp.h"
8
9 #define NDP_Interval g_rand_int_range(slirp->grand, \
10         NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
11
12 static void ra_timer_handler(void *opaque)
13 {
14     Slirp *slirp = opaque;
15
16     slirp->cb->timer_mod(slirp->ra_timer,
17         slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval,
18         slirp->opaque);
19     ndp_send_ra(slirp);
20 }
21
22 void icmp6_init(Slirp *slirp)
23 {
24     if (!slirp->in6_enabled) {
25         return;
26     }
27
28     slirp->ra_timer = slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque);
29     slirp->cb->timer_mod(slirp->ra_timer,
30         slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval,
31         slirp->opaque);
32 }
33
34 void icmp6_cleanup(Slirp *slirp)
35 {
36     if (!slirp->in6_enabled) {
37         return;
38     }
39
40     slirp->cb->timer_free(slirp->ra_timer, slirp->opaque);
41 }
42
43 static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
44         struct icmp6 *icmp)
45 {
46     struct mbuf *t = m_get(slirp);
47     t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
48     memcpy(t->m_data, m->m_data, t->m_len);
49
50     /* IPv6 Packet */
51     struct ip6 *rip = mtod(t, struct ip6 *);
52     rip->ip_dst = ip->ip_src;
53     rip->ip_src = ip->ip_dst;
54
55     /* ICMPv6 packet */
56     t->m_data += sizeof(struct ip6);
57     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
58     ricmp->icmp6_type = ICMP6_ECHO_REPLY;
59     ricmp->icmp6_cksum = 0;
60
61     /* Checksum */
62     t->m_data -= sizeof(struct ip6);
63     ricmp->icmp6_cksum = ip6_cksum(t);
64
65     ip6_output(NULL, t, 0);
66 }
67
68 void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
69 {
70     Slirp *slirp = m->slirp;
71     struct mbuf *t;
72     struct ip6 *ip = mtod(m, struct ip6 *);
73     char addrstr[INET6_ADDRSTRLEN];
74
75     DEBUG_CALL("icmp6_send_error");
76     DEBUG_ARG("type = %d, code = %d", type, code);
77
78     if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
79             in6_zero(&ip->ip_src)) {
80         /* TODO icmp error? */
81         return;
82     }
83
84     t = m_get(slirp);
85
86     /* IPv6 packet */
87     struct ip6 *rip = mtod(t, struct ip6 *);
88     rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
89     rip->ip_dst = ip->ip_src;
90     inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
91     DEBUG_ARG("target = %s", addrstr);
92
93     rip->ip_nh = IPPROTO_ICMPV6;
94     const int error_data_len = MIN(m->m_len,
95             IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
96     rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
97     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
98
99     /* ICMPv6 packet */
100     t->m_data += sizeof(struct ip6);
101     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
102     ricmp->icmp6_type = type;
103     ricmp->icmp6_code = code;
104     ricmp->icmp6_cksum = 0;
105
106     switch (type) {
107     case ICMP6_UNREACH:
108     case ICMP6_TIMXCEED:
109         ricmp->icmp6_err.unused = 0;
110         break;
111     case ICMP6_TOOBIG:
112         ricmp->icmp6_err.mtu = htonl(IF_MTU);
113         break;
114     case ICMP6_PARAMPROB:
115         /* TODO: Handle this case */
116         break;
117     default:
118         g_assert_not_reached();
119         break;
120     }
121     t->m_data += ICMP6_ERROR_MINLEN;
122     memcpy(t->m_data, m->m_data, error_data_len);
123
124     /* Checksum */
125     t->m_data -= ICMP6_ERROR_MINLEN;
126     t->m_data -= sizeof(struct ip6);
127     ricmp->icmp6_cksum = ip6_cksum(t);
128
129     ip6_output(NULL, t, 0);
130 }
131
132 /*
133  * Send NDP Router Advertisement
134  */
135 void ndp_send_ra(Slirp *slirp)
136 {
137     DEBUG_CALL("ndp_send_ra");
138
139     /* Build IPv6 packet */
140     struct mbuf *t = m_get(slirp);
141     struct ip6 *rip = mtod(t, struct ip6 *);
142     size_t pl_size = 0;
143     struct in6_addr addr;
144     uint32_t scope_id;
145
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
150     /* Build ICMPv6 packet */
151     t->m_data += sizeof(struct ip6);
152     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
153     ricmp->icmp6_type = ICMP6_NDP_RA;
154     ricmp->icmp6_code = 0;
155     ricmp->icmp6_cksum = 0;
156
157     /* NDP */
158     ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
159     ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
160     ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
161     ricmp->icmp6_nra.reserved = 0;
162     ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
163     ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
164     ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
165     t->m_data += ICMP6_NDP_RA_MINLEN;
166     pl_size += ICMP6_NDP_RA_MINLEN;
167
168     /* Source link-layer address (NDP option) */
169     struct ndpopt *opt = mtod(t, struct ndpopt *);
170     opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
171     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
172     in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
173     t->m_data += NDPOPT_LINKLAYER_LEN;
174     pl_size += NDPOPT_LINKLAYER_LEN;
175
176     /* Prefix information (NDP option) */
177     struct ndpopt *opt2 = mtod(t, struct ndpopt *);
178     opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
179     opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
180     opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
181     opt2->ndpopt_prefixinfo.L = 1;
182     opt2->ndpopt_prefixinfo.A = 1;
183     opt2->ndpopt_prefixinfo.reserved1 = 0;
184     opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
185     opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
186     opt2->ndpopt_prefixinfo.reserved2 = 0;
187     opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
188     t->m_data += NDPOPT_PREFIXINFO_LEN;
189     pl_size += NDPOPT_PREFIXINFO_LEN;
190
191     /* Prefix information (NDP option) */
192     if (get_dns6_addr(&addr, &scope_id) >= 0) {
193         /* Host system does have an IPv6 DNS server, announce our proxy.  */
194         struct ndpopt *opt3 = mtod(t, struct ndpopt *);
195         opt3->ndpopt_type = NDPOPT_RDNSS;
196         opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8;
197         opt3->ndpopt_rdnss.reserved = 0;
198         opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval);
199         opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6;
200         t->m_data += NDPOPT_RDNSS_LEN;
201         pl_size += NDPOPT_RDNSS_LEN;
202     }
203
204     rip->ip_pl = htons(pl_size);
205     t->m_data -= sizeof(struct ip6) + pl_size;
206     t->m_len = sizeof(struct ip6) + pl_size;
207
208     /* ICMPv6 Checksum */
209     ricmp->icmp6_cksum = ip6_cksum(t);
210
211     ip6_output(NULL, t, 0);
212 }
213
214 /*
215  * Send NDP Neighbor Solitication
216  */
217 void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
218 {
219     char addrstr[INET6_ADDRSTRLEN];
220
221     inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
222
223     DEBUG_CALL("ndp_send_ns");
224     DEBUG_ARG("target = %s", addrstr);
225
226     /* Build IPv6 packet */
227     struct mbuf *t = m_get(slirp);
228     struct ip6 *rip = mtod(t, struct ip6 *);
229     rip->ip_src = slirp->vhost_addr6;
230     rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
231     memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
232     rip->ip_nh = IPPROTO_ICMPV6;
233     rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
234     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
235
236     /* Build ICMPv6 packet */
237     t->m_data += sizeof(struct ip6);
238     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
239     ricmp->icmp6_type = ICMP6_NDP_NS;
240     ricmp->icmp6_code = 0;
241     ricmp->icmp6_cksum = 0;
242
243     /* NDP */
244     ricmp->icmp6_nns.reserved = 0;
245     ricmp->icmp6_nns.target = addr;
246
247     /* Build NDP option */
248     t->m_data += ICMP6_NDP_NS_MINLEN;
249     struct ndpopt *opt = mtod(t, struct ndpopt *);
250     opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
251     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
252     in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
253
254     /* ICMPv6 Checksum */
255     t->m_data -= ICMP6_NDP_NA_MINLEN;
256     t->m_data -= sizeof(struct ip6);
257     ricmp->icmp6_cksum = ip6_cksum(t);
258
259     ip6_output(NULL, t, 1);
260 }
261
262 /*
263  * Send NDP Neighbor Advertisement
264  */
265 static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
266 {
267     /* Build IPv6 packet */
268     struct mbuf *t = m_get(slirp);
269     struct ip6 *rip = mtod(t, struct ip6 *);
270     rip->ip_src = icmp->icmp6_nns.target;
271     if (in6_zero(&ip->ip_src)) {
272         rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
273     } else {
274         rip->ip_dst = ip->ip_src;
275     }
276     rip->ip_nh = IPPROTO_ICMPV6;
277     rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
278                         + NDPOPT_LINKLAYER_LEN);
279     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
280
281     /* Build ICMPv6 packet */
282     t->m_data += sizeof(struct ip6);
283     struct icmp6 *ricmp = mtod(t, struct icmp6 *);
284     ricmp->icmp6_type = ICMP6_NDP_NA;
285     ricmp->icmp6_code = 0;
286     ricmp->icmp6_cksum = 0;
287
288     /* NDP */
289     ricmp->icmp6_nna.R = NDP_IsRouter;
290     ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
291     ricmp->icmp6_nna.O = 1;
292     ricmp->icmp6_nna.reserved_hi = 0;
293     ricmp->icmp6_nna.reserved_lo = 0;
294     ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
295
296     /* Build NDP option */
297     t->m_data += ICMP6_NDP_NA_MINLEN;
298     struct ndpopt *opt = mtod(t, struct ndpopt *);
299     opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
300     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
301     in6_compute_ethaddr(ricmp->icmp6_nna.target,
302                     opt->ndpopt_linklayer);
303
304     /* ICMPv6 Checksum */
305     t->m_data -= ICMP6_NDP_NA_MINLEN;
306     t->m_data -= sizeof(struct ip6);
307     ricmp->icmp6_cksum = ip6_cksum(t);
308
309     ip6_output(NULL, t, 0);
310 }
311
312 /*
313  * Process a NDP message
314  */
315 static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
316         struct icmp6 *icmp)
317 {
318     m->m_len += ETH_HLEN;
319     m->m_data -= ETH_HLEN;
320     struct ethhdr *eth = mtod(m, struct ethhdr *);
321     m->m_len -= ETH_HLEN;
322     m->m_data += ETH_HLEN;
323
324     switch (icmp->icmp6_type) {
325     case ICMP6_NDP_RS:
326         DEBUG_CALL(" type = Router Solicitation");
327         if (ip->ip_hl == 255
328                 && icmp->icmp6_code == 0
329                 && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
330             /* Gratuitous NDP */
331             ndp_table_add(slirp, ip->ip_src, eth->h_source);
332
333             ndp_send_ra(slirp);
334         }
335         break;
336
337     case ICMP6_NDP_RA:
338         DEBUG_CALL(" type = Router Advertisement");
339         slirp->cb->guest_error("Warning: guest sent NDP RA, but shouldn't",
340                                slirp->opaque);
341         break;
342
343     case ICMP6_NDP_NS:
344         DEBUG_CALL(" type = Neighbor Solicitation");
345         if (ip->ip_hl == 255
346                 && icmp->icmp6_code == 0
347                 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
348                 && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
349                 && (!in6_zero(&ip->ip_src)
350                     || in6_solicitednode_multicast(&ip->ip_dst))) {
351             if (in6_equal_host(&icmp->icmp6_nns.target)) {
352                 /* Gratuitous NDP */
353                 ndp_table_add(slirp, ip->ip_src, eth->h_source);
354                 ndp_send_na(slirp, ip, icmp);
355             }
356         }
357         break;
358
359     case ICMP6_NDP_NA:
360         DEBUG_CALL(" type = Neighbor Advertisement");
361         if (ip->ip_hl == 255
362                 && icmp->icmp6_code == 0
363                 && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
364                 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
365                 && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
366                     || icmp->icmp6_nna.S == 0)) {
367             ndp_table_add(slirp, ip->ip_src, eth->h_source);
368         }
369         break;
370
371     case ICMP6_NDP_REDIRECT:
372         DEBUG_CALL(" type = Redirect");
373         slirp->cb->guest_error(
374             "Warning: guest sent NDP REDIRECT, but shouldn't", slirp->opaque);
375         break;
376     }
377 }
378
379 /*
380  * Process a received ICMPv6 message.
381  */
382 void icmp6_input(struct mbuf *m)
383 {
384     struct icmp6 *icmp;
385     struct ip6 *ip = mtod(m, struct ip6 *);
386     Slirp *slirp = m->slirp;
387     int hlen = sizeof(struct ip6);
388
389     DEBUG_CALL("icmp6_input");
390     DEBUG_ARG("m = %p", m);
391     DEBUG_ARG("m_len = %d", m->m_len);
392
393     if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
394         goto end;
395     }
396
397     if (ip6_cksum(m)) {
398         goto end;
399     }
400
401     m->m_len -= hlen;
402     m->m_data += hlen;
403     icmp = mtod(m, struct icmp6 *);
404     m->m_len += hlen;
405     m->m_data -= hlen;
406
407     DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
408     switch (icmp->icmp6_type) {
409     case ICMP6_ECHO_REQUEST:
410         if (in6_equal_host(&ip->ip_dst)) {
411             icmp6_send_echoreply(m, slirp, ip, icmp);
412         } else {
413             /* TODO */
414             g_critical("external icmpv6 not supported yet");
415         }
416         break;
417
418     case ICMP6_NDP_RS:
419     case ICMP6_NDP_RA:
420     case ICMP6_NDP_NS:
421     case ICMP6_NDP_NA:
422     case ICMP6_NDP_REDIRECT:
423         ndp_input(m, slirp, ip, icmp);
424         break;
425
426     case ICMP6_UNREACH:
427     case ICMP6_TOOBIG:
428     case ICMP6_TIMXCEED:
429     case ICMP6_PARAMPROB:
430         /* XXX? report error? close socket? */
431     default:
432         break;
433     }
434
435 end:
436     m_free(m);
437 }
This page took 0.046415 seconds and 4 git commands to generate.