globals: Allow global properties to be optional
[qemu/kevin.git] / slirp / ip6_icmp.c
blobcd1e0b9fe1a1febca37d4a3352b8a3c96e763ea6
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) + 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_full(NULL, QEMU_CLOCK_VIRTUAL,
31 SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
32 ra_timer_handler, slirp);
33 timer_mod(slirp->ra_timer,
34 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
37 void icmp6_cleanup(Slirp *slirp)
39 if (!slirp->in6_enabled) {
40 return;
43 timer_del(slirp->ra_timer);
44 timer_free(slirp->ra_timer);
47 static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
48 struct icmp6 *icmp)
50 struct mbuf *t = m_get(slirp);
51 t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
52 memcpy(t->m_data, m->m_data, t->m_len);
54 /* IPv6 Packet */
55 struct ip6 *rip = mtod(t, struct ip6 *);
56 rip->ip_dst = ip->ip_src;
57 rip->ip_src = ip->ip_dst;
59 /* ICMPv6 packet */
60 t->m_data += sizeof(struct ip6);
61 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
62 ricmp->icmp6_type = ICMP6_ECHO_REPLY;
63 ricmp->icmp6_cksum = 0;
65 /* Checksum */
66 t->m_data -= sizeof(struct ip6);
67 ricmp->icmp6_cksum = ip6_cksum(t);
69 ip6_output(NULL, t, 0);
72 void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
74 Slirp *slirp = m->slirp;
75 struct mbuf *t;
76 struct ip6 *ip = mtod(m, struct ip6 *);
78 DEBUG_CALL("icmp6_send_error");
79 DEBUG_ARGS((dfd, " type = %d, code = %d\n", type, code));
81 if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
82 in6_zero(&ip->ip_src)) {
83 /* TODO icmp error? */
84 return;
87 t = m_get(slirp);
89 /* IPv6 packet */
90 struct ip6 *rip = mtod(t, struct ip6 *);
91 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
92 rip->ip_dst = ip->ip_src;
93 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
94 char addrstr[INET6_ADDRSTRLEN];
95 inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
96 DEBUG_ARG("target = %s", addrstr);
97 #endif
99 rip->ip_nh = IPPROTO_ICMPV6;
100 const int error_data_len = MIN(m->m_len,
101 IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
102 rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
103 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
105 /* ICMPv6 packet */
106 t->m_data += sizeof(struct ip6);
107 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
108 ricmp->icmp6_type = type;
109 ricmp->icmp6_code = code;
110 ricmp->icmp6_cksum = 0;
112 switch (type) {
113 case ICMP6_UNREACH:
114 case ICMP6_TIMXCEED:
115 ricmp->icmp6_err.unused = 0;
116 break;
117 case ICMP6_TOOBIG:
118 ricmp->icmp6_err.mtu = htonl(IF_MTU);
119 break;
120 case ICMP6_PARAMPROB:
121 /* TODO: Handle this case */
122 break;
123 default:
124 g_assert_not_reached();
125 break;
127 t->m_data += ICMP6_ERROR_MINLEN;
128 memcpy(t->m_data, m->m_data, error_data_len);
130 /* Checksum */
131 t->m_data -= ICMP6_ERROR_MINLEN;
132 t->m_data -= sizeof(struct ip6);
133 ricmp->icmp6_cksum = ip6_cksum(t);
135 ip6_output(NULL, t, 0);
139 * Send NDP Router Advertisement
141 void ndp_send_ra(Slirp *slirp)
143 DEBUG_CALL("ndp_send_ra");
145 /* Build IPv6 packet */
146 struct mbuf *t = m_get(slirp);
147 struct ip6 *rip = mtod(t, struct ip6 *);
148 size_t pl_size = 0;
149 struct in6_addr addr;
150 uint32_t scope_id;
152 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
153 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
154 rip->ip_nh = IPPROTO_ICMPV6;
156 /* Build ICMPv6 packet */
157 t->m_data += sizeof(struct ip6);
158 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
159 ricmp->icmp6_type = ICMP6_NDP_RA;
160 ricmp->icmp6_code = 0;
161 ricmp->icmp6_cksum = 0;
163 /* NDP */
164 ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
165 ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
166 ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
167 ricmp->icmp6_nra.reserved = 0;
168 ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
169 ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
170 ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
171 t->m_data += ICMP6_NDP_RA_MINLEN;
172 pl_size += ICMP6_NDP_RA_MINLEN;
174 /* Source link-layer address (NDP option) */
175 struct ndpopt *opt = mtod(t, struct ndpopt *);
176 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
177 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
178 in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
179 t->m_data += NDPOPT_LINKLAYER_LEN;
180 pl_size += NDPOPT_LINKLAYER_LEN;
182 /* Prefix information (NDP option) */
183 struct ndpopt *opt2 = mtod(t, struct ndpopt *);
184 opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
185 opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
186 opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
187 opt2->ndpopt_prefixinfo.L = 1;
188 opt2->ndpopt_prefixinfo.A = 1;
189 opt2->ndpopt_prefixinfo.reserved1 = 0;
190 opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
191 opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
192 opt2->ndpopt_prefixinfo.reserved2 = 0;
193 opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
194 t->m_data += NDPOPT_PREFIXINFO_LEN;
195 pl_size += NDPOPT_PREFIXINFO_LEN;
197 /* Prefix information (NDP option) */
198 if (get_dns6_addr(&addr, &scope_id) >= 0) {
199 /* Host system does have an IPv6 DNS server, announce our proxy. */
200 struct ndpopt *opt3 = mtod(t, struct ndpopt *);
201 opt3->ndpopt_type = NDPOPT_RDNSS;
202 opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8;
203 opt3->ndpopt_rdnss.reserved = 0;
204 opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval);
205 opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6;
206 t->m_data += NDPOPT_RDNSS_LEN;
207 pl_size += NDPOPT_RDNSS_LEN;
210 rip->ip_pl = htons(pl_size);
211 t->m_data -= sizeof(struct ip6) + pl_size;
212 t->m_len = sizeof(struct ip6) + pl_size;
214 /* ICMPv6 Checksum */
215 ricmp->icmp6_cksum = ip6_cksum(t);
217 ip6_output(NULL, t, 0);
221 * Send NDP Neighbor Solitication
223 void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
225 DEBUG_CALL("ndp_send_ns");
226 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
227 char addrstr[INET6_ADDRSTRLEN];
228 inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
229 DEBUG_ARG("target = %s", addrstr);
230 #endif
232 /* Build IPv6 packet */
233 struct mbuf *t = m_get(slirp);
234 struct ip6 *rip = mtod(t, struct ip6 *);
235 rip->ip_src = slirp->vhost_addr6;
236 rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
237 memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
238 rip->ip_nh = IPPROTO_ICMPV6;
239 rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
240 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
242 /* Build ICMPv6 packet */
243 t->m_data += sizeof(struct ip6);
244 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
245 ricmp->icmp6_type = ICMP6_NDP_NS;
246 ricmp->icmp6_code = 0;
247 ricmp->icmp6_cksum = 0;
249 /* NDP */
250 ricmp->icmp6_nns.reserved = 0;
251 ricmp->icmp6_nns.target = addr;
253 /* Build NDP option */
254 t->m_data += ICMP6_NDP_NS_MINLEN;
255 struct ndpopt *opt = mtod(t, struct ndpopt *);
256 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
257 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
258 in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
260 /* ICMPv6 Checksum */
261 t->m_data -= ICMP6_NDP_NA_MINLEN;
262 t->m_data -= sizeof(struct ip6);
263 ricmp->icmp6_cksum = ip6_cksum(t);
265 ip6_output(NULL, t, 1);
269 * Send NDP Neighbor Advertisement
271 static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
273 /* Build IPv6 packet */
274 struct mbuf *t = m_get(slirp);
275 struct ip6 *rip = mtod(t, struct ip6 *);
276 rip->ip_src = icmp->icmp6_nns.target;
277 if (in6_zero(&ip->ip_src)) {
278 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
279 } else {
280 rip->ip_dst = ip->ip_src;
282 rip->ip_nh = IPPROTO_ICMPV6;
283 rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
284 + NDPOPT_LINKLAYER_LEN);
285 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
287 /* Build ICMPv6 packet */
288 t->m_data += sizeof(struct ip6);
289 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
290 ricmp->icmp6_type = ICMP6_NDP_NA;
291 ricmp->icmp6_code = 0;
292 ricmp->icmp6_cksum = 0;
294 /* NDP */
295 ricmp->icmp6_nna.R = NDP_IsRouter;
296 ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
297 ricmp->icmp6_nna.O = 1;
298 ricmp->icmp6_nna.reserved_hi = 0;
299 ricmp->icmp6_nna.reserved_lo = 0;
300 ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
302 /* Build NDP option */
303 t->m_data += ICMP6_NDP_NA_MINLEN;
304 struct ndpopt *opt = mtod(t, struct ndpopt *);
305 opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
306 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
307 in6_compute_ethaddr(ricmp->icmp6_nna.target,
308 opt->ndpopt_linklayer);
310 /* ICMPv6 Checksum */
311 t->m_data -= ICMP6_NDP_NA_MINLEN;
312 t->m_data -= sizeof(struct ip6);
313 ricmp->icmp6_cksum = ip6_cksum(t);
315 ip6_output(NULL, t, 0);
319 * Process a NDP message
321 static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
322 struct icmp6 *icmp)
324 m->m_len += ETH_HLEN;
325 m->m_data -= ETH_HLEN;
326 struct ethhdr *eth = mtod(m, struct ethhdr *);
327 m->m_len -= ETH_HLEN;
328 m->m_data += ETH_HLEN;
330 switch (icmp->icmp6_type) {
331 case ICMP6_NDP_RS:
332 DEBUG_CALL(" type = Router Solicitation");
333 if (ip->ip_hl == 255
334 && icmp->icmp6_code == 0
335 && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
336 /* Gratuitous NDP */
337 ndp_table_add(slirp, ip->ip_src, eth->h_source);
339 ndp_send_ra(slirp);
341 break;
343 case ICMP6_NDP_RA:
344 DEBUG_CALL(" type = Router Advertisement");
345 qemu_log_mask(LOG_GUEST_ERROR,
346 "Warning: guest sent NDP RA, but shouldn't");
347 break;
349 case ICMP6_NDP_NS:
350 DEBUG_CALL(" type = Neighbor Solicitation");
351 if (ip->ip_hl == 255
352 && icmp->icmp6_code == 0
353 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
354 && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
355 && (!in6_zero(&ip->ip_src)
356 || in6_solicitednode_multicast(&ip->ip_dst))) {
357 if (in6_equal_host(&icmp->icmp6_nns.target)) {
358 /* Gratuitous NDP */
359 ndp_table_add(slirp, ip->ip_src, eth->h_source);
360 ndp_send_na(slirp, ip, icmp);
363 break;
365 case ICMP6_NDP_NA:
366 DEBUG_CALL(" type = Neighbor Advertisement");
367 if (ip->ip_hl == 255
368 && icmp->icmp6_code == 0
369 && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
370 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
371 && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
372 || icmp->icmp6_nna.S == 0)) {
373 ndp_table_add(slirp, ip->ip_src, eth->h_source);
375 break;
377 case ICMP6_NDP_REDIRECT:
378 DEBUG_CALL(" type = Redirect");
379 qemu_log_mask(LOG_GUEST_ERROR,
380 "Warning: guest sent NDP REDIRECT, but shouldn't");
381 break;
386 * Process a received ICMPv6 message.
388 void icmp6_input(struct mbuf *m)
390 struct icmp6 *icmp;
391 struct ip6 *ip = mtod(m, struct ip6 *);
392 Slirp *slirp = m->slirp;
393 int hlen = sizeof(struct ip6);
395 DEBUG_CALL("icmp6_input");
396 DEBUG_ARG("m = %lx", (long) m);
397 DEBUG_ARG("m_len = %d", m->m_len);
399 if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
400 goto end;
403 if (ip6_cksum(m)) {
404 goto end;
407 m->m_len -= hlen;
408 m->m_data += hlen;
409 icmp = mtod(m, struct icmp6 *);
410 m->m_len += hlen;
411 m->m_data -= hlen;
413 DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
414 switch (icmp->icmp6_type) {
415 case ICMP6_ECHO_REQUEST:
416 if (in6_equal_host(&ip->ip_dst)) {
417 icmp6_send_echoreply(m, slirp, ip, icmp);
418 } else {
419 /* TODO */
420 error_report("external icmpv6 not supported yet");
422 break;
424 case ICMP6_NDP_RS:
425 case ICMP6_NDP_RA:
426 case ICMP6_NDP_NS:
427 case ICMP6_NDP_NA:
428 case ICMP6_NDP_REDIRECT:
429 ndp_input(m, slirp, ip, icmp);
430 break;
432 case ICMP6_UNREACH:
433 case ICMP6_TOOBIG:
434 case ICMP6_TIMXCEED:
435 case ICMP6_PARAMPROB:
436 /* XXX? report error? close socket? */
437 default:
438 break;
441 end:
442 m_free(m);