1/* SPDX-License-Identifier: BSD-3-Clause */
2/*
3 * Copyright (c) 2013
4 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
5 */
6
7#include "slirp.h"
8#include "ip6_icmp.h"
9
10#define NDP_Interval \
11 g_rand_int_range(slirp->grand, NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
12
13static void ra_timer_handler(void *opaque)
14{
15 Slirp *slirp = opaque;
16
17 slirp->cb->timer_mod(slirp->ra_timer,
18 slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS +
19 NDP_Interval,
20 slirp->opaque);
21 ndp_send_ra(slirp);
22}
23
24void icmp6_init(Slirp *slirp)
25{
26 if (!slirp->in6_enabled) {
27 return;
28 }
29
30 slirp->ra_timer =
31 slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque);
32 slirp->cb->timer_mod(slirp->ra_timer,
33 slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS +
34 NDP_Interval,
35 slirp->opaque);
36}
37
38void icmp6_cleanup(Slirp *slirp)
39{
40 if (!slirp->in6_enabled) {
41 return;
42 }
43
44 slirp->cb->timer_free(slirp->ra_timer, slirp->opaque);
45}
46
47static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
48 struct icmp6 *icmp)
49{
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);
53
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;
58
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;
64
65 /* Checksum */
66 t->m_data -= sizeof(struct ip6);
67 ricmp->icmp6_cksum = ip6_cksum(t);
68
69 ip6_output(NULL, t, 0);
70}
71
72void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
73{
74 Slirp *slirp = m->slirp;
75 struct mbuf *t;
76 struct ip6 *ip = mtod(m, struct ip6 *);
77 char addrstr[INET6_ADDRSTRLEN];
78
79 DEBUG_CALL("icmp6_send_error");
80 DEBUG_ARG("type = %d, code = %d", type, code);
81
82 if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) || in6_zero(&ip->ip_src)) {
83 /* TODO icmp error? */
84 return;
85 }
86
87 t = m_get(slirp);
88
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 inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
94 DEBUG_ARG("target = %s", addrstr);
95
96 rip->ip_nh = IPPROTO_ICMPV6;
97 const int error_data_len =
98 MIN(m->m_len, IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
99 rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
100 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
101
102 /* ICMPv6 packet */
103 t->m_data += sizeof(struct ip6);
104 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
105 ricmp->icmp6_type = type;
106 ricmp->icmp6_code = code;
107 ricmp->icmp6_cksum = 0;
108
109 switch (type) {
110 case ICMP6_UNREACH:
111 case ICMP6_TIMXCEED:
112 ricmp->icmp6_err.unused = 0;
113 break;
114 case ICMP6_TOOBIG:
115 ricmp->icmp6_err.mtu = htonl(IF_MTU);
116 break;
117 case ICMP6_PARAMPROB:
118 /* TODO: Handle this case */
119 break;
120 default:
121 g_assert_not_reached();
122 break;
123 }
124 t->m_data += ICMP6_ERROR_MINLEN;
125 memcpy(t->m_data, m->m_data, error_data_len);
126
127 /* Checksum */
128 t->m_data -= ICMP6_ERROR_MINLEN;
129 t->m_data -= sizeof(struct ip6);
130 ricmp->icmp6_cksum = ip6_cksum(t);
131
132 ip6_output(NULL, t, 0);
133}
134
135/*
136 * Send NDP Router Advertisement
137 */
138void ndp_send_ra(Slirp *slirp)
139{
140 DEBUG_CALL("ndp_send_ra");
141
142 /* Build IPv6 packet */
143 struct mbuf *t = m_get(slirp);
144 struct ip6 *rip = mtod(t, struct ip6 *);
145 size_t pl_size = 0;
146 struct in6_addr addr;
147 uint32_t scope_id;
148
149 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
150 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
151 rip->ip_nh = IPPROTO_ICMPV6;
152
153 /* Build ICMPv6 packet */
154 t->m_data += sizeof(struct ip6);
155 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
156 ricmp->icmp6_type = ICMP6_NDP_RA;
157 ricmp->icmp6_code = 0;
158 ricmp->icmp6_cksum = 0;
159
160 /* NDP */
161 ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
162 ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
163 ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
164 ricmp->icmp6_nra.reserved = 0;
165 ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
166 ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
167 ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
168 t->m_data += ICMP6_NDP_RA_MINLEN;
169 pl_size += ICMP6_NDP_RA_MINLEN;
170
171 /* Source link-layer address (NDP option) */
172 struct ndpopt *opt = mtod(t, struct ndpopt *);
173 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
174 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
175 in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
176 t->m_data += NDPOPT_LINKLAYER_LEN;
177 pl_size += NDPOPT_LINKLAYER_LEN;
178
179 /* Prefix information (NDP option) */
180 struct ndpopt *opt2 = mtod(t, struct ndpopt *);
181 opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
182 opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
183 opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
184 opt2->ndpopt_prefixinfo.L = 1;
185 opt2->ndpopt_prefixinfo.A = 1;
186 opt2->ndpopt_prefixinfo.reserved1 = 0;
187 opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
188 opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
189 opt2->ndpopt_prefixinfo.reserved2 = 0;
190 opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
191 t->m_data += NDPOPT_PREFIXINFO_LEN;
192 pl_size += NDPOPT_PREFIXINFO_LEN;
193
194 /* Prefix information (NDP option) */
195 if (get_dns6_addr(&addr, &scope_id) >= 0) {
196 /* Host system does have an IPv6 DNS server, announce our proxy. */
197 struct ndpopt *opt3 = mtod(t, struct ndpopt *);
198 opt3->ndpopt_type = NDPOPT_RDNSS;
199 opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8;
200 opt3->ndpopt_rdnss.reserved = 0;
201 opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval);
202 opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6;
203 t->m_data += NDPOPT_RDNSS_LEN;
204 pl_size += NDPOPT_RDNSS_LEN;
205 }
206
207 rip->ip_pl = htons(pl_size);
208 t->m_data -= sizeof(struct ip6) + pl_size;
209 t->m_len = sizeof(struct ip6) + pl_size;
210
211 /* ICMPv6 Checksum */
212 ricmp->icmp6_cksum = ip6_cksum(t);
213
214 ip6_output(NULL, t, 0);
215}
216
217/*
218 * Send NDP Neighbor Solitication
219 */
220void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
221{
222 char addrstr[INET6_ADDRSTRLEN];
223
224 inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
225
226 DEBUG_CALL("ndp_send_ns");
227 DEBUG_ARG("target = %s", addrstr);
228
229 /* Build IPv6 packet */
230 struct mbuf *t = m_get(slirp);
231 struct ip6 *rip = mtod(t, struct ip6 *);
232 rip->ip_src = slirp->vhost_addr6;
233 rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
234 memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
235 rip->ip_nh = IPPROTO_ICMPV6;
236 rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
237 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
238
239 /* Build ICMPv6 packet */
240 t->m_data += sizeof(struct ip6);
241 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
242 ricmp->icmp6_type = ICMP6_NDP_NS;
243 ricmp->icmp6_code = 0;
244 ricmp->icmp6_cksum = 0;
245
246 /* NDP */
247 ricmp->icmp6_nns.reserved = 0;
248 ricmp->icmp6_nns.target = addr;
249
250 /* Build NDP option */
251 t->m_data += ICMP6_NDP_NS_MINLEN;
252 struct ndpopt *opt = mtod(t, struct ndpopt *);
253 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
254 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
255 in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
256
257 /* ICMPv6 Checksum */
258 t->m_data -= ICMP6_NDP_NA_MINLEN;
259 t->m_data -= sizeof(struct ip6);
260 ricmp->icmp6_cksum = ip6_cksum(t);
261
262 ip6_output(NULL, t, 1);
263}
264
265/*
266 * Send NDP Neighbor Advertisement
267 */
268static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
269{
270 /* Build IPv6 packet */
271 struct mbuf *t = m_get(slirp);
272 struct ip6 *rip = mtod(t, struct ip6 *);
273 rip->ip_src = icmp->icmp6_nns.target;
274 if (in6_zero(&ip->ip_src)) {
275 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
276 } else {
277 rip->ip_dst = ip->ip_src;
278 }
279 rip->ip_nh = IPPROTO_ICMPV6;
280 rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN + NDPOPT_LINKLAYER_LEN);
281 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
282
283 /* Build ICMPv6 packet */
284 t->m_data += sizeof(struct ip6);
285 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
286 ricmp->icmp6_type = ICMP6_NDP_NA;
287 ricmp->icmp6_code = 0;
288 ricmp->icmp6_cksum = 0;
289
290 /* NDP */
291 ricmp->icmp6_nna.R = NDP_IsRouter;
292 ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
293 ricmp->icmp6_nna.O = 1;
294 ricmp->icmp6_nna.reserved_hi = 0;
295 ricmp->icmp6_nna.reserved_lo = 0;
296 ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
297
298 /* Build NDP option */
299 t->m_data += ICMP6_NDP_NA_MINLEN;
300 struct ndpopt *opt = mtod(t, struct ndpopt *);
301 opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
302 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
303 in6_compute_ethaddr(ricmp->icmp6_nna.target, opt->ndpopt_linklayer);
304
305 /* ICMPv6 Checksum */
306 t->m_data -= ICMP6_NDP_NA_MINLEN;
307 t->m_data -= sizeof(struct ip6);
308 ricmp->icmp6_cksum = ip6_cksum(t);
309
310 ip6_output(NULL, t, 0);
311}
312
313/*
314 * Process a NDP message
315 */
316static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
317 struct icmp6 *icmp)
318{
319 m->m_len += ETH_HLEN;
320 m->m_data -= ETH_HLEN;
321 struct ethhdr *eth = mtod(m, struct ethhdr *);
322 m->m_len -= ETH_HLEN;
323 m->m_data += ETH_HLEN;
324
325 switch (icmp->icmp6_type) {
326 case ICMP6_NDP_RS:
327 DEBUG_CALL(" type = Router Solicitation");
328 if (ip->ip_hl == 255 && 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");
339 slirp->cb->guest_error("Warning: guest sent NDP RA, but shouldn't",
340 slirp->opaque);
341 break;
342
343 case ICMP6_NDP_NS:
344 DEBUG_CALL(" type = Neighbor Solicitation");
345 if (ip->ip_hl == 255 && icmp->icmp6_code == 0 &&
346 !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target) &&
347 ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN &&
348 (!in6_zero(&ip->ip_src) ||
349 in6_solicitednode_multicast(&ip->ip_dst))) {
350 if (in6_equal_host(&icmp->icmp6_nns.target)) {
351 /* Gratuitous NDP */
352 ndp_table_add(slirp, ip->ip_src, eth->h_source);
353 ndp_send_na(slirp, ip, icmp);
354 }
355 }
356 break;
357
358 case ICMP6_NDP_NA:
359 DEBUG_CALL(" type = Neighbor Advertisement");
360 if (ip->ip_hl == 255 && icmp->icmp6_code == 0 &&
361 ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN &&
362 !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target) &&
363 (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst) || icmp->icmp6_nna.S == 0)) {
364 ndp_table_add(slirp, ip->ip_src, eth->h_source);
365 }
366 break;
367
368 case ICMP6_NDP_REDIRECT:
369 DEBUG_CALL(" type = Redirect");
370 slirp->cb->guest_error(
371 "Warning: guest sent NDP REDIRECT, but shouldn't", slirp->opaque);
372 break;
373 }
374}
375
376/*
377 * Process a received ICMPv6 message.
378 */
379void icmp6_input(struct mbuf *m)
380{
381 struct icmp6 *icmp;
382 struct ip6 *ip = mtod(m, struct ip6 *);
383 Slirp *slirp = m->slirp;
384 int hlen = sizeof(struct ip6);
385
386 DEBUG_CALL("icmp6_input");
387 DEBUG_ARG("m = %p", m);
388 DEBUG_ARG("m_len = %d", m->m_len);
389
390 if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
391 goto end;
392 }
393
394 if (ip6_cksum(m)) {
395 goto end;
396 }
397
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;
403
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");
412 }
413 break;
414
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;
422
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;
430 }
431
432end:
433 m_free(m);
434}
435