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