slirp: fix ipv6 timers
[qemu/ar7.git] / slirp / ip6_icmp.c
blob3f41187cfe8e62cabc78e9fcd801fec9a0c8381b
1 /*
2 * Copyright (c) 2013
3 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
4 */
6 #include "qemu/osdep.h"
7 #include "slirp.h"
8 #include "ip6_icmp.h"
9 #include "qemu/timer.h"
10 #include "qemu/error-report.h"
11 #include "qemu/log.h"
13 #define NDP_Interval g_rand_int_range(slirp->grand, \
14 NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
16 static void ra_timer_handler(void *opaque)
18 Slirp *slirp = opaque;
19 timer_mod(slirp->ra_timer,
20 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT) + NDP_Interval);
21 ndp_send_ra(slirp);
24 void icmp6_init(Slirp *slirp)
26 if (!slirp->in6_enabled) {
27 return;
30 slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_EXT,
31 ra_timer_handler, slirp);
32 timer_mod(slirp->ra_timer,
33 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT) + NDP_Interval);
36 void icmp6_cleanup(Slirp *slirp)
38 if (!slirp->in6_enabled) {
39 return;
42 timer_del(slirp->ra_timer);
43 timer_free(slirp->ra_timer);
46 static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
47 struct icmp6 *icmp)
49 struct mbuf *t = m_get(slirp);
50 t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
51 memcpy(t->m_data, m->m_data, t->m_len);
53 /* IPv6 Packet */
54 struct ip6 *rip = mtod(t, struct ip6 *);
55 rip->ip_dst = ip->ip_src;
56 rip->ip_src = ip->ip_dst;
58 /* ICMPv6 packet */
59 t->m_data += sizeof(struct ip6);
60 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
61 ricmp->icmp6_type = ICMP6_ECHO_REPLY;
62 ricmp->icmp6_cksum = 0;
64 /* Checksum */
65 t->m_data -= sizeof(struct ip6);
66 ricmp->icmp6_cksum = ip6_cksum(t);
68 ip6_output(NULL, t, 0);
71 void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
73 Slirp *slirp = m->slirp;
74 struct mbuf *t;
75 struct ip6 *ip = mtod(m, struct ip6 *);
77 DEBUG_CALL("icmp6_send_error");
78 DEBUG_ARGS((dfd, " type = %d, code = %d\n", type, code));
80 if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
81 in6_zero(&ip->ip_src)) {
82 /* TODO icmp error? */
83 return;
86 t = m_get(slirp);
88 /* IPv6 packet */
89 struct ip6 *rip = mtod(t, struct ip6 *);
90 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
91 rip->ip_dst = ip->ip_src;
92 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
93 char addrstr[INET6_ADDRSTRLEN];
94 inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
95 DEBUG_ARG("target = %s", addrstr);
96 #endif
98 rip->ip_nh = IPPROTO_ICMPV6;
99 const int error_data_len = MIN(m->m_len,
100 IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
101 rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
102 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
104 /* ICMPv6 packet */
105 t->m_data += sizeof(struct ip6);
106 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
107 ricmp->icmp6_type = type;
108 ricmp->icmp6_code = code;
109 ricmp->icmp6_cksum = 0;
111 switch (type) {
112 case ICMP6_UNREACH:
113 case ICMP6_TIMXCEED:
114 ricmp->icmp6_err.unused = 0;
115 break;
116 case ICMP6_TOOBIG:
117 ricmp->icmp6_err.mtu = htonl(IF_MTU);
118 break;
119 case ICMP6_PARAMPROB:
120 /* TODO: Handle this case */
121 break;
122 default:
123 g_assert_not_reached();
124 break;
126 t->m_data += ICMP6_ERROR_MINLEN;
127 memcpy(t->m_data, m->m_data, error_data_len);
129 /* Checksum */
130 t->m_data -= ICMP6_ERROR_MINLEN;
131 t->m_data -= sizeof(struct ip6);
132 ricmp->icmp6_cksum = ip6_cksum(t);
134 ip6_output(NULL, t, 0);
138 * Send NDP Router Advertisement
140 void ndp_send_ra(Slirp *slirp)
142 DEBUG_CALL("ndp_send_ra");
144 /* Build IPv6 packet */
145 struct mbuf *t = m_get(slirp);
146 struct ip6 *rip = mtod(t, struct ip6 *);
147 size_t pl_size = 0;
148 struct in6_addr addr;
149 uint32_t scope_id;
151 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
152 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
153 rip->ip_nh = IPPROTO_ICMPV6;
155 /* Build ICMPv6 packet */
156 t->m_data += sizeof(struct ip6);
157 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
158 ricmp->icmp6_type = ICMP6_NDP_RA;
159 ricmp->icmp6_code = 0;
160 ricmp->icmp6_cksum = 0;
162 /* NDP */
163 ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
164 ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
165 ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
166 ricmp->icmp6_nra.reserved = 0;
167 ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
168 ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
169 ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
170 t->m_data += ICMP6_NDP_RA_MINLEN;
171 pl_size += ICMP6_NDP_RA_MINLEN;
173 /* Source link-layer address (NDP option) */
174 struct ndpopt *opt = mtod(t, struct ndpopt *);
175 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
176 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
177 in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
178 t->m_data += NDPOPT_LINKLAYER_LEN;
179 pl_size += NDPOPT_LINKLAYER_LEN;
181 /* Prefix information (NDP option) */
182 struct ndpopt *opt2 = mtod(t, struct ndpopt *);
183 opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
184 opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
185 opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
186 opt2->ndpopt_prefixinfo.L = 1;
187 opt2->ndpopt_prefixinfo.A = 1;
188 opt2->ndpopt_prefixinfo.reserved1 = 0;
189 opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
190 opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
191 opt2->ndpopt_prefixinfo.reserved2 = 0;
192 opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
193 t->m_data += NDPOPT_PREFIXINFO_LEN;
194 pl_size += NDPOPT_PREFIXINFO_LEN;
196 /* Prefix information (NDP option) */
197 if (get_dns6_addr(&addr, &scope_id) >= 0) {
198 /* Host system does have an IPv6 DNS server, announce our proxy. */
199 struct ndpopt *opt3 = mtod(t, struct ndpopt *);
200 opt3->ndpopt_type = NDPOPT_RDNSS;
201 opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8;
202 opt3->ndpopt_rdnss.reserved = 0;
203 opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval);
204 opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6;
205 t->m_data += NDPOPT_RDNSS_LEN;
206 pl_size += NDPOPT_RDNSS_LEN;
209 rip->ip_pl = htons(pl_size);
210 t->m_data -= sizeof(struct ip6) + pl_size;
211 t->m_len = sizeof(struct ip6) + pl_size;
213 /* ICMPv6 Checksum */
214 ricmp->icmp6_cksum = ip6_cksum(t);
216 ip6_output(NULL, t, 0);
220 * Send NDP Neighbor Solitication
222 void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
224 DEBUG_CALL("ndp_send_ns");
225 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
226 char addrstr[INET6_ADDRSTRLEN];
227 inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
228 DEBUG_ARG("target = %s", addrstr);
229 #endif
231 /* Build IPv6 packet */
232 struct mbuf *t = m_get(slirp);
233 struct ip6 *rip = mtod(t, struct ip6 *);
234 rip->ip_src = slirp->vhost_addr6;
235 rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
236 memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
237 rip->ip_nh = IPPROTO_ICMPV6;
238 rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
239 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
241 /* Build ICMPv6 packet */
242 t->m_data += sizeof(struct ip6);
243 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
244 ricmp->icmp6_type = ICMP6_NDP_NS;
245 ricmp->icmp6_code = 0;
246 ricmp->icmp6_cksum = 0;
248 /* NDP */
249 ricmp->icmp6_nns.reserved = 0;
250 ricmp->icmp6_nns.target = addr;
252 /* Build NDP option */
253 t->m_data += ICMP6_NDP_NS_MINLEN;
254 struct ndpopt *opt = mtod(t, struct ndpopt *);
255 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
256 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
257 in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
259 /* ICMPv6 Checksum */
260 t->m_data -= ICMP6_NDP_NA_MINLEN;
261 t->m_data -= sizeof(struct ip6);
262 ricmp->icmp6_cksum = ip6_cksum(t);
264 ip6_output(NULL, t, 1);
268 * Send NDP Neighbor Advertisement
270 static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
272 /* Build IPv6 packet */
273 struct mbuf *t = m_get(slirp);
274 struct ip6 *rip = mtod(t, struct ip6 *);
275 rip->ip_src = icmp->icmp6_nns.target;
276 if (in6_zero(&ip->ip_src)) {
277 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
278 } else {
279 rip->ip_dst = ip->ip_src;
281 rip->ip_nh = IPPROTO_ICMPV6;
282 rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
283 + NDPOPT_LINKLAYER_LEN);
284 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
286 /* Build ICMPv6 packet */
287 t->m_data += sizeof(struct ip6);
288 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
289 ricmp->icmp6_type = ICMP6_NDP_NA;
290 ricmp->icmp6_code = 0;
291 ricmp->icmp6_cksum = 0;
293 /* NDP */
294 ricmp->icmp6_nna.R = NDP_IsRouter;
295 ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
296 ricmp->icmp6_nna.O = 1;
297 ricmp->icmp6_nna.reserved_hi = 0;
298 ricmp->icmp6_nna.reserved_lo = 0;
299 ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
301 /* Build NDP option */
302 t->m_data += ICMP6_NDP_NA_MINLEN;
303 struct ndpopt *opt = mtod(t, struct ndpopt *);
304 opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
305 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
306 in6_compute_ethaddr(ricmp->icmp6_nna.target,
307 opt->ndpopt_linklayer);
309 /* ICMPv6 Checksum */
310 t->m_data -= ICMP6_NDP_NA_MINLEN;
311 t->m_data -= sizeof(struct ip6);
312 ricmp->icmp6_cksum = ip6_cksum(t);
314 ip6_output(NULL, t, 0);
318 * Process a NDP message
320 static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
321 struct icmp6 *icmp)
323 m->m_len += ETH_HLEN;
324 m->m_data -= ETH_HLEN;
325 struct ethhdr *eth = mtod(m, struct ethhdr *);
326 m->m_len -= ETH_HLEN;
327 m->m_data += ETH_HLEN;
329 switch (icmp->icmp6_type) {
330 case ICMP6_NDP_RS:
331 DEBUG_CALL(" type = Router Solicitation");
332 if (ip->ip_hl == 255
333 && icmp->icmp6_code == 0
334 && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
335 /* Gratuitous NDP */
336 ndp_table_add(slirp, ip->ip_src, eth->h_source);
338 ndp_send_ra(slirp);
340 break;
342 case ICMP6_NDP_RA:
343 DEBUG_CALL(" type = Router Advertisement");
344 qemu_log_mask(LOG_GUEST_ERROR,
345 "Warning: guest sent NDP RA, but shouldn't");
346 break;
348 case ICMP6_NDP_NS:
349 DEBUG_CALL(" type = Neighbor Solicitation");
350 if (ip->ip_hl == 255
351 && icmp->icmp6_code == 0
352 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
353 && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
354 && (!in6_zero(&ip->ip_src)
355 || in6_solicitednode_multicast(&ip->ip_dst))) {
356 if (in6_equal_host(&icmp->icmp6_nns.target)) {
357 /* Gratuitous NDP */
358 ndp_table_add(slirp, ip->ip_src, eth->h_source);
359 ndp_send_na(slirp, ip, icmp);
362 break;
364 case ICMP6_NDP_NA:
365 DEBUG_CALL(" type = Neighbor Advertisement");
366 if (ip->ip_hl == 255
367 && icmp->icmp6_code == 0
368 && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
369 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
370 && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
371 || icmp->icmp6_nna.S == 0)) {
372 ndp_table_add(slirp, ip->ip_src, eth->h_source);
374 break;
376 case ICMP6_NDP_REDIRECT:
377 DEBUG_CALL(" type = Redirect");
378 qemu_log_mask(LOG_GUEST_ERROR,
379 "Warning: guest sent NDP REDIRECT, but shouldn't");
380 break;
385 * Process a received ICMPv6 message.
387 void icmp6_input(struct mbuf *m)
389 struct icmp6 *icmp;
390 struct ip6 *ip = mtod(m, struct ip6 *);
391 Slirp *slirp = m->slirp;
392 int hlen = sizeof(struct ip6);
394 DEBUG_CALL("icmp6_input");
395 DEBUG_ARG("m = %lx", (long) m);
396 DEBUG_ARG("m_len = %d", m->m_len);
398 if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
399 goto end;
402 if (ip6_cksum(m)) {
403 goto end;
406 m->m_len -= hlen;
407 m->m_data += hlen;
408 icmp = mtod(m, struct icmp6 *);
409 m->m_len += hlen;
410 m->m_data -= hlen;
412 DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
413 switch (icmp->icmp6_type) {
414 case ICMP6_ECHO_REQUEST:
415 if (in6_equal_host(&ip->ip_dst)) {
416 icmp6_send_echoreply(m, slirp, ip, icmp);
417 } else {
418 /* TODO */
419 error_report("external icmpv6 not supported yet");
421 break;
423 case ICMP6_NDP_RS:
424 case ICMP6_NDP_RA:
425 case ICMP6_NDP_NS:
426 case ICMP6_NDP_NA:
427 case ICMP6_NDP_REDIRECT:
428 ndp_input(m, slirp, ip, icmp);
429 break;
431 case ICMP6_UNREACH:
432 case ICMP6_TOOBIG:
433 case ICMP6_TIMXCEED:
434 case ICMP6_PARAMPROB:
435 /* XXX? report error? close socket? */
436 default:
437 break;
440 end:
441 m_free(m);