]>
git.proxmox.com Git - mirror_qemu.git/blob - slirp/ip6_icmp.c
3 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
6 #include "qemu/osdep.h"
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() / SCALE_MS
+ NDP_Interval
);
22 void icmp6_init(Slirp
*slirp
)
24 if (!slirp
->in6_enabled
) {
28 slirp
->ra_timer
= slirp
->cb
->timer_new(ra_timer_handler
, slirp
);
29 slirp
->cb
->timer_mod(slirp
->ra_timer
,
30 slirp
->cb
->clock_get_ns() / SCALE_MS
+ NDP_Interval
);
33 void icmp6_cleanup(Slirp
*slirp
)
35 if (!slirp
->in6_enabled
) {
39 slirp
->cb
->timer_free(slirp
->ra_timer
);
42 static void icmp6_send_echoreply(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
45 struct mbuf
*t
= m_get(slirp
);
46 t
->m_len
= sizeof(struct ip6
) + ntohs(ip
->ip_pl
);
47 memcpy(t
->m_data
, m
->m_data
, t
->m_len
);
50 struct ip6
*rip
= mtod(t
, struct ip6
*);
51 rip
->ip_dst
= ip
->ip_src
;
52 rip
->ip_src
= ip
->ip_dst
;
55 t
->m_data
+= sizeof(struct ip6
);
56 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
57 ricmp
->icmp6_type
= ICMP6_ECHO_REPLY
;
58 ricmp
->icmp6_cksum
= 0;
61 t
->m_data
-= sizeof(struct ip6
);
62 ricmp
->icmp6_cksum
= ip6_cksum(t
);
64 ip6_output(NULL
, t
, 0);
67 void icmp6_send_error(struct mbuf
*m
, uint8_t type
, uint8_t code
)
69 Slirp
*slirp
= m
->slirp
;
71 struct ip6
*ip
= mtod(m
, struct ip6
*);
72 char addrstr
[INET6_ADDRSTRLEN
];
74 DEBUG_CALL("icmp6_send_error");
75 DEBUG_ARG("type = %d, code = %d", type
, code
);
77 if (IN6_IS_ADDR_MULTICAST(&ip
->ip_src
) ||
78 in6_zero(&ip
->ip_src
)) {
79 /* TODO icmp error? */
86 struct ip6
*rip
= mtod(t
, struct ip6
*);
87 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
88 rip
->ip_dst
= ip
->ip_src
;
89 inet_ntop(AF_INET6
, &rip
->ip_dst
, addrstr
, INET6_ADDRSTRLEN
);
90 DEBUG_ARG("target = %s", addrstr
);
92 rip
->ip_nh
= IPPROTO_ICMPV6
;
93 const int error_data_len
= MIN(m
->m_len
,
94 IF_MTU
- (sizeof(struct ip6
) + ICMP6_ERROR_MINLEN
));
95 rip
->ip_pl
= htons(ICMP6_ERROR_MINLEN
+ error_data_len
);
96 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
99 t
->m_data
+= sizeof(struct ip6
);
100 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
101 ricmp
->icmp6_type
= type
;
102 ricmp
->icmp6_code
= code
;
103 ricmp
->icmp6_cksum
= 0;
108 ricmp
->icmp6_err
.unused
= 0;
111 ricmp
->icmp6_err
.mtu
= htonl(IF_MTU
);
113 case ICMP6_PARAMPROB
:
114 /* TODO: Handle this case */
117 g_assert_not_reached();
120 t
->m_data
+= ICMP6_ERROR_MINLEN
;
121 memcpy(t
->m_data
, m
->m_data
, error_data_len
);
124 t
->m_data
-= ICMP6_ERROR_MINLEN
;
125 t
->m_data
-= sizeof(struct ip6
);
126 ricmp
->icmp6_cksum
= ip6_cksum(t
);
128 ip6_output(NULL
, t
, 0);
132 * Send NDP Router Advertisement
134 void ndp_send_ra(Slirp
*slirp
)
136 DEBUG_CALL("ndp_send_ra");
138 /* Build IPv6 packet */
139 struct mbuf
*t
= m_get(slirp
);
140 struct ip6
*rip
= mtod(t
, struct ip6
*);
142 struct in6_addr addr
;
145 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
146 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
147 rip
->ip_nh
= IPPROTO_ICMPV6
;
149 /* Build ICMPv6 packet */
150 t
->m_data
+= sizeof(struct ip6
);
151 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
152 ricmp
->icmp6_type
= ICMP6_NDP_RA
;
153 ricmp
->icmp6_code
= 0;
154 ricmp
->icmp6_cksum
= 0;
157 ricmp
->icmp6_nra
.chl
= NDP_AdvCurHopLimit
;
158 ricmp
->icmp6_nra
.M
= NDP_AdvManagedFlag
;
159 ricmp
->icmp6_nra
.O
= NDP_AdvOtherConfigFlag
;
160 ricmp
->icmp6_nra
.reserved
= 0;
161 ricmp
->icmp6_nra
.lifetime
= htons(NDP_AdvDefaultLifetime
);
162 ricmp
->icmp6_nra
.reach_time
= htonl(NDP_AdvReachableTime
);
163 ricmp
->icmp6_nra
.retrans_time
= htonl(NDP_AdvRetransTime
);
164 t
->m_data
+= ICMP6_NDP_RA_MINLEN
;
165 pl_size
+= ICMP6_NDP_RA_MINLEN
;
167 /* Source link-layer address (NDP option) */
168 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
169 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
170 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
171 in6_compute_ethaddr(rip
->ip_src
, opt
->ndpopt_linklayer
);
172 t
->m_data
+= NDPOPT_LINKLAYER_LEN
;
173 pl_size
+= NDPOPT_LINKLAYER_LEN
;
175 /* Prefix information (NDP option) */
176 struct ndpopt
*opt2
= mtod(t
, struct ndpopt
*);
177 opt2
->ndpopt_type
= NDPOPT_PREFIX_INFO
;
178 opt2
->ndpopt_len
= NDPOPT_PREFIXINFO_LEN
/ 8;
179 opt2
->ndpopt_prefixinfo
.prefix_length
= slirp
->vprefix_len
;
180 opt2
->ndpopt_prefixinfo
.L
= 1;
181 opt2
->ndpopt_prefixinfo
.A
= 1;
182 opt2
->ndpopt_prefixinfo
.reserved1
= 0;
183 opt2
->ndpopt_prefixinfo
.valid_lt
= htonl(NDP_AdvValidLifetime
);
184 opt2
->ndpopt_prefixinfo
.pref_lt
= htonl(NDP_AdvPrefLifetime
);
185 opt2
->ndpopt_prefixinfo
.reserved2
= 0;
186 opt2
->ndpopt_prefixinfo
.prefix
= slirp
->vprefix_addr6
;
187 t
->m_data
+= NDPOPT_PREFIXINFO_LEN
;
188 pl_size
+= NDPOPT_PREFIXINFO_LEN
;
190 /* Prefix information (NDP option) */
191 if (get_dns6_addr(&addr
, &scope_id
) >= 0) {
192 /* Host system does have an IPv6 DNS server, announce our proxy. */
193 struct ndpopt
*opt3
= mtod(t
, struct ndpopt
*);
194 opt3
->ndpopt_type
= NDPOPT_RDNSS
;
195 opt3
->ndpopt_len
= NDPOPT_RDNSS_LEN
/ 8;
196 opt3
->ndpopt_rdnss
.reserved
= 0;
197 opt3
->ndpopt_rdnss
.lifetime
= htonl(2 * NDP_MaxRtrAdvInterval
);
198 opt3
->ndpopt_rdnss
.addr
= slirp
->vnameserver_addr6
;
199 t
->m_data
+= NDPOPT_RDNSS_LEN
;
200 pl_size
+= NDPOPT_RDNSS_LEN
;
203 rip
->ip_pl
= htons(pl_size
);
204 t
->m_data
-= sizeof(struct ip6
) + pl_size
;
205 t
->m_len
= sizeof(struct ip6
) + pl_size
;
207 /* ICMPv6 Checksum */
208 ricmp
->icmp6_cksum
= ip6_cksum(t
);
210 ip6_output(NULL
, t
, 0);
214 * Send NDP Neighbor Solitication
216 void ndp_send_ns(Slirp
*slirp
, struct in6_addr addr
)
218 char addrstr
[INET6_ADDRSTRLEN
];
220 inet_ntop(AF_INET6
, &addr
, addrstr
, INET6_ADDRSTRLEN
);
222 DEBUG_CALL("ndp_send_ns");
223 DEBUG_ARG("target = %s", addrstr
);
225 /* Build IPv6 packet */
226 struct mbuf
*t
= m_get(slirp
);
227 struct ip6
*rip
= mtod(t
, struct ip6
*);
228 rip
->ip_src
= slirp
->vhost_addr6
;
229 rip
->ip_dst
= (struct in6_addr
)SOLICITED_NODE_PREFIX
;
230 memcpy(&rip
->ip_dst
.s6_addr
[13], &addr
.s6_addr
[13], 3);
231 rip
->ip_nh
= IPPROTO_ICMPV6
;
232 rip
->ip_pl
= htons(ICMP6_NDP_NS_MINLEN
+ NDPOPT_LINKLAYER_LEN
);
233 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
235 /* Build ICMPv6 packet */
236 t
->m_data
+= sizeof(struct ip6
);
237 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
238 ricmp
->icmp6_type
= ICMP6_NDP_NS
;
239 ricmp
->icmp6_code
= 0;
240 ricmp
->icmp6_cksum
= 0;
243 ricmp
->icmp6_nns
.reserved
= 0;
244 ricmp
->icmp6_nns
.target
= addr
;
246 /* Build NDP option */
247 t
->m_data
+= ICMP6_NDP_NS_MINLEN
;
248 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
249 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
250 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
251 in6_compute_ethaddr(slirp
->vhost_addr6
, opt
->ndpopt_linklayer
);
253 /* ICMPv6 Checksum */
254 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
255 t
->m_data
-= sizeof(struct ip6
);
256 ricmp
->icmp6_cksum
= ip6_cksum(t
);
258 ip6_output(NULL
, t
, 1);
262 * Send NDP Neighbor Advertisement
264 static void ndp_send_na(Slirp
*slirp
, struct ip6
*ip
, struct icmp6
*icmp
)
266 /* Build IPv6 packet */
267 struct mbuf
*t
= m_get(slirp
);
268 struct ip6
*rip
= mtod(t
, struct ip6
*);
269 rip
->ip_src
= icmp
->icmp6_nns
.target
;
270 if (in6_zero(&ip
->ip_src
)) {
271 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
273 rip
->ip_dst
= ip
->ip_src
;
275 rip
->ip_nh
= IPPROTO_ICMPV6
;
276 rip
->ip_pl
= htons(ICMP6_NDP_NA_MINLEN
277 + NDPOPT_LINKLAYER_LEN
);
278 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
280 /* Build ICMPv6 packet */
281 t
->m_data
+= sizeof(struct ip6
);
282 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
283 ricmp
->icmp6_type
= ICMP6_NDP_NA
;
284 ricmp
->icmp6_code
= 0;
285 ricmp
->icmp6_cksum
= 0;
288 ricmp
->icmp6_nna
.R
= NDP_IsRouter
;
289 ricmp
->icmp6_nna
.S
= !IN6_IS_ADDR_MULTICAST(&rip
->ip_dst
);
290 ricmp
->icmp6_nna
.O
= 1;
291 ricmp
->icmp6_nna
.reserved_hi
= 0;
292 ricmp
->icmp6_nna
.reserved_lo
= 0;
293 ricmp
->icmp6_nna
.target
= icmp
->icmp6_nns
.target
;
295 /* Build NDP option */
296 t
->m_data
+= ICMP6_NDP_NA_MINLEN
;
297 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
298 opt
->ndpopt_type
= NDPOPT_LINKLAYER_TARGET
;
299 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
300 in6_compute_ethaddr(ricmp
->icmp6_nna
.target
,
301 opt
->ndpopt_linklayer
);
303 /* ICMPv6 Checksum */
304 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
305 t
->m_data
-= sizeof(struct ip6
);
306 ricmp
->icmp6_cksum
= ip6_cksum(t
);
308 ip6_output(NULL
, t
, 0);
312 * Process a NDP message
314 static void ndp_input(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
317 m
->m_len
+= ETH_HLEN
;
318 m
->m_data
-= ETH_HLEN
;
319 struct ethhdr
*eth
= mtod(m
, struct ethhdr
*);
320 m
->m_len
-= ETH_HLEN
;
321 m
->m_data
+= ETH_HLEN
;
323 switch (icmp
->icmp6_type
) {
325 DEBUG_CALL(" type = Router Solicitation");
327 && icmp
->icmp6_code
== 0
328 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_RS_MINLEN
) {
330 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
337 DEBUG_CALL(" type = Router Advertisement");
338 slirp
->cb
->guest_error("Warning: guest sent NDP RA, but shouldn't");
342 DEBUG_CALL(" type = Neighbor Solicitation");
344 && icmp
->icmp6_code
== 0
345 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nns
.target
)
346 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NS_MINLEN
347 && (!in6_zero(&ip
->ip_src
)
348 || in6_solicitednode_multicast(&ip
->ip_dst
))) {
349 if (in6_equal_host(&icmp
->icmp6_nns
.target
)) {
351 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
352 ndp_send_na(slirp
, ip
, icmp
);
358 DEBUG_CALL(" type = Neighbor Advertisement");
360 && 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
)
364 || icmp
->icmp6_nna
.S
== 0)) {
365 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
369 case ICMP6_NDP_REDIRECT
:
370 DEBUG_CALL(" type = Redirect");
371 slirp
->cb
->guest_error(
372 "Warning: guest sent NDP REDIRECT, but shouldn't");
378 * Process a received ICMPv6 message.
380 void icmp6_input(struct mbuf
*m
)
383 struct ip6
*ip
= mtod(m
, struct ip6
*);
384 Slirp
*slirp
= m
->slirp
;
385 int hlen
= sizeof(struct ip6
);
387 DEBUG_CALL("icmp6_input");
388 DEBUG_ARG("m = %p", m
);
389 DEBUG_ARG("m_len = %d", m
->m_len
);
391 if (ntohs(ip
->ip_pl
) < ICMP6_MINLEN
) {
401 icmp
= mtod(m
, struct icmp6
*);
405 DEBUG_ARG("icmp6_type = %d", icmp
->icmp6_type
);
406 switch (icmp
->icmp6_type
) {
407 case ICMP6_ECHO_REQUEST
:
408 if (in6_equal_host(&ip
->ip_dst
)) {
409 icmp6_send_echoreply(m
, slirp
, ip
, icmp
);
412 g_critical("external icmpv6 not supported yet");
420 case ICMP6_NDP_REDIRECT
:
421 ndp_input(m
, slirp
, ip
, icmp
);
427 case ICMP6_PARAMPROB
:
428 /* XXX? report error? close socket? */