]>
git.proxmox.com Git - mirror_qemu.git/blob - slirp/src/ip6_icmp.c
5642457fdd012754865bbd8a8306beb9a56937f5
1 /* SPDX-License-Identifier: BSD-3-Clause */
4 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
10 #define NDP_Interval g_rand_int_range(slirp->grand, \
11 NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
13 static void ra_timer_handler(void *opaque
)
15 Slirp
*slirp
= opaque
;
17 slirp
->cb
->timer_mod(slirp
->ra_timer
,
18 slirp
->cb
->clock_get_ns(slirp
->opaque
) / SCALE_MS
+ NDP_Interval
,
23 void icmp6_init(Slirp
*slirp
)
25 if (!slirp
->in6_enabled
) {
29 slirp
->ra_timer
= slirp
->cb
->timer_new(ra_timer_handler
, slirp
, slirp
->opaque
);
30 slirp
->cb
->timer_mod(slirp
->ra_timer
,
31 slirp
->cb
->clock_get_ns(slirp
->opaque
) / SCALE_MS
+ NDP_Interval
,
35 void icmp6_cleanup(Slirp
*slirp
)
37 if (!slirp
->in6_enabled
) {
41 slirp
->cb
->timer_free(slirp
->ra_timer
, slirp
->opaque
);
44 static void icmp6_send_echoreply(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
47 struct mbuf
*t
= m_get(slirp
);
48 t
->m_len
= sizeof(struct ip6
) + ntohs(ip
->ip_pl
);
49 memcpy(t
->m_data
, m
->m_data
, t
->m_len
);
52 struct ip6
*rip
= mtod(t
, struct ip6
*);
53 rip
->ip_dst
= ip
->ip_src
;
54 rip
->ip_src
= ip
->ip_dst
;
57 t
->m_data
+= sizeof(struct ip6
);
58 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
59 ricmp
->icmp6_type
= ICMP6_ECHO_REPLY
;
60 ricmp
->icmp6_cksum
= 0;
63 t
->m_data
-= sizeof(struct ip6
);
64 ricmp
->icmp6_cksum
= ip6_cksum(t
);
66 ip6_output(NULL
, t
, 0);
69 void icmp6_send_error(struct mbuf
*m
, uint8_t type
, uint8_t code
)
71 Slirp
*slirp
= m
->slirp
;
73 struct ip6
*ip
= mtod(m
, struct ip6
*);
74 char addrstr
[INET6_ADDRSTRLEN
];
76 DEBUG_CALL("icmp6_send_error");
77 DEBUG_ARG("type = %d, code = %d", type
, code
);
79 if (IN6_IS_ADDR_MULTICAST(&ip
->ip_src
) ||
80 in6_zero(&ip
->ip_src
)) {
81 /* TODO icmp error? */
88 struct ip6
*rip
= mtod(t
, struct ip6
*);
89 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
90 rip
->ip_dst
= ip
->ip_src
;
91 inet_ntop(AF_INET6
, &rip
->ip_dst
, addrstr
, INET6_ADDRSTRLEN
);
92 DEBUG_ARG("target = %s", addrstr
);
94 rip
->ip_nh
= IPPROTO_ICMPV6
;
95 const int error_data_len
= MIN(m
->m_len
,
96 IF_MTU
- (sizeof(struct ip6
) + ICMP6_ERROR_MINLEN
));
97 rip
->ip_pl
= htons(ICMP6_ERROR_MINLEN
+ error_data_len
);
98 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
101 t
->m_data
+= sizeof(struct ip6
);
102 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
103 ricmp
->icmp6_type
= type
;
104 ricmp
->icmp6_code
= code
;
105 ricmp
->icmp6_cksum
= 0;
110 ricmp
->icmp6_err
.unused
= 0;
113 ricmp
->icmp6_err
.mtu
= htonl(IF_MTU
);
115 case ICMP6_PARAMPROB
:
116 /* TODO: Handle this case */
119 g_assert_not_reached();
122 t
->m_data
+= ICMP6_ERROR_MINLEN
;
123 memcpy(t
->m_data
, m
->m_data
, error_data_len
);
126 t
->m_data
-= ICMP6_ERROR_MINLEN
;
127 t
->m_data
-= sizeof(struct ip6
);
128 ricmp
->icmp6_cksum
= ip6_cksum(t
);
130 ip6_output(NULL
, t
, 0);
134 * Send NDP Router Advertisement
136 void ndp_send_ra(Slirp
*slirp
)
138 DEBUG_CALL("ndp_send_ra");
140 /* Build IPv6 packet */
141 struct mbuf
*t
= m_get(slirp
);
142 struct ip6
*rip
= mtod(t
, struct ip6
*);
144 struct in6_addr addr
;
147 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
148 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
149 rip
->ip_nh
= IPPROTO_ICMPV6
;
151 /* Build ICMPv6 packet */
152 t
->m_data
+= sizeof(struct ip6
);
153 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
154 ricmp
->icmp6_type
= ICMP6_NDP_RA
;
155 ricmp
->icmp6_code
= 0;
156 ricmp
->icmp6_cksum
= 0;
159 ricmp
->icmp6_nra
.chl
= NDP_AdvCurHopLimit
;
160 ricmp
->icmp6_nra
.M
= NDP_AdvManagedFlag
;
161 ricmp
->icmp6_nra
.O
= NDP_AdvOtherConfigFlag
;
162 ricmp
->icmp6_nra
.reserved
= 0;
163 ricmp
->icmp6_nra
.lifetime
= htons(NDP_AdvDefaultLifetime
);
164 ricmp
->icmp6_nra
.reach_time
= htonl(NDP_AdvReachableTime
);
165 ricmp
->icmp6_nra
.retrans_time
= htonl(NDP_AdvRetransTime
);
166 t
->m_data
+= ICMP6_NDP_RA_MINLEN
;
167 pl_size
+= ICMP6_NDP_RA_MINLEN
;
169 /* Source link-layer address (NDP option) */
170 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
171 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
172 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
173 in6_compute_ethaddr(rip
->ip_src
, opt
->ndpopt_linklayer
);
174 t
->m_data
+= NDPOPT_LINKLAYER_LEN
;
175 pl_size
+= NDPOPT_LINKLAYER_LEN
;
177 /* Prefix information (NDP option) */
178 struct ndpopt
*opt2
= mtod(t
, struct ndpopt
*);
179 opt2
->ndpopt_type
= NDPOPT_PREFIX_INFO
;
180 opt2
->ndpopt_len
= NDPOPT_PREFIXINFO_LEN
/ 8;
181 opt2
->ndpopt_prefixinfo
.prefix_length
= slirp
->vprefix_len
;
182 opt2
->ndpopt_prefixinfo
.L
= 1;
183 opt2
->ndpopt_prefixinfo
.A
= 1;
184 opt2
->ndpopt_prefixinfo
.reserved1
= 0;
185 opt2
->ndpopt_prefixinfo
.valid_lt
= htonl(NDP_AdvValidLifetime
);
186 opt2
->ndpopt_prefixinfo
.pref_lt
= htonl(NDP_AdvPrefLifetime
);
187 opt2
->ndpopt_prefixinfo
.reserved2
= 0;
188 opt2
->ndpopt_prefixinfo
.prefix
= slirp
->vprefix_addr6
;
189 t
->m_data
+= NDPOPT_PREFIXINFO_LEN
;
190 pl_size
+= NDPOPT_PREFIXINFO_LEN
;
192 /* Prefix information (NDP option) */
193 if (get_dns6_addr(&addr
, &scope_id
) >= 0) {
194 /* Host system does have an IPv6 DNS server, announce our proxy. */
195 struct ndpopt
*opt3
= mtod(t
, struct ndpopt
*);
196 opt3
->ndpopt_type
= NDPOPT_RDNSS
;
197 opt3
->ndpopt_len
= NDPOPT_RDNSS_LEN
/ 8;
198 opt3
->ndpopt_rdnss
.reserved
= 0;
199 opt3
->ndpopt_rdnss
.lifetime
= htonl(2 * NDP_MaxRtrAdvInterval
);
200 opt3
->ndpopt_rdnss
.addr
= slirp
->vnameserver_addr6
;
201 t
->m_data
+= NDPOPT_RDNSS_LEN
;
202 pl_size
+= NDPOPT_RDNSS_LEN
;
205 rip
->ip_pl
= htons(pl_size
);
206 t
->m_data
-= sizeof(struct ip6
) + pl_size
;
207 t
->m_len
= sizeof(struct ip6
) + pl_size
;
209 /* ICMPv6 Checksum */
210 ricmp
->icmp6_cksum
= ip6_cksum(t
);
212 ip6_output(NULL
, t
, 0);
216 * Send NDP Neighbor Solitication
218 void ndp_send_ns(Slirp
*slirp
, struct in6_addr addr
)
220 char addrstr
[INET6_ADDRSTRLEN
];
222 inet_ntop(AF_INET6
, &addr
, addrstr
, INET6_ADDRSTRLEN
);
224 DEBUG_CALL("ndp_send_ns");
225 DEBUG_ARG("target = %s", addrstr
);
227 /* Build IPv6 packet */
228 struct mbuf
*t
= m_get(slirp
);
229 struct ip6
*rip
= mtod(t
, struct ip6
*);
230 rip
->ip_src
= slirp
->vhost_addr6
;
231 rip
->ip_dst
= (struct in6_addr
)SOLICITED_NODE_PREFIX
;
232 memcpy(&rip
->ip_dst
.s6_addr
[13], &addr
.s6_addr
[13], 3);
233 rip
->ip_nh
= IPPROTO_ICMPV6
;
234 rip
->ip_pl
= htons(ICMP6_NDP_NS_MINLEN
+ NDPOPT_LINKLAYER_LEN
);
235 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
237 /* Build ICMPv6 packet */
238 t
->m_data
+= sizeof(struct ip6
);
239 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
240 ricmp
->icmp6_type
= ICMP6_NDP_NS
;
241 ricmp
->icmp6_code
= 0;
242 ricmp
->icmp6_cksum
= 0;
245 ricmp
->icmp6_nns
.reserved
= 0;
246 ricmp
->icmp6_nns
.target
= addr
;
248 /* Build NDP option */
249 t
->m_data
+= ICMP6_NDP_NS_MINLEN
;
250 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
251 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
252 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
253 in6_compute_ethaddr(slirp
->vhost_addr6
, opt
->ndpopt_linklayer
);
255 /* ICMPv6 Checksum */
256 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
257 t
->m_data
-= sizeof(struct ip6
);
258 ricmp
->icmp6_cksum
= ip6_cksum(t
);
260 ip6_output(NULL
, t
, 1);
264 * Send NDP Neighbor Advertisement
266 static void ndp_send_na(Slirp
*slirp
, struct ip6
*ip
, struct icmp6
*icmp
)
268 /* Build IPv6 packet */
269 struct mbuf
*t
= m_get(slirp
);
270 struct ip6
*rip
= mtod(t
, struct ip6
*);
271 rip
->ip_src
= icmp
->icmp6_nns
.target
;
272 if (in6_zero(&ip
->ip_src
)) {
273 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
275 rip
->ip_dst
= ip
->ip_src
;
277 rip
->ip_nh
= IPPROTO_ICMPV6
;
278 rip
->ip_pl
= htons(ICMP6_NDP_NA_MINLEN
279 + NDPOPT_LINKLAYER_LEN
);
280 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
282 /* Build ICMPv6 packet */
283 t
->m_data
+= sizeof(struct ip6
);
284 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
285 ricmp
->icmp6_type
= ICMP6_NDP_NA
;
286 ricmp
->icmp6_code
= 0;
287 ricmp
->icmp6_cksum
= 0;
290 ricmp
->icmp6_nna
.R
= NDP_IsRouter
;
291 ricmp
->icmp6_nna
.S
= !IN6_IS_ADDR_MULTICAST(&rip
->ip_dst
);
292 ricmp
->icmp6_nna
.O
= 1;
293 ricmp
->icmp6_nna
.reserved_hi
= 0;
294 ricmp
->icmp6_nna
.reserved_lo
= 0;
295 ricmp
->icmp6_nna
.target
= icmp
->icmp6_nns
.target
;
297 /* Build NDP option */
298 t
->m_data
+= ICMP6_NDP_NA_MINLEN
;
299 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
300 opt
->ndpopt_type
= NDPOPT_LINKLAYER_TARGET
;
301 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
302 in6_compute_ethaddr(ricmp
->icmp6_nna
.target
,
303 opt
->ndpopt_linklayer
);
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
);
310 ip6_output(NULL
, t
, 0);
314 * Process a NDP message
316 static void ndp_input(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
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
;
325 switch (icmp
->icmp6_type
) {
327 DEBUG_CALL(" type = Router Solicitation");
329 && icmp
->icmp6_code
== 0
330 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_RS_MINLEN
) {
332 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
339 DEBUG_CALL(" type = Router Advertisement");
340 slirp
->cb
->guest_error("Warning: guest sent NDP RA, but shouldn't",
345 DEBUG_CALL(" type = Neighbor Solicitation");
347 && icmp
->icmp6_code
== 0
348 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nns
.target
)
349 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NS_MINLEN
350 && (!in6_zero(&ip
->ip_src
)
351 || in6_solicitednode_multicast(&ip
->ip_dst
))) {
352 if (in6_equal_host(&icmp
->icmp6_nns
.target
)) {
354 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
355 ndp_send_na(slirp
, ip
, icmp
);
361 DEBUG_CALL(" type = Neighbor Advertisement");
363 && icmp
->icmp6_code
== 0
364 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NA_MINLEN
365 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nna
.target
)
366 && (!IN6_IS_ADDR_MULTICAST(&ip
->ip_dst
)
367 || icmp
->icmp6_nna
.S
== 0)) {
368 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
372 case ICMP6_NDP_REDIRECT
:
373 DEBUG_CALL(" type = Redirect");
374 slirp
->cb
->guest_error(
375 "Warning: guest sent NDP REDIRECT, but shouldn't", slirp
->opaque
);
381 * Process a received ICMPv6 message.
383 void icmp6_input(struct mbuf
*m
)
386 struct ip6
*ip
= mtod(m
, struct ip6
*);
387 Slirp
*slirp
= m
->slirp
;
388 int hlen
= sizeof(struct ip6
);
390 DEBUG_CALL("icmp6_input");
391 DEBUG_ARG("m = %p", m
);
392 DEBUG_ARG("m_len = %d", m
->m_len
);
394 if (ntohs(ip
->ip_pl
) < ICMP6_MINLEN
) {
404 icmp
= mtod(m
, struct icmp6
*);
408 DEBUG_ARG("icmp6_type = %d", icmp
->icmp6_type
);
409 switch (icmp
->icmp6_type
) {
410 case ICMP6_ECHO_REQUEST
:
411 if (in6_equal_host(&ip
->ip_dst
)) {
412 icmp6_send_echoreply(m
, slirp
, ip
, icmp
);
415 g_critical("external icmpv6 not supported yet");
423 case ICMP6_NDP_REDIRECT
:
424 ndp_input(m
, slirp
, ip
, icmp
);
430 case ICMP6_PARAMPROB
:
431 /* XXX? report error? close socket? */