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