]> Git Repo - qemu.git/blame - slirp/ip6_icmp.c
sd: express dependencies with kconfig
[qemu.git] / slirp / ip6_icmp.c
CommitLineData
0d6ff71a
GS
1/*
2 * Copyright (c) 2013
3 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
4 */
5
0d6ff71a
GS
6#include "slirp.h"
7#include "ip6_icmp.h"
0d6ff71a
GS
8
9#define NDP_Interval g_rand_int_range(slirp->grand, \
10 NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
11
12static void ra_timer_handler(void *opaque)
13{
14 Slirp *slirp = opaque;
07abf6d4
MAL
15
16 slirp->cb->timer_mod(slirp->ra_timer,
3e0fad3a
MAL
17 slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval,
18 slirp->opaque);
0d6ff71a
GS
19 ndp_send_ra(slirp);
20}
21
22void icmp6_init(Slirp *slirp)
23{
0b11c036
ST
24 if (!slirp->in6_enabled) {
25 return;
26 }
27
3e0fad3a 28 slirp->ra_timer = slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque);
07abf6d4 29 slirp->cb->timer_mod(slirp->ra_timer,
3e0fad3a
MAL
30 slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval,
31 slirp->opaque);
0d6ff71a
GS
32}
33
34void icmp6_cleanup(Slirp *slirp)
35{
0b11c036
ST
36 if (!slirp->in6_enabled) {
37 return;
38 }
39
3e0fad3a 40 slirp->cb->timer_free(slirp->ra_timer, slirp->opaque);
0d6ff71a
GS
41}
42
43static 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
fc6c9257
YB
68void 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 *);
df2ad332 73 char addrstr[INET6_ADDRSTRLEN];
fc6c9257
YB
74
75 DEBUG_CALL("icmp6_send_error");
a857d91d 76 DEBUG_ARG("type = %d, code = %d", type, code);
fc6c9257
YB
77
78 if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
1120fae0 79 in6_zero(&ip->ip_src)) {
fc6c9257
YB
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;
fc6c9257
YB
90 inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
91 DEBUG_ARG("target = %s", addrstr);
fc6c9257
YB
92
93 rip->ip_nh = IPPROTO_ICMPV6;
893dcdbf 94 const int error_data_len = MIN(m->m_len,
fc6c9257
YB
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
0d6ff71a
GS
132/*
133 * Send NDP Router Advertisement
134 */
135void 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 *);
e42f869b 142 size_t pl_size = 0;
a2f80fdf
ST
143 struct in6_addr addr;
144 uint32_t scope_id;
145
0d6ff71a
GS
146 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
147 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
148 rip->ip_nh = IPPROTO_ICMPV6;
0d6ff71a
GS
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);
f7725df3 165 t->m_data += ICMP6_NDP_RA_MINLEN;
e42f869b 166 pl_size += ICMP6_NDP_RA_MINLEN;
0d6ff71a
GS
167
168 /* Source link-layer address (NDP option) */
0d6ff71a
GS
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);
f7725df3 173 t->m_data += NDPOPT_LINKLAYER_LEN;
e42f869b 174 pl_size += NDPOPT_LINKLAYER_LEN;
0d6ff71a
GS
175
176 /* Prefix information (NDP option) */
0d6ff71a
GS
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;
f7725df3 188 t->m_data += NDPOPT_PREFIXINFO_LEN;
e42f869b 189 pl_size += NDPOPT_PREFIXINFO_LEN;
f7725df3 190
f7725df3 191 /* Prefix information (NDP option) */
a2f80fdf
ST
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 }
0d6ff71a 203
e42f869b
ST
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
0d6ff71a 208 /* ICMPv6 Checksum */
0d6ff71a
GS
209 ricmp->icmp6_cksum = ip6_cksum(t);
210
211 ip6_output(NULL, t, 0);
212}
213
214/*
215 * Send NDP Neighbor Solitication
216 */
217void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
218{
0d6ff71a 219 char addrstr[INET6_ADDRSTRLEN];
df2ad332 220
0d6ff71a 221 inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
df2ad332
MAL
222
223 DEBUG_CALL("ndp_send_ns");
0d6ff71a 224 DEBUG_ARG("target = %s", addrstr);
0d6ff71a
GS
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 */
265static 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;
1120fae0 271 if (in6_zero(&ip->ip_src)) {
0d6ff71a
GS
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 */
315static 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");
3e0fad3a
MAL
339 slirp->cb->guest_error("Warning: guest sent NDP RA, but shouldn't",
340 slirp->opaque);
0d6ff71a
GS
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
1120fae0 349 && (!in6_zero(&ip->ip_src)
0d6ff71a
GS
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");
2addc8fb 373 slirp->cb->guest_error(
3e0fad3a 374 "Warning: guest sent NDP REDIRECT, but shouldn't", slirp->opaque);
0d6ff71a
GS
375 break;
376 }
377}
378
379/*
380 * Process a received ICMPv6 message.
381 */
382void 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");
3402618c 390 DEBUG_ARG("m = %p", m);
0d6ff71a
GS
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 */
90dfa278 414 g_critical("external icmpv6 not supported yet");
0d6ff71a
GS
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
435end:
436 m_free(m);
437}
This page took 0.205877 seconds and 4 git commands to generate.