slirp: improve send_packet() callback
[qemu/ar7.git] / slirp / ip6_icmp.c
blob2a432ebbd486f9785f06ada5193f22d74738d458
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() / SCALE_MS + NDP_Interval);
18 ndp_send_ra(slirp);
21 void icmp6_init(Slirp *slirp)
23 if (!slirp->in6_enabled) {
24 return;
27 slirp->ra_timer = slirp->cb->timer_new(ra_timer_handler, slirp);
28 slirp->cb->timer_mod(slirp->ra_timer,
29 slirp->cb->clock_get_ns() / SCALE_MS + NDP_Interval);
32 void icmp6_cleanup(Slirp *slirp)
34 if (!slirp->in6_enabled) {
35 return;
38 slirp->cb->timer_free(slirp->ra_timer);
41 static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
42 struct icmp6 *icmp)
44 struct mbuf *t = m_get(slirp);
45 t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
46 memcpy(t->m_data, m->m_data, t->m_len);
48 /* IPv6 Packet */
49 struct ip6 *rip = mtod(t, struct ip6 *);
50 rip->ip_dst = ip->ip_src;
51 rip->ip_src = ip->ip_dst;
53 /* ICMPv6 packet */
54 t->m_data += sizeof(struct ip6);
55 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
56 ricmp->icmp6_type = ICMP6_ECHO_REPLY;
57 ricmp->icmp6_cksum = 0;
59 /* Checksum */
60 t->m_data -= sizeof(struct ip6);
61 ricmp->icmp6_cksum = ip6_cksum(t);
63 ip6_output(NULL, t, 0);
66 void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
68 Slirp *slirp = m->slirp;
69 struct mbuf *t;
70 struct ip6 *ip = mtod(m, struct ip6 *);
71 char addrstr[INET6_ADDRSTRLEN];
73 DEBUG_CALL("icmp6_send_error");
74 DEBUG_ARG("type = %d, code = %d", type, code);
76 if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
77 in6_zero(&ip->ip_src)) {
78 /* TODO icmp error? */
79 return;
82 t = m_get(slirp);
84 /* IPv6 packet */
85 struct ip6 *rip = mtod(t, struct ip6 *);
86 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
87 rip->ip_dst = ip->ip_src;
88 inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
89 DEBUG_ARG("target = %s", addrstr);
91 rip->ip_nh = IPPROTO_ICMPV6;
92 const int error_data_len = MIN(m->m_len,
93 IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
94 rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
95 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
97 /* ICMPv6 packet */
98 t->m_data += sizeof(struct ip6);
99 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
100 ricmp->icmp6_type = type;
101 ricmp->icmp6_code = code;
102 ricmp->icmp6_cksum = 0;
104 switch (type) {
105 case ICMP6_UNREACH:
106 case ICMP6_TIMXCEED:
107 ricmp->icmp6_err.unused = 0;
108 break;
109 case ICMP6_TOOBIG:
110 ricmp->icmp6_err.mtu = htonl(IF_MTU);
111 break;
112 case ICMP6_PARAMPROB:
113 /* TODO: Handle this case */
114 break;
115 default:
116 g_assert_not_reached();
117 break;
119 t->m_data += ICMP6_ERROR_MINLEN;
120 memcpy(t->m_data, m->m_data, error_data_len);
122 /* Checksum */
123 t->m_data -= ICMP6_ERROR_MINLEN;
124 t->m_data -= sizeof(struct ip6);
125 ricmp->icmp6_cksum = ip6_cksum(t);
127 ip6_output(NULL, t, 0);
131 * Send NDP Router Advertisement
133 void ndp_send_ra(Slirp *slirp)
135 DEBUG_CALL("ndp_send_ra");
137 /* Build IPv6 packet */
138 struct mbuf *t = m_get(slirp);
139 struct ip6 *rip = mtod(t, struct ip6 *);
140 size_t pl_size = 0;
141 struct in6_addr addr;
142 uint32_t scope_id;
144 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
145 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
146 rip->ip_nh = IPPROTO_ICMPV6;
148 /* Build ICMPv6 packet */
149 t->m_data += sizeof(struct ip6);
150 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
151 ricmp->icmp6_type = ICMP6_NDP_RA;
152 ricmp->icmp6_code = 0;
153 ricmp->icmp6_cksum = 0;
155 /* NDP */
156 ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
157 ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
158 ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
159 ricmp->icmp6_nra.reserved = 0;
160 ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
161 ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
162 ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
163 t->m_data += ICMP6_NDP_RA_MINLEN;
164 pl_size += ICMP6_NDP_RA_MINLEN;
166 /* Source link-layer address (NDP option) */
167 struct ndpopt *opt = mtod(t, struct ndpopt *);
168 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
169 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
170 in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
171 t->m_data += NDPOPT_LINKLAYER_LEN;
172 pl_size += NDPOPT_LINKLAYER_LEN;
174 /* Prefix information (NDP option) */
175 struct ndpopt *opt2 = mtod(t, struct ndpopt *);
176 opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
177 opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
178 opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
179 opt2->ndpopt_prefixinfo.L = 1;
180 opt2->ndpopt_prefixinfo.A = 1;
181 opt2->ndpopt_prefixinfo.reserved1 = 0;
182 opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
183 opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
184 opt2->ndpopt_prefixinfo.reserved2 = 0;
185 opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
186 t->m_data += NDPOPT_PREFIXINFO_LEN;
187 pl_size += NDPOPT_PREFIXINFO_LEN;
189 /* Prefix information (NDP option) */
190 if (get_dns6_addr(&addr, &scope_id) >= 0) {
191 /* Host system does have an IPv6 DNS server, announce our proxy. */
192 struct ndpopt *opt3 = mtod(t, struct ndpopt *);
193 opt3->ndpopt_type = NDPOPT_RDNSS;
194 opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8;
195 opt3->ndpopt_rdnss.reserved = 0;
196 opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval);
197 opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6;
198 t->m_data += NDPOPT_RDNSS_LEN;
199 pl_size += NDPOPT_RDNSS_LEN;
202 rip->ip_pl = htons(pl_size);
203 t->m_data -= sizeof(struct ip6) + pl_size;
204 t->m_len = sizeof(struct ip6) + pl_size;
206 /* ICMPv6 Checksum */
207 ricmp->icmp6_cksum = ip6_cksum(t);
209 ip6_output(NULL, t, 0);
213 * Send NDP Neighbor Solitication
215 void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
217 char addrstr[INET6_ADDRSTRLEN];
219 inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
221 DEBUG_CALL("ndp_send_ns");
222 DEBUG_ARG("target = %s", addrstr);
224 /* Build IPv6 packet */
225 struct mbuf *t = m_get(slirp);
226 struct ip6 *rip = mtod(t, struct ip6 *);
227 rip->ip_src = slirp->vhost_addr6;
228 rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
229 memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
230 rip->ip_nh = IPPROTO_ICMPV6;
231 rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
232 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
234 /* Build ICMPv6 packet */
235 t->m_data += sizeof(struct ip6);
236 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
237 ricmp->icmp6_type = ICMP6_NDP_NS;
238 ricmp->icmp6_code = 0;
239 ricmp->icmp6_cksum = 0;
241 /* NDP */
242 ricmp->icmp6_nns.reserved = 0;
243 ricmp->icmp6_nns.target = addr;
245 /* Build NDP option */
246 t->m_data += ICMP6_NDP_NS_MINLEN;
247 struct ndpopt *opt = mtod(t, struct ndpopt *);
248 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
249 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
250 in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
252 /* ICMPv6 Checksum */
253 t->m_data -= ICMP6_NDP_NA_MINLEN;
254 t->m_data -= sizeof(struct ip6);
255 ricmp->icmp6_cksum = ip6_cksum(t);
257 ip6_output(NULL, t, 1);
261 * Send NDP Neighbor Advertisement
263 static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
265 /* Build IPv6 packet */
266 struct mbuf *t = m_get(slirp);
267 struct ip6 *rip = mtod(t, struct ip6 *);
268 rip->ip_src = icmp->icmp6_nns.target;
269 if (in6_zero(&ip->ip_src)) {
270 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
271 } else {
272 rip->ip_dst = ip->ip_src;
274 rip->ip_nh = IPPROTO_ICMPV6;
275 rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
276 + NDPOPT_LINKLAYER_LEN);
277 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
279 /* Build ICMPv6 packet */
280 t->m_data += sizeof(struct ip6);
281 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
282 ricmp->icmp6_type = ICMP6_NDP_NA;
283 ricmp->icmp6_code = 0;
284 ricmp->icmp6_cksum = 0;
286 /* NDP */
287 ricmp->icmp6_nna.R = NDP_IsRouter;
288 ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
289 ricmp->icmp6_nna.O = 1;
290 ricmp->icmp6_nna.reserved_hi = 0;
291 ricmp->icmp6_nna.reserved_lo = 0;
292 ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
294 /* Build NDP option */
295 t->m_data += ICMP6_NDP_NA_MINLEN;
296 struct ndpopt *opt = mtod(t, struct ndpopt *);
297 opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
298 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
299 in6_compute_ethaddr(ricmp->icmp6_nna.target,
300 opt->ndpopt_linklayer);
302 /* ICMPv6 Checksum */
303 t->m_data -= ICMP6_NDP_NA_MINLEN;
304 t->m_data -= sizeof(struct ip6);
305 ricmp->icmp6_cksum = ip6_cksum(t);
307 ip6_output(NULL, t, 0);
311 * Process a NDP message
313 static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
314 struct icmp6 *icmp)
316 m->m_len += ETH_HLEN;
317 m->m_data -= ETH_HLEN;
318 struct ethhdr *eth = mtod(m, struct ethhdr *);
319 m->m_len -= ETH_HLEN;
320 m->m_data += ETH_HLEN;
322 switch (icmp->icmp6_type) {
323 case ICMP6_NDP_RS:
324 DEBUG_CALL(" type = Router Solicitation");
325 if (ip->ip_hl == 255
326 && icmp->icmp6_code == 0
327 && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
328 /* Gratuitous NDP */
329 ndp_table_add(slirp, ip->ip_src, eth->h_source);
331 ndp_send_ra(slirp);
333 break;
335 case ICMP6_NDP_RA:
336 DEBUG_CALL(" type = Router Advertisement");
337 slirp->cb->guest_error("Warning: guest sent NDP RA, but shouldn't");
338 break;
340 case ICMP6_NDP_NS:
341 DEBUG_CALL(" type = Neighbor Solicitation");
342 if (ip->ip_hl == 255
343 && icmp->icmp6_code == 0
344 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
345 && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
346 && (!in6_zero(&ip->ip_src)
347 || in6_solicitednode_multicast(&ip->ip_dst))) {
348 if (in6_equal_host(&icmp->icmp6_nns.target)) {
349 /* Gratuitous NDP */
350 ndp_table_add(slirp, ip->ip_src, eth->h_source);
351 ndp_send_na(slirp, ip, icmp);
354 break;
356 case ICMP6_NDP_NA:
357 DEBUG_CALL(" type = Neighbor Advertisement");
358 if (ip->ip_hl == 255
359 && icmp->icmp6_code == 0
360 && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
361 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
362 && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
363 || icmp->icmp6_nna.S == 0)) {
364 ndp_table_add(slirp, ip->ip_src, eth->h_source);
366 break;
368 case ICMP6_NDP_REDIRECT:
369 DEBUG_CALL(" type = Redirect");
370 slirp->cb->guest_error(
371 "Warning: guest sent NDP REDIRECT, but shouldn't");
372 break;
377 * Process a received ICMPv6 message.
379 void icmp6_input(struct mbuf *m)
381 struct icmp6 *icmp;
382 struct ip6 *ip = mtod(m, struct ip6 *);
383 Slirp *slirp = m->slirp;
384 int hlen = sizeof(struct ip6);
386 DEBUG_CALL("icmp6_input");
387 DEBUG_ARG("m = %p", m);
388 DEBUG_ARG("m_len = %d", m->m_len);
390 if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
391 goto end;
394 if (ip6_cksum(m)) {
395 goto end;
398 m->m_len -= hlen;
399 m->m_data += hlen;
400 icmp = mtod(m, struct icmp6 *);
401 m->m_len += hlen;
402 m->m_data -= hlen;
404 DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
405 switch (icmp->icmp6_type) {
406 case ICMP6_ECHO_REQUEST:
407 if (in6_equal_host(&ip->ip_dst)) {
408 icmp6_send_echoreply(m, slirp, ip, icmp);
409 } else {
410 /* TODO */
411 g_critical("external icmpv6 not supported yet");
413 break;
415 case ICMP6_NDP_RS:
416 case ICMP6_NDP_RA:
417 case ICMP6_NDP_NS:
418 case ICMP6_NDP_NA:
419 case ICMP6_NDP_REDIRECT:
420 ndp_input(m, slirp, ip, icmp);
421 break;
423 case ICMP6_UNREACH:
424 case ICMP6_TOOBIG:
425 case ICMP6_TIMXCEED:
426 case ICMP6_PARAMPROB:
427 /* XXX? report error? close socket? */
428 default:
429 break;
432 end:
433 m_free(m);