3 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
6 #include "qemu/osdep.h"
9 #include "qemu/timer.h"
10 #include "qemu/error-report.h"
14 #define NDP_Interval g_rand_int_range(slirp->grand, \
15 NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
17 static void ra_timer_handler(void *opaque
)
19 Slirp
*slirp
= opaque
;
20 timer_mod(slirp
->ra_timer
,
21 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) + NDP_Interval
);
25 void icmp6_init(Slirp
*slirp
)
27 slirp
->ra_timer
= timer_new_ms(QEMU_CLOCK_VIRTUAL
, ra_timer_handler
, slirp
);
28 timer_mod(slirp
->ra_timer
,
29 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) + NDP_Interval
);
32 void icmp6_cleanup(Slirp
*slirp
)
34 timer_del(slirp
->ra_timer
);
35 timer_free(slirp
->ra_timer
);
38 static void icmp6_send_echoreply(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
41 struct mbuf
*t
= m_get(slirp
);
42 t
->m_len
= sizeof(struct ip6
) + ntohs(ip
->ip_pl
);
43 memcpy(t
->m_data
, m
->m_data
, t
->m_len
);
46 struct ip6
*rip
= mtod(t
, struct ip6
*);
47 rip
->ip_dst
= ip
->ip_src
;
48 rip
->ip_src
= ip
->ip_dst
;
51 t
->m_data
+= sizeof(struct ip6
);
52 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
53 ricmp
->icmp6_type
= ICMP6_ECHO_REPLY
;
54 ricmp
->icmp6_cksum
= 0;
57 t
->m_data
-= sizeof(struct ip6
);
58 ricmp
->icmp6_cksum
= ip6_cksum(t
);
60 ip6_output(NULL
, t
, 0);
63 void icmp6_send_error(struct mbuf
*m
, uint8_t type
, uint8_t code
)
65 Slirp
*slirp
= m
->slirp
;
67 struct ip6
*ip
= mtod(m
, struct ip6
*);
69 DEBUG_CALL("icmp6_send_error");
70 DEBUG_ARGS((dfd
, " type = %d, code = %d\n", type
, code
));
72 if (IN6_IS_ADDR_MULTICAST(&ip
->ip_src
) ||
73 IN6_IS_ADDR_UNSPECIFIED(&ip
->ip_src
)) {
74 /* TODO icmp error? */
81 struct ip6
*rip
= mtod(t
, struct ip6
*);
82 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
83 rip
->ip_dst
= ip
->ip_src
;
84 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
85 char addrstr
[INET6_ADDRSTRLEN
];
86 inet_ntop(AF_INET6
, &rip
->ip_dst
, addrstr
, INET6_ADDRSTRLEN
);
87 DEBUG_ARG("target = %s", addrstr
);
90 rip
->ip_nh
= IPPROTO_ICMPV6
;
91 const int error_data_len
= min(m
->m_len
,
92 IF_MTU
- (sizeof(struct ip6
) + ICMP6_ERROR_MINLEN
));
93 rip
->ip_pl
= htons(ICMP6_ERROR_MINLEN
+ error_data_len
);
94 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
97 t
->m_data
+= sizeof(struct ip6
);
98 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
99 ricmp
->icmp6_type
= type
;
100 ricmp
->icmp6_code
= code
;
101 ricmp
->icmp6_cksum
= 0;
106 ricmp
->icmp6_err
.unused
= 0;
109 ricmp
->icmp6_err
.mtu
= htonl(IF_MTU
);
111 case ICMP6_PARAMPROB
:
112 /* TODO: Handle this case */
115 g_assert_not_reached();
118 t
->m_data
+= ICMP6_ERROR_MINLEN
;
119 memcpy(t
->m_data
, m
->m_data
, error_data_len
);
122 t
->m_data
-= ICMP6_ERROR_MINLEN
;
123 t
->m_data
-= sizeof(struct ip6
);
124 ricmp
->icmp6_cksum
= ip6_cksum(t
);
126 ip6_output(NULL
, t
, 0);
130 * Send NDP Router Advertisement
132 void ndp_send_ra(Slirp
*slirp
)
134 DEBUG_CALL("ndp_send_ra");
136 /* Build IPv6 packet */
137 struct mbuf
*t
= m_get(slirp
);
138 struct ip6
*rip
= mtod(t
, struct ip6
*);
139 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
140 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
141 rip
->ip_nh
= IPPROTO_ICMPV6
;
142 rip
->ip_pl
= htons(ICMP6_NDP_RA_MINLEN
143 + NDPOPT_LINKLAYER_LEN
144 + NDPOPT_PREFIXINFO_LEN
);
145 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
147 /* Build ICMPv6 packet */
148 t
->m_data
+= sizeof(struct ip6
);
149 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
150 ricmp
->icmp6_type
= ICMP6_NDP_RA
;
151 ricmp
->icmp6_code
= 0;
152 ricmp
->icmp6_cksum
= 0;
155 ricmp
->icmp6_nra
.chl
= NDP_AdvCurHopLimit
;
156 ricmp
->icmp6_nra
.M
= NDP_AdvManagedFlag
;
157 ricmp
->icmp6_nra
.O
= NDP_AdvOtherConfigFlag
;
158 ricmp
->icmp6_nra
.reserved
= 0;
159 ricmp
->icmp6_nra
.lifetime
= htons(NDP_AdvDefaultLifetime
);
160 ricmp
->icmp6_nra
.reach_time
= htonl(NDP_AdvReachableTime
);
161 ricmp
->icmp6_nra
.retrans_time
= htonl(NDP_AdvRetransTime
);
163 /* Source link-layer address (NDP option) */
164 t
->m_data
+= ICMP6_NDP_RA_MINLEN
;
165 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
166 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
167 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
168 in6_compute_ethaddr(rip
->ip_src
, opt
->ndpopt_linklayer
);
170 /* Prefix information (NDP option) */
171 t
->m_data
+= NDPOPT_LINKLAYER_LEN
;
172 struct ndpopt
*opt2
= mtod(t
, struct ndpopt
*);
173 opt2
->ndpopt_type
= NDPOPT_PREFIX_INFO
;
174 opt2
->ndpopt_len
= NDPOPT_PREFIXINFO_LEN
/ 8;
175 opt2
->ndpopt_prefixinfo
.prefix_length
= slirp
->vprefix_len
;
176 opt2
->ndpopt_prefixinfo
.L
= 1;
177 opt2
->ndpopt_prefixinfo
.A
= 1;
178 opt2
->ndpopt_prefixinfo
.reserved1
= 0;
179 opt2
->ndpopt_prefixinfo
.valid_lt
= htonl(NDP_AdvValidLifetime
);
180 opt2
->ndpopt_prefixinfo
.pref_lt
= htonl(NDP_AdvPrefLifetime
);
181 opt2
->ndpopt_prefixinfo
.reserved2
= 0;
182 opt2
->ndpopt_prefixinfo
.prefix
= slirp
->vprefix_addr6
;
184 /* ICMPv6 Checksum */
185 t
->m_data
-= NDPOPT_LINKLAYER_LEN
;
186 t
->m_data
-= ICMP6_NDP_RA_MINLEN
;
187 t
->m_data
-= sizeof(struct ip6
);
188 ricmp
->icmp6_cksum
= ip6_cksum(t
);
190 ip6_output(NULL
, t
, 0);
194 * Send NDP Neighbor Solitication
196 void ndp_send_ns(Slirp
*slirp
, struct in6_addr addr
)
198 DEBUG_CALL("ndp_send_ns");
199 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
200 char addrstr
[INET6_ADDRSTRLEN
];
201 inet_ntop(AF_INET6
, &addr
, addrstr
, INET6_ADDRSTRLEN
);
202 DEBUG_ARG("target = %s", addrstr
);
205 /* Build IPv6 packet */
206 struct mbuf
*t
= m_get(slirp
);
207 struct ip6
*rip
= mtod(t
, struct ip6
*);
208 rip
->ip_src
= slirp
->vhost_addr6
;
209 rip
->ip_dst
= (struct in6_addr
)SOLICITED_NODE_PREFIX
;
210 memcpy(&rip
->ip_dst
.s6_addr
[13], &addr
.s6_addr
[13], 3);
211 rip
->ip_nh
= IPPROTO_ICMPV6
;
212 rip
->ip_pl
= htons(ICMP6_NDP_NS_MINLEN
+ NDPOPT_LINKLAYER_LEN
);
213 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
215 /* Build ICMPv6 packet */
216 t
->m_data
+= sizeof(struct ip6
);
217 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
218 ricmp
->icmp6_type
= ICMP6_NDP_NS
;
219 ricmp
->icmp6_code
= 0;
220 ricmp
->icmp6_cksum
= 0;
223 ricmp
->icmp6_nns
.reserved
= 0;
224 ricmp
->icmp6_nns
.target
= addr
;
226 /* Build NDP option */
227 t
->m_data
+= ICMP6_NDP_NS_MINLEN
;
228 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
229 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
230 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
231 in6_compute_ethaddr(slirp
->vhost_addr6
, opt
->ndpopt_linklayer
);
233 /* ICMPv6 Checksum */
234 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
235 t
->m_data
-= sizeof(struct ip6
);
236 ricmp
->icmp6_cksum
= ip6_cksum(t
);
238 ip6_output(NULL
, t
, 1);
242 * Send NDP Neighbor Advertisement
244 static void ndp_send_na(Slirp
*slirp
, struct ip6
*ip
, struct icmp6
*icmp
)
246 /* Build IPv6 packet */
247 struct mbuf
*t
= m_get(slirp
);
248 struct ip6
*rip
= mtod(t
, struct ip6
*);
249 rip
->ip_src
= icmp
->icmp6_nns
.target
;
250 if (IN6_IS_ADDR_UNSPECIFIED(&ip
->ip_src
)) {
251 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
253 rip
->ip_dst
= ip
->ip_src
;
255 rip
->ip_nh
= IPPROTO_ICMPV6
;
256 rip
->ip_pl
= htons(ICMP6_NDP_NA_MINLEN
257 + NDPOPT_LINKLAYER_LEN
);
258 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
260 /* Build ICMPv6 packet */
261 t
->m_data
+= sizeof(struct ip6
);
262 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
263 ricmp
->icmp6_type
= ICMP6_NDP_NA
;
264 ricmp
->icmp6_code
= 0;
265 ricmp
->icmp6_cksum
= 0;
268 ricmp
->icmp6_nna
.R
= NDP_IsRouter
;
269 ricmp
->icmp6_nna
.S
= !IN6_IS_ADDR_MULTICAST(&rip
->ip_dst
);
270 ricmp
->icmp6_nna
.O
= 1;
271 ricmp
->icmp6_nna
.reserved_hi
= 0;
272 ricmp
->icmp6_nna
.reserved_lo
= 0;
273 ricmp
->icmp6_nna
.target
= icmp
->icmp6_nns
.target
;
275 /* Build NDP option */
276 t
->m_data
+= ICMP6_NDP_NA_MINLEN
;
277 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
278 opt
->ndpopt_type
= NDPOPT_LINKLAYER_TARGET
;
279 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
280 in6_compute_ethaddr(ricmp
->icmp6_nna
.target
,
281 opt
->ndpopt_linklayer
);
283 /* ICMPv6 Checksum */
284 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
285 t
->m_data
-= sizeof(struct ip6
);
286 ricmp
->icmp6_cksum
= ip6_cksum(t
);
288 ip6_output(NULL
, t
, 0);
292 * Process a NDP message
294 static void ndp_input(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
297 m
->m_len
+= ETH_HLEN
;
298 m
->m_data
-= ETH_HLEN
;
299 struct ethhdr
*eth
= mtod(m
, struct ethhdr
*);
300 m
->m_len
-= ETH_HLEN
;
301 m
->m_data
+= ETH_HLEN
;
303 switch (icmp
->icmp6_type
) {
305 DEBUG_CALL(" type = Router Solicitation");
307 && icmp
->icmp6_code
== 0
308 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_RS_MINLEN
) {
310 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
317 DEBUG_CALL(" type = Router Advertisement");
318 qemu_log_mask(LOG_GUEST_ERROR
,
319 "Warning: guest sent NDP RA, but shouldn't");
323 DEBUG_CALL(" type = Neighbor Solicitation");
325 && icmp
->icmp6_code
== 0
326 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nns
.target
)
327 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NS_MINLEN
328 && (!IN6_IS_ADDR_UNSPECIFIED(&ip
->ip_src
)
329 || in6_solicitednode_multicast(&ip
->ip_dst
))) {
330 if (in6_equal_host(&icmp
->icmp6_nns
.target
)) {
332 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
333 ndp_send_na(slirp
, ip
, icmp
);
339 DEBUG_CALL(" type = Neighbor Advertisement");
341 && icmp
->icmp6_code
== 0
342 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NA_MINLEN
343 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nna
.target
)
344 && (!IN6_IS_ADDR_MULTICAST(&ip
->ip_dst
)
345 || icmp
->icmp6_nna
.S
== 0)) {
346 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
350 case ICMP6_NDP_REDIRECT
:
351 DEBUG_CALL(" type = Redirect");
352 qemu_log_mask(LOG_GUEST_ERROR
,
353 "Warning: guest sent NDP REDIRECT, but shouldn't");
359 * Process a received ICMPv6 message.
361 void icmp6_input(struct mbuf
*m
)
364 struct ip6
*ip
= mtod(m
, struct ip6
*);
365 Slirp
*slirp
= m
->slirp
;
366 int hlen
= sizeof(struct ip6
);
368 DEBUG_CALL("icmp6_input");
369 DEBUG_ARG("m = %lx", (long) m
);
370 DEBUG_ARG("m_len = %d", m
->m_len
);
372 if (ntohs(ip
->ip_pl
) < ICMP6_MINLEN
) {
382 icmp
= mtod(m
, struct icmp6
*);
386 DEBUG_ARG("icmp6_type = %d", icmp
->icmp6_type
);
387 switch (icmp
->icmp6_type
) {
388 case ICMP6_ECHO_REQUEST
:
389 if (in6_equal_host(&ip
->ip_dst
)) {
390 icmp6_send_echoreply(m
, slirp
, ip
, icmp
);
393 error_report("external icmpv6 not supported yet");
401 case ICMP6_NDP_REDIRECT
:
402 ndp_input(m
, slirp
, ip
, icmp
);
408 case ICMP6_PARAMPROB
:
409 /* XXX? report error? close socket? */