block/snapshot.c: eliminate use of ID input in snapshot operations
[qemu/rayw.git] / slirp / ip6_icmp.c
blobc1e3d30470acaa696a2f6fe1fd34eb3ed1ebe7e2
1 /*
2 * Copyright (c) 2013
3 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
4 */
6 #include "slirp.h"
7 #include "ip6_icmp.h"
9 #define NDP_Interval g_rand_int_range(slirp->grand, \
10 NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
12 static void ra_timer_handler(void *opaque)
14 Slirp *slirp = opaque;
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);
22 void icmp6_init(Slirp *slirp)
24 if (!slirp->in6_enabled) {
25 return;
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);
34 void icmp6_cleanup(Slirp *slirp)
36 if (!slirp->in6_enabled) {
37 return;
40 slirp->cb->timer_free(slirp->ra_timer, slirp->opaque);
43 static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
44 struct icmp6 *icmp)
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);
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;
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;
61 /* Checksum */
62 t->m_data -= sizeof(struct ip6);
63 ricmp->icmp6_cksum = ip6_cksum(t);
65 ip6_output(NULL, t, 0);
68 void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
70 Slirp *slirp = m->slirp;
71 struct mbuf *t;
72 struct ip6 *ip = mtod(m, struct ip6 *);
73 char addrstr[INET6_ADDRSTRLEN];
75 DEBUG_CALL("icmp6_send_error");
76 DEBUG_ARG("type = %d, code = %d", type, code);
78 if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
79 in6_zero(&ip->ip_src)) {
80 /* TODO icmp error? */
81 return;
84 t = m_get(slirp);
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);
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);
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;
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;
121 t->m_data += ICMP6_ERROR_MINLEN;
122 memcpy(t->m_data, m->m_data, error_data_len);
124 /* Checksum */
125 t->m_data -= ICMP6_ERROR_MINLEN;
126 t->m_data -= sizeof(struct ip6);
127 ricmp->icmp6_cksum = ip6_cksum(t);
129 ip6_output(NULL, t, 0);
133 * Send NDP Router Advertisement
135 void ndp_send_ra(Slirp *slirp)
137 DEBUG_CALL("ndp_send_ra");
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;
146 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
147 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
148 rip->ip_nh = IPPROTO_ICMPV6;
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;
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;
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;
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;
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;
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;
208 /* ICMPv6 Checksum */
209 ricmp->icmp6_cksum = ip6_cksum(t);
211 ip6_output(NULL, t, 0);
215 * Send NDP Neighbor Solitication
217 void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
219 char addrstr[INET6_ADDRSTRLEN];
221 inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
223 DEBUG_CALL("ndp_send_ns");
224 DEBUG_ARG("target = %s", addrstr);
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);
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;
243 /* NDP */
244 ricmp->icmp6_nns.reserved = 0;
245 ricmp->icmp6_nns.target = addr;
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);
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);
259 ip6_output(NULL, t, 1);
263 * Send NDP Neighbor Advertisement
265 static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
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;
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);
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;
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;
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);
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);
309 ip6_output(NULL, t, 0);
313 * Process a NDP message
315 static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
316 struct icmp6 *icmp)
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;
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);
333 ndp_send_ra(slirp);
335 break;
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;
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);
357 break;
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);
369 break;
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;
380 * Process a received ICMPv6 message.
382 void icmp6_input(struct mbuf *m)
384 struct icmp6 *icmp;
385 struct ip6 *ip = mtod(m, struct ip6 *);
386 Slirp *slirp = m->slirp;
387 int hlen = sizeof(struct ip6);
389 DEBUG_CALL("icmp6_input");
390 DEBUG_ARG("m = %p", m);
391 DEBUG_ARG("m_len = %d", m->m_len);
393 if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
394 goto end;
397 if (ip6_cksum(m)) {
398 goto end;
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;
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");
416 break;
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;
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;
435 end:
436 m_free(m);