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);
64 * Send NDP Router Advertisement
66 void ndp_send_ra(Slirp *slirp)
68 DEBUG_CALL("ndp_send_ra");
70 /* Build IPv6 packet */
71 struct mbuf *t = m_get(slirp);
72 struct ip6 *rip = mtod(t, struct ip6 *);
73 rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
74 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
75 rip->ip_nh = IPPROTO_ICMPV6;
76 rip->ip_pl = htons(ICMP6_NDP_RA_MINLEN
77 + NDPOPT_LINKLAYER_LEN
78 + NDPOPT_PREFIXINFO_LEN);
79 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
81 /* Build ICMPv6 packet */
82 t->m_data += sizeof(struct ip6);
83 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
84 ricmp->icmp6_type = ICMP6_NDP_RA;
85 ricmp->icmp6_code = 0;
86 ricmp->icmp6_cksum = 0;
89 ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
90 ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
91 ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
92 ricmp->icmp6_nra.reserved = 0;
93 ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
94 ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
95 ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
97 /* Source link-layer address (NDP option) */
98 t->m_data += ICMP6_NDP_RA_MINLEN;
99 struct ndpopt *opt = mtod(t, struct ndpopt *);
100 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
101 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
102 in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
104 /* Prefix information (NDP option) */
105 t->m_data += NDPOPT_LINKLAYER_LEN;
106 struct ndpopt *opt2 = mtod(t, struct ndpopt *);
107 opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
108 opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
109 opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
110 opt2->ndpopt_prefixinfo.L = 1;
111 opt2->ndpopt_prefixinfo.A = 1;
112 opt2->ndpopt_prefixinfo.reserved1 = 0;
113 opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
114 opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
115 opt2->ndpopt_prefixinfo.reserved2 = 0;
116 opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
118 /* ICMPv6 Checksum */
119 t->m_data -= NDPOPT_LINKLAYER_LEN;
120 t->m_data -= ICMP6_NDP_RA_MINLEN;
121 t->m_data -= sizeof(struct ip6);
122 ricmp->icmp6_cksum = ip6_cksum(t);
124 ip6_output(NULL, t, 0);
128 * Send NDP Neighbor Solitication
130 void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
132 DEBUG_CALL("ndp_send_ns");
133 #if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
134 char addrstr[INET6_ADDRSTRLEN];
135 inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
136 DEBUG_ARG("target = %s", addrstr);
139 /* Build IPv6 packet */
140 struct mbuf *t = m_get(slirp);
141 struct ip6 *rip = mtod(t, struct ip6 *);
142 rip->ip_src = slirp->vhost_addr6;
143 rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
144 memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
145 rip->ip_nh = IPPROTO_ICMPV6;
146 rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
147 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
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_NS;
153 ricmp->icmp6_code = 0;
154 ricmp->icmp6_cksum = 0;
157 ricmp->icmp6_nns.reserved = 0;
158 ricmp->icmp6_nns.target = addr;
160 /* Build NDP option */
161 t->m_data += ICMP6_NDP_NS_MINLEN;
162 struct ndpopt *opt = mtod(t, struct ndpopt *);
163 opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
164 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
165 in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
167 /* ICMPv6 Checksum */
168 t->m_data -= ICMP6_NDP_NA_MINLEN;
169 t->m_data -= sizeof(struct ip6);
170 ricmp->icmp6_cksum = ip6_cksum(t);
172 ip6_output(NULL, t, 1);
176 * Send NDP Neighbor Advertisement
178 static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
180 /* Build IPv6 packet */
181 struct mbuf *t = m_get(slirp);
182 struct ip6 *rip = mtod(t, struct ip6 *);
183 rip->ip_src = icmp->icmp6_nns.target;
184 if (IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
185 rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
187 rip->ip_dst = ip->ip_src;
189 rip->ip_nh = IPPROTO_ICMPV6;
190 rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
191 + NDPOPT_LINKLAYER_LEN);
192 t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
194 /* Build ICMPv6 packet */
195 t->m_data += sizeof(struct ip6);
196 struct icmp6 *ricmp = mtod(t, struct icmp6 *);
197 ricmp->icmp6_type = ICMP6_NDP_NA;
198 ricmp->icmp6_code = 0;
199 ricmp->icmp6_cksum = 0;
202 ricmp->icmp6_nna.R = NDP_IsRouter;
203 ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
204 ricmp->icmp6_nna.O = 1;
205 ricmp->icmp6_nna.reserved_hi = 0;
206 ricmp->icmp6_nna.reserved_lo = 0;
207 ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
209 /* Build NDP option */
210 t->m_data += ICMP6_NDP_NA_MINLEN;
211 struct ndpopt *opt = mtod(t, struct ndpopt *);
212 opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
213 opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
214 in6_compute_ethaddr(ricmp->icmp6_nna.target,
215 opt->ndpopt_linklayer);
217 /* ICMPv6 Checksum */
218 t->m_data -= ICMP6_NDP_NA_MINLEN;
219 t->m_data -= sizeof(struct ip6);
220 ricmp->icmp6_cksum = ip6_cksum(t);
222 ip6_output(NULL, t, 0);
226 * Process a NDP message
228 static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
231 m->m_len += ETH_HLEN;
232 m->m_data -= ETH_HLEN;
233 struct ethhdr *eth = mtod(m, struct ethhdr *);
234 m->m_len -= ETH_HLEN;
235 m->m_data += ETH_HLEN;
237 switch (icmp->icmp6_type) {
239 DEBUG_CALL(" type = Router Solicitation");
241 && icmp->icmp6_code == 0
242 && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
244 ndp_table_add(slirp, ip->ip_src, eth->h_source);
251 DEBUG_CALL(" type = Router Advertisement");
252 qemu_log_mask(LOG_GUEST_ERROR,
253 "Warning: guest sent NDP RA, but shouldn't");
257 DEBUG_CALL(" type = Neighbor Solicitation");
259 && icmp->icmp6_code == 0
260 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
261 && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
262 && (!IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)
263 || in6_solicitednode_multicast(&ip->ip_dst))) {
264 if (in6_equal_host(&icmp->icmp6_nns.target)) {
266 ndp_table_add(slirp, ip->ip_src, eth->h_source);
267 ndp_send_na(slirp, ip, icmp);
273 DEBUG_CALL(" type = Neighbor Advertisement");
275 && icmp->icmp6_code == 0
276 && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
277 && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
278 && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
279 || icmp->icmp6_nna.S == 0)) {
280 ndp_table_add(slirp, ip->ip_src, eth->h_source);
284 case ICMP6_NDP_REDIRECT:
285 DEBUG_CALL(" type = Redirect");
286 qemu_log_mask(LOG_GUEST_ERROR,
287 "Warning: guest sent NDP REDIRECT, but shouldn't");
293 * Process a received ICMPv6 message.
295 void icmp6_input(struct mbuf *m)
298 struct ip6 *ip = mtod(m, struct ip6 *);
299 Slirp *slirp = m->slirp;
300 int hlen = sizeof(struct ip6);
302 DEBUG_CALL("icmp6_input");
303 DEBUG_ARG("m = %lx", (long) m);
304 DEBUG_ARG("m_len = %d", m->m_len);
306 if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
316 icmp = mtod(m, struct icmp6 *);
320 DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
321 switch (icmp->icmp6_type) {
322 case ICMP6_ECHO_REQUEST:
323 if (in6_equal_host(&ip->ip_dst)) {
324 icmp6_send_echoreply(m, slirp, ip, icmp);
327 error_report("external icmpv6 not supported yet");
335 case ICMP6_NDP_REDIRECT:
336 ndp_input(m, slirp, ip, icmp);
342 case ICMP6_PARAMPROB:
343 /* XXX? report error? close socket? */