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 if (!slirp
->in6_enabled
) {
31 slirp
->ra_timer
= timer_new_ms(QEMU_CLOCK_VIRTUAL
, ra_timer_handler
, slirp
);
32 timer_mod(slirp
->ra_timer
,
33 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) + NDP_Interval
);
36 void icmp6_cleanup(Slirp
*slirp
)
38 if (!slirp
->in6_enabled
) {
42 timer_del(slirp
->ra_timer
);
43 timer_free(slirp
->ra_timer
);
46 static void icmp6_send_echoreply(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
49 struct mbuf
*t
= m_get(slirp
);
50 t
->m_len
= sizeof(struct ip6
) + ntohs(ip
->ip_pl
);
51 memcpy(t
->m_data
, m
->m_data
, t
->m_len
);
54 struct ip6
*rip
= mtod(t
, struct ip6
*);
55 rip
->ip_dst
= ip
->ip_src
;
56 rip
->ip_src
= ip
->ip_dst
;
59 t
->m_data
+= sizeof(struct ip6
);
60 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
61 ricmp
->icmp6_type
= ICMP6_ECHO_REPLY
;
62 ricmp
->icmp6_cksum
= 0;
65 t
->m_data
-= sizeof(struct ip6
);
66 ricmp
->icmp6_cksum
= ip6_cksum(t
);
68 ip6_output(NULL
, t
, 0);
71 void icmp6_send_error(struct mbuf
*m
, uint8_t type
, uint8_t code
)
73 Slirp
*slirp
= m
->slirp
;
75 struct ip6
*ip
= mtod(m
, struct ip6
*);
77 DEBUG_CALL("icmp6_send_error");
78 DEBUG_ARGS((dfd
, " type = %d, code = %d\n", type
, code
));
80 if (IN6_IS_ADDR_MULTICAST(&ip
->ip_src
) ||
81 IN6_IS_ADDR_UNSPECIFIED(&ip
->ip_src
)) {
82 /* TODO icmp error? */
89 struct ip6
*rip
= mtod(t
, struct ip6
*);
90 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
91 rip
->ip_dst
= ip
->ip_src
;
92 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
93 char addrstr
[INET6_ADDRSTRLEN
];
94 inet_ntop(AF_INET6
, &rip
->ip_dst
, addrstr
, INET6_ADDRSTRLEN
);
95 DEBUG_ARG("target = %s", addrstr
);
98 rip
->ip_nh
= IPPROTO_ICMPV6
;
99 const int error_data_len
= min(m
->m_len
,
100 IF_MTU
- (sizeof(struct ip6
) + ICMP6_ERROR_MINLEN
));
101 rip
->ip_pl
= htons(ICMP6_ERROR_MINLEN
+ error_data_len
);
102 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
105 t
->m_data
+= sizeof(struct ip6
);
106 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
107 ricmp
->icmp6_type
= type
;
108 ricmp
->icmp6_code
= code
;
109 ricmp
->icmp6_cksum
= 0;
114 ricmp
->icmp6_err
.unused
= 0;
117 ricmp
->icmp6_err
.mtu
= htonl(IF_MTU
);
119 case ICMP6_PARAMPROB
:
120 /* TODO: Handle this case */
123 g_assert_not_reached();
126 t
->m_data
+= ICMP6_ERROR_MINLEN
;
127 memcpy(t
->m_data
, m
->m_data
, error_data_len
);
130 t
->m_data
-= ICMP6_ERROR_MINLEN
;
131 t
->m_data
-= sizeof(struct ip6
);
132 ricmp
->icmp6_cksum
= ip6_cksum(t
);
134 ip6_output(NULL
, t
, 0);
138 * Send NDP Router Advertisement
140 void ndp_send_ra(Slirp
*slirp
)
142 DEBUG_CALL("ndp_send_ra");
144 /* Build IPv6 packet */
145 struct mbuf
*t
= m_get(slirp
);
146 struct ip6
*rip
= mtod(t
, struct ip6
*);
147 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
148 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
149 rip
->ip_nh
= IPPROTO_ICMPV6
;
150 rip
->ip_pl
= htons(ICMP6_NDP_RA_MINLEN
151 + NDPOPT_LINKLAYER_LEN
152 + NDPOPT_PREFIXINFO_LEN
);
153 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
155 /* Build ICMPv6 packet */
156 t
->m_data
+= sizeof(struct ip6
);
157 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
158 ricmp
->icmp6_type
= ICMP6_NDP_RA
;
159 ricmp
->icmp6_code
= 0;
160 ricmp
->icmp6_cksum
= 0;
163 ricmp
->icmp6_nra
.chl
= NDP_AdvCurHopLimit
;
164 ricmp
->icmp6_nra
.M
= NDP_AdvManagedFlag
;
165 ricmp
->icmp6_nra
.O
= NDP_AdvOtherConfigFlag
;
166 ricmp
->icmp6_nra
.reserved
= 0;
167 ricmp
->icmp6_nra
.lifetime
= htons(NDP_AdvDefaultLifetime
);
168 ricmp
->icmp6_nra
.reach_time
= htonl(NDP_AdvReachableTime
);
169 ricmp
->icmp6_nra
.retrans_time
= htonl(NDP_AdvRetransTime
);
171 /* Source link-layer address (NDP option) */
172 t
->m_data
+= ICMP6_NDP_RA_MINLEN
;
173 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
174 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
175 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
176 in6_compute_ethaddr(rip
->ip_src
, opt
->ndpopt_linklayer
);
178 /* Prefix information (NDP option) */
179 t
->m_data
+= NDPOPT_LINKLAYER_LEN
;
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
;
192 /* ICMPv6 Checksum */
193 t
->m_data
-= NDPOPT_LINKLAYER_LEN
;
194 t
->m_data
-= ICMP6_NDP_RA_MINLEN
;
195 t
->m_data
-= sizeof(struct ip6
);
196 ricmp
->icmp6_cksum
= ip6_cksum(t
);
198 ip6_output(NULL
, t
, 0);
202 * Send NDP Neighbor Solitication
204 void ndp_send_ns(Slirp
*slirp
, struct in6_addr addr
)
206 DEBUG_CALL("ndp_send_ns");
207 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
208 char addrstr
[INET6_ADDRSTRLEN
];
209 inet_ntop(AF_INET6
, &addr
, addrstr
, INET6_ADDRSTRLEN
);
210 DEBUG_ARG("target = %s", addrstr
);
213 /* Build IPv6 packet */
214 struct mbuf
*t
= m_get(slirp
);
215 struct ip6
*rip
= mtod(t
, struct ip6
*);
216 rip
->ip_src
= slirp
->vhost_addr6
;
217 rip
->ip_dst
= (struct in6_addr
)SOLICITED_NODE_PREFIX
;
218 memcpy(&rip
->ip_dst
.s6_addr
[13], &addr
.s6_addr
[13], 3);
219 rip
->ip_nh
= IPPROTO_ICMPV6
;
220 rip
->ip_pl
= htons(ICMP6_NDP_NS_MINLEN
+ NDPOPT_LINKLAYER_LEN
);
221 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
223 /* Build ICMPv6 packet */
224 t
->m_data
+= sizeof(struct ip6
);
225 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
226 ricmp
->icmp6_type
= ICMP6_NDP_NS
;
227 ricmp
->icmp6_code
= 0;
228 ricmp
->icmp6_cksum
= 0;
231 ricmp
->icmp6_nns
.reserved
= 0;
232 ricmp
->icmp6_nns
.target
= addr
;
234 /* Build NDP option */
235 t
->m_data
+= ICMP6_NDP_NS_MINLEN
;
236 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
237 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
238 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
239 in6_compute_ethaddr(slirp
->vhost_addr6
, opt
->ndpopt_linklayer
);
241 /* ICMPv6 Checksum */
242 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
243 t
->m_data
-= sizeof(struct ip6
);
244 ricmp
->icmp6_cksum
= ip6_cksum(t
);
246 ip6_output(NULL
, t
, 1);
250 * Send NDP Neighbor Advertisement
252 static void ndp_send_na(Slirp
*slirp
, struct ip6
*ip
, struct icmp6
*icmp
)
254 /* Build IPv6 packet */
255 struct mbuf
*t
= m_get(slirp
);
256 struct ip6
*rip
= mtod(t
, struct ip6
*);
257 rip
->ip_src
= icmp
->icmp6_nns
.target
;
258 if (IN6_IS_ADDR_UNSPECIFIED(&ip
->ip_src
)) {
259 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
261 rip
->ip_dst
= ip
->ip_src
;
263 rip
->ip_nh
= IPPROTO_ICMPV6
;
264 rip
->ip_pl
= htons(ICMP6_NDP_NA_MINLEN
265 + NDPOPT_LINKLAYER_LEN
);
266 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
268 /* Build ICMPv6 packet */
269 t
->m_data
+= sizeof(struct ip6
);
270 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
271 ricmp
->icmp6_type
= ICMP6_NDP_NA
;
272 ricmp
->icmp6_code
= 0;
273 ricmp
->icmp6_cksum
= 0;
276 ricmp
->icmp6_nna
.R
= NDP_IsRouter
;
277 ricmp
->icmp6_nna
.S
= !IN6_IS_ADDR_MULTICAST(&rip
->ip_dst
);
278 ricmp
->icmp6_nna
.O
= 1;
279 ricmp
->icmp6_nna
.reserved_hi
= 0;
280 ricmp
->icmp6_nna
.reserved_lo
= 0;
281 ricmp
->icmp6_nna
.target
= icmp
->icmp6_nns
.target
;
283 /* Build NDP option */
284 t
->m_data
+= ICMP6_NDP_NA_MINLEN
;
285 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
286 opt
->ndpopt_type
= NDPOPT_LINKLAYER_TARGET
;
287 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
288 in6_compute_ethaddr(ricmp
->icmp6_nna
.target
,
289 opt
->ndpopt_linklayer
);
291 /* ICMPv6 Checksum */
292 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
293 t
->m_data
-= sizeof(struct ip6
);
294 ricmp
->icmp6_cksum
= ip6_cksum(t
);
296 ip6_output(NULL
, t
, 0);
300 * Process a NDP message
302 static void ndp_input(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
305 m
->m_len
+= ETH_HLEN
;
306 m
->m_data
-= ETH_HLEN
;
307 struct ethhdr
*eth
= mtod(m
, struct ethhdr
*);
308 m
->m_len
-= ETH_HLEN
;
309 m
->m_data
+= ETH_HLEN
;
311 switch (icmp
->icmp6_type
) {
313 DEBUG_CALL(" type = Router Solicitation");
315 && icmp
->icmp6_code
== 0
316 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_RS_MINLEN
) {
318 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
325 DEBUG_CALL(" type = Router Advertisement");
326 qemu_log_mask(LOG_GUEST_ERROR
,
327 "Warning: guest sent NDP RA, but shouldn't");
331 DEBUG_CALL(" type = Neighbor Solicitation");
333 && icmp
->icmp6_code
== 0
334 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nns
.target
)
335 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NS_MINLEN
336 && (!IN6_IS_ADDR_UNSPECIFIED(&ip
->ip_src
)
337 || in6_solicitednode_multicast(&ip
->ip_dst
))) {
338 if (in6_equal_host(&icmp
->icmp6_nns
.target
)) {
340 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
341 ndp_send_na(slirp
, ip
, icmp
);
347 DEBUG_CALL(" type = Neighbor Advertisement");
349 && icmp
->icmp6_code
== 0
350 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NA_MINLEN
351 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nna
.target
)
352 && (!IN6_IS_ADDR_MULTICAST(&ip
->ip_dst
)
353 || icmp
->icmp6_nna
.S
== 0)) {
354 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
358 case ICMP6_NDP_REDIRECT
:
359 DEBUG_CALL(" type = Redirect");
360 qemu_log_mask(LOG_GUEST_ERROR
,
361 "Warning: guest sent NDP REDIRECT, but shouldn't");
367 * Process a received ICMPv6 message.
369 void icmp6_input(struct mbuf
*m
)
372 struct ip6
*ip
= mtod(m
, struct ip6
*);
373 Slirp
*slirp
= m
->slirp
;
374 int hlen
= sizeof(struct ip6
);
376 DEBUG_CALL("icmp6_input");
377 DEBUG_ARG("m = %lx", (long) m
);
378 DEBUG_ARG("m_len = %d", m
->m_len
);
380 if (ntohs(ip
->ip_pl
) < ICMP6_MINLEN
) {
390 icmp
= mtod(m
, struct icmp6
*);
394 DEBUG_ARG("icmp6_type = %d", icmp
->icmp6_type
);
395 switch (icmp
->icmp6_type
) {
396 case ICMP6_ECHO_REQUEST
:
397 if (in6_equal_host(&ip
->ip_dst
)) {
398 icmp6_send_echoreply(m
, slirp
, ip
, icmp
);
401 error_report("external icmpv6 not supported yet");
409 case ICMP6_NDP_REDIRECT
:
410 ndp_input(m
, slirp
, ip
, icmp
);
416 case ICMP6_PARAMPROB
:
417 /* XXX? report error? close socket? */