3 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
6 #include "qemu/osdep.h"
9 #include "qemu/timer.h"
10 #include "qemu/error-report.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
);
24 void icmp6_init(Slirp
*slirp
)
26 if (!slirp
->in6_enabled
) {
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
) {
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
,
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
);
55 struct ip6
*rip
= mtod(t
, struct ip6
*);
56 rip
->ip_dst
= ip
->ip_src
;
57 rip
->ip_src
= ip
->ip_dst
;
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;
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
;
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? */
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
);
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
);
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;
115 ricmp
->icmp6_err
.unused
= 0;
118 ricmp
->icmp6_err
.mtu
= htonl(IF_MTU
);
120 case ICMP6_PARAMPROB
:
121 /* TODO: Handle this case */
124 g_assert_not_reached();
127 t
->m_data
+= ICMP6_ERROR_MINLEN
;
128 memcpy(t
->m_data
, m
->m_data
, error_data_len
);
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
*);
149 struct in6_addr addr
;
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;
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
);
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;
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
;
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;
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
,
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
) {
332 DEBUG_CALL(" type = Router Solicitation");
334 && icmp
->icmp6_code
== 0
335 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_RS_MINLEN
) {
337 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
344 DEBUG_CALL(" type = Router Advertisement");
345 qemu_log_mask(LOG_GUEST_ERROR
,
346 "Warning: guest sent NDP RA, but shouldn't");
350 DEBUG_CALL(" type = Neighbor Solicitation");
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
)) {
359 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
360 ndp_send_na(slirp
, ip
, icmp
);
366 DEBUG_CALL(" type = Neighbor Advertisement");
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
);
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");
386 * Process a received ICMPv6 message.
388 void icmp6_input(struct mbuf
*m
)
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
) {
409 icmp
= mtod(m
, struct icmp6
*);
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
);
420 error_report("external icmpv6 not supported yet");
428 case ICMP6_NDP_REDIRECT
:
429 ndp_input(m
, slirp
, ip
, icmp
);
435 case ICMP6_PARAMPROB
:
436 /* XXX? report error? close socket? */