3 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
9 #define NDP_Interval g_rand_int_range(slirp->grand, \
10 NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
12 static void ra_timer_handler(void *opaque
)
14 Slirp
*slirp
= opaque
;
16 slirp
->cb
->timer_mod(slirp
->ra_timer
,
17 slirp
->cb
->clock_get_ns() / SCALE_MS
+ NDP_Interval
);
21 void icmp6_init(Slirp
*slirp
)
23 if (!slirp
->in6_enabled
) {
27 slirp
->ra_timer
= slirp
->cb
->timer_new(ra_timer_handler
, slirp
);
28 slirp
->cb
->timer_mod(slirp
->ra_timer
,
29 slirp
->cb
->clock_get_ns() / SCALE_MS
+ NDP_Interval
);
32 void icmp6_cleanup(Slirp
*slirp
)
34 if (!slirp
->in6_enabled
) {
38 slirp
->cb
->timer_free(slirp
->ra_timer
);
41 static void icmp6_send_echoreply(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
44 struct mbuf
*t
= m_get(slirp
);
45 t
->m_len
= sizeof(struct ip6
) + ntohs(ip
->ip_pl
);
46 memcpy(t
->m_data
, m
->m_data
, t
->m_len
);
49 struct ip6
*rip
= mtod(t
, struct ip6
*);
50 rip
->ip_dst
= ip
->ip_src
;
51 rip
->ip_src
= ip
->ip_dst
;
54 t
->m_data
+= sizeof(struct ip6
);
55 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
56 ricmp
->icmp6_type
= ICMP6_ECHO_REPLY
;
57 ricmp
->icmp6_cksum
= 0;
60 t
->m_data
-= sizeof(struct ip6
);
61 ricmp
->icmp6_cksum
= ip6_cksum(t
);
63 ip6_output(NULL
, t
, 0);
66 void icmp6_send_error(struct mbuf
*m
, uint8_t type
, uint8_t code
)
68 Slirp
*slirp
= m
->slirp
;
70 struct ip6
*ip
= mtod(m
, struct ip6
*);
71 char addrstr
[INET6_ADDRSTRLEN
];
73 DEBUG_CALL("icmp6_send_error");
74 DEBUG_ARG("type = %d, code = %d", type
, code
);
76 if (IN6_IS_ADDR_MULTICAST(&ip
->ip_src
) ||
77 in6_zero(&ip
->ip_src
)) {
78 /* TODO icmp error? */
85 struct ip6
*rip
= mtod(t
, struct ip6
*);
86 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
87 rip
->ip_dst
= ip
->ip_src
;
88 inet_ntop(AF_INET6
, &rip
->ip_dst
, addrstr
, INET6_ADDRSTRLEN
);
89 DEBUG_ARG("target = %s", addrstr
);
91 rip
->ip_nh
= IPPROTO_ICMPV6
;
92 const int error_data_len
= MIN(m
->m_len
,
93 IF_MTU
- (sizeof(struct ip6
) + ICMP6_ERROR_MINLEN
));
94 rip
->ip_pl
= htons(ICMP6_ERROR_MINLEN
+ error_data_len
);
95 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
98 t
->m_data
+= sizeof(struct ip6
);
99 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
100 ricmp
->icmp6_type
= type
;
101 ricmp
->icmp6_code
= code
;
102 ricmp
->icmp6_cksum
= 0;
107 ricmp
->icmp6_err
.unused
= 0;
110 ricmp
->icmp6_err
.mtu
= htonl(IF_MTU
);
112 case ICMP6_PARAMPROB
:
113 /* TODO: Handle this case */
116 g_assert_not_reached();
119 t
->m_data
+= ICMP6_ERROR_MINLEN
;
120 memcpy(t
->m_data
, m
->m_data
, error_data_len
);
123 t
->m_data
-= ICMP6_ERROR_MINLEN
;
124 t
->m_data
-= sizeof(struct ip6
);
125 ricmp
->icmp6_cksum
= ip6_cksum(t
);
127 ip6_output(NULL
, t
, 0);
131 * Send NDP Router Advertisement
133 void ndp_send_ra(Slirp
*slirp
)
135 DEBUG_CALL("ndp_send_ra");
137 /* Build IPv6 packet */
138 struct mbuf
*t
= m_get(slirp
);
139 struct ip6
*rip
= mtod(t
, struct ip6
*);
141 struct in6_addr addr
;
144 rip
->ip_src
= (struct in6_addr
)LINKLOCAL_ADDR
;
145 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
146 rip
->ip_nh
= IPPROTO_ICMPV6
;
148 /* Build ICMPv6 packet */
149 t
->m_data
+= sizeof(struct ip6
);
150 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
151 ricmp
->icmp6_type
= ICMP6_NDP_RA
;
152 ricmp
->icmp6_code
= 0;
153 ricmp
->icmp6_cksum
= 0;
156 ricmp
->icmp6_nra
.chl
= NDP_AdvCurHopLimit
;
157 ricmp
->icmp6_nra
.M
= NDP_AdvManagedFlag
;
158 ricmp
->icmp6_nra
.O
= NDP_AdvOtherConfigFlag
;
159 ricmp
->icmp6_nra
.reserved
= 0;
160 ricmp
->icmp6_nra
.lifetime
= htons(NDP_AdvDefaultLifetime
);
161 ricmp
->icmp6_nra
.reach_time
= htonl(NDP_AdvReachableTime
);
162 ricmp
->icmp6_nra
.retrans_time
= htonl(NDP_AdvRetransTime
);
163 t
->m_data
+= ICMP6_NDP_RA_MINLEN
;
164 pl_size
+= ICMP6_NDP_RA_MINLEN
;
166 /* Source link-layer address (NDP option) */
167 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
168 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
169 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
170 in6_compute_ethaddr(rip
->ip_src
, opt
->ndpopt_linklayer
);
171 t
->m_data
+= NDPOPT_LINKLAYER_LEN
;
172 pl_size
+= NDPOPT_LINKLAYER_LEN
;
174 /* Prefix information (NDP option) */
175 struct ndpopt
*opt2
= mtod(t
, struct ndpopt
*);
176 opt2
->ndpopt_type
= NDPOPT_PREFIX_INFO
;
177 opt2
->ndpopt_len
= NDPOPT_PREFIXINFO_LEN
/ 8;
178 opt2
->ndpopt_prefixinfo
.prefix_length
= slirp
->vprefix_len
;
179 opt2
->ndpopt_prefixinfo
.L
= 1;
180 opt2
->ndpopt_prefixinfo
.A
= 1;
181 opt2
->ndpopt_prefixinfo
.reserved1
= 0;
182 opt2
->ndpopt_prefixinfo
.valid_lt
= htonl(NDP_AdvValidLifetime
);
183 opt2
->ndpopt_prefixinfo
.pref_lt
= htonl(NDP_AdvPrefLifetime
);
184 opt2
->ndpopt_prefixinfo
.reserved2
= 0;
185 opt2
->ndpopt_prefixinfo
.prefix
= slirp
->vprefix_addr6
;
186 t
->m_data
+= NDPOPT_PREFIXINFO_LEN
;
187 pl_size
+= NDPOPT_PREFIXINFO_LEN
;
189 /* Prefix information (NDP option) */
190 if (get_dns6_addr(&addr
, &scope_id
) >= 0) {
191 /* Host system does have an IPv6 DNS server, announce our proxy. */
192 struct ndpopt
*opt3
= mtod(t
, struct ndpopt
*);
193 opt3
->ndpopt_type
= NDPOPT_RDNSS
;
194 opt3
->ndpopt_len
= NDPOPT_RDNSS_LEN
/ 8;
195 opt3
->ndpopt_rdnss
.reserved
= 0;
196 opt3
->ndpopt_rdnss
.lifetime
= htonl(2 * NDP_MaxRtrAdvInterval
);
197 opt3
->ndpopt_rdnss
.addr
= slirp
->vnameserver_addr6
;
198 t
->m_data
+= NDPOPT_RDNSS_LEN
;
199 pl_size
+= NDPOPT_RDNSS_LEN
;
202 rip
->ip_pl
= htons(pl_size
);
203 t
->m_data
-= sizeof(struct ip6
) + pl_size
;
204 t
->m_len
= sizeof(struct ip6
) + pl_size
;
206 /* ICMPv6 Checksum */
207 ricmp
->icmp6_cksum
= ip6_cksum(t
);
209 ip6_output(NULL
, t
, 0);
213 * Send NDP Neighbor Solitication
215 void ndp_send_ns(Slirp
*slirp
, struct in6_addr addr
)
217 char addrstr
[INET6_ADDRSTRLEN
];
219 inet_ntop(AF_INET6
, &addr
, addrstr
, INET6_ADDRSTRLEN
);
221 DEBUG_CALL("ndp_send_ns");
222 DEBUG_ARG("target = %s", addrstr
);
224 /* Build IPv6 packet */
225 struct mbuf
*t
= m_get(slirp
);
226 struct ip6
*rip
= mtod(t
, struct ip6
*);
227 rip
->ip_src
= slirp
->vhost_addr6
;
228 rip
->ip_dst
= (struct in6_addr
)SOLICITED_NODE_PREFIX
;
229 memcpy(&rip
->ip_dst
.s6_addr
[13], &addr
.s6_addr
[13], 3);
230 rip
->ip_nh
= IPPROTO_ICMPV6
;
231 rip
->ip_pl
= htons(ICMP6_NDP_NS_MINLEN
+ NDPOPT_LINKLAYER_LEN
);
232 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
234 /* Build ICMPv6 packet */
235 t
->m_data
+= sizeof(struct ip6
);
236 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
237 ricmp
->icmp6_type
= ICMP6_NDP_NS
;
238 ricmp
->icmp6_code
= 0;
239 ricmp
->icmp6_cksum
= 0;
242 ricmp
->icmp6_nns
.reserved
= 0;
243 ricmp
->icmp6_nns
.target
= addr
;
245 /* Build NDP option */
246 t
->m_data
+= ICMP6_NDP_NS_MINLEN
;
247 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
248 opt
->ndpopt_type
= NDPOPT_LINKLAYER_SOURCE
;
249 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
250 in6_compute_ethaddr(slirp
->vhost_addr6
, opt
->ndpopt_linklayer
);
252 /* ICMPv6 Checksum */
253 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
254 t
->m_data
-= sizeof(struct ip6
);
255 ricmp
->icmp6_cksum
= ip6_cksum(t
);
257 ip6_output(NULL
, t
, 1);
261 * Send NDP Neighbor Advertisement
263 static void ndp_send_na(Slirp
*slirp
, struct ip6
*ip
, struct icmp6
*icmp
)
265 /* Build IPv6 packet */
266 struct mbuf
*t
= m_get(slirp
);
267 struct ip6
*rip
= mtod(t
, struct ip6
*);
268 rip
->ip_src
= icmp
->icmp6_nns
.target
;
269 if (in6_zero(&ip
->ip_src
)) {
270 rip
->ip_dst
= (struct in6_addr
)ALLNODES_MULTICAST
;
272 rip
->ip_dst
= ip
->ip_src
;
274 rip
->ip_nh
= IPPROTO_ICMPV6
;
275 rip
->ip_pl
= htons(ICMP6_NDP_NA_MINLEN
276 + NDPOPT_LINKLAYER_LEN
);
277 t
->m_len
= sizeof(struct ip6
) + ntohs(rip
->ip_pl
);
279 /* Build ICMPv6 packet */
280 t
->m_data
+= sizeof(struct ip6
);
281 struct icmp6
*ricmp
= mtod(t
, struct icmp6
*);
282 ricmp
->icmp6_type
= ICMP6_NDP_NA
;
283 ricmp
->icmp6_code
= 0;
284 ricmp
->icmp6_cksum
= 0;
287 ricmp
->icmp6_nna
.R
= NDP_IsRouter
;
288 ricmp
->icmp6_nna
.S
= !IN6_IS_ADDR_MULTICAST(&rip
->ip_dst
);
289 ricmp
->icmp6_nna
.O
= 1;
290 ricmp
->icmp6_nna
.reserved_hi
= 0;
291 ricmp
->icmp6_nna
.reserved_lo
= 0;
292 ricmp
->icmp6_nna
.target
= icmp
->icmp6_nns
.target
;
294 /* Build NDP option */
295 t
->m_data
+= ICMP6_NDP_NA_MINLEN
;
296 struct ndpopt
*opt
= mtod(t
, struct ndpopt
*);
297 opt
->ndpopt_type
= NDPOPT_LINKLAYER_TARGET
;
298 opt
->ndpopt_len
= NDPOPT_LINKLAYER_LEN
/ 8;
299 in6_compute_ethaddr(ricmp
->icmp6_nna
.target
,
300 opt
->ndpopt_linklayer
);
302 /* ICMPv6 Checksum */
303 t
->m_data
-= ICMP6_NDP_NA_MINLEN
;
304 t
->m_data
-= sizeof(struct ip6
);
305 ricmp
->icmp6_cksum
= ip6_cksum(t
);
307 ip6_output(NULL
, t
, 0);
311 * Process a NDP message
313 static void ndp_input(struct mbuf
*m
, Slirp
*slirp
, struct ip6
*ip
,
316 m
->m_len
+= ETH_HLEN
;
317 m
->m_data
-= ETH_HLEN
;
318 struct ethhdr
*eth
= mtod(m
, struct ethhdr
*);
319 m
->m_len
-= ETH_HLEN
;
320 m
->m_data
+= ETH_HLEN
;
322 switch (icmp
->icmp6_type
) {
324 DEBUG_CALL(" type = Router Solicitation");
326 && icmp
->icmp6_code
== 0
327 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_RS_MINLEN
) {
329 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
336 DEBUG_CALL(" type = Router Advertisement");
337 slirp
->cb
->guest_error("Warning: guest sent NDP RA, but shouldn't");
341 DEBUG_CALL(" type = Neighbor Solicitation");
343 && icmp
->icmp6_code
== 0
344 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nns
.target
)
345 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NS_MINLEN
346 && (!in6_zero(&ip
->ip_src
)
347 || in6_solicitednode_multicast(&ip
->ip_dst
))) {
348 if (in6_equal_host(&icmp
->icmp6_nns
.target
)) {
350 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
351 ndp_send_na(slirp
, ip
, icmp
);
357 DEBUG_CALL(" type = Neighbor Advertisement");
359 && icmp
->icmp6_code
== 0
360 && ntohs(ip
->ip_pl
) >= ICMP6_NDP_NA_MINLEN
361 && !IN6_IS_ADDR_MULTICAST(&icmp
->icmp6_nna
.target
)
362 && (!IN6_IS_ADDR_MULTICAST(&ip
->ip_dst
)
363 || icmp
->icmp6_nna
.S
== 0)) {
364 ndp_table_add(slirp
, ip
->ip_src
, eth
->h_source
);
368 case ICMP6_NDP_REDIRECT
:
369 DEBUG_CALL(" type = Redirect");
370 slirp
->cb
->guest_error(
371 "Warning: guest sent NDP REDIRECT, but shouldn't");
377 * Process a received ICMPv6 message.
379 void icmp6_input(struct mbuf
*m
)
382 struct ip6
*ip
= mtod(m
, struct ip6
*);
383 Slirp
*slirp
= m
->slirp
;
384 int hlen
= sizeof(struct ip6
);
386 DEBUG_CALL("icmp6_input");
387 DEBUG_ARG("m = %p", m
);
388 DEBUG_ARG("m_len = %d", m
->m_len
);
390 if (ntohs(ip
->ip_pl
) < ICMP6_MINLEN
) {
400 icmp
= mtod(m
, struct icmp6
*);
404 DEBUG_ARG("icmp6_type = %d", icmp
->icmp6_type
);
405 switch (icmp
->icmp6_type
) {
406 case ICMP6_ECHO_REQUEST
:
407 if (in6_equal_host(&ip
->ip_dst
)) {
408 icmp6_send_echoreply(m
, slirp
, ip
, icmp
);
411 g_critical("external icmpv6 not supported yet");
419 case ICMP6_NDP_REDIRECT
:
420 ndp_input(m
, slirp
, ip
, icmp
);
426 case ICMP6_PARAMPROB
:
427 /* XXX? report error? close socket? */