Changeset 53350 in vbox
- Timestamp:
- Nov 19, 2014 1:05:41 PM (10 years ago)
- svn:sync-xref-src-repo-rev:
- 96956
- Location:
- trunk/src/VBox/Devices/Network/slirp
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Network/slirp/ip_icmp.h
r53298 r53350 207 207 int icmpwin_init (PNATState); 208 208 void icmpwin_finit (PNATState); 209 void icmpwin_ping(PNATState, struct mbuf *, int); 209 210 #endif 210 211 -
trunk/src/VBox/Devices/Network/slirp/ip_icmpwin.c
r53310 r53350 19 19 #include "ip_icmp.h" 20 20 21 #include <winternl.h> /* for PIO_APC_ROUTINE &c */ 21 22 #include <iphlpapi.h> 22 23 #include <icmpapi.h> 23 24 25 /* 26 * A header of ICMP ECHO. Intended for storage, unlike struct icmp 27 * which is intended to be overlayed onto a buffer. 28 */ 29 struct icmp_echo { 30 uint8_t icmp_type; 31 uint8_t icmp_code; 32 uint16_t icmp_cksum; 33 uint16_t icmp_echo_id; 34 uint16_t icmp_echo_seq; 35 }; 36 37 AssertCompileSize(struct icmp_echo, 8); 38 39 40 struct pong { 41 PNATState pData; 42 43 struct ip reqiph; 44 struct icmp_echo reqicmph; 45 46 size_t bufsize; 47 uint8_t buf[1]; 48 }; 49 50 51 static VOID WINAPI icmpwin_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved); 52 static VOID WINAPI icmpwin_callback_old(void *ctx); 53 54 static void icmpwin_callback(struct pong *pong); 55 56 static struct mbuf *icmpwin_get_error(struct pong *pong, int type, int code); 57 static struct mbuf *icmpwin_get_mbuf(PNATState pData, size_t reqsize); 58 59 60 /* 61 * On Windows XP and Windows Server 2003 IcmpSendEcho2() callback 62 * is FARPROC, but starting from Vista it's PIO_APC_ROUTINE with 63 * two extra arguments. Callbacks use WINAPI (stdcall) calling 64 * convention with callee responsible for popping the arguments, 65 * so to avoid stack corruption we check windows version at run 66 * time and provide correct callback. 67 * 68 * XXX: this is system-wide, but what about multiple NAT threads? 69 */ 70 static void *pfIcmpCallback; 71 24 72 25 73 int 26 74 icmpwin_init(PNATState pData) 27 75 { 76 if (pfIcmpCallback == NULL) 77 { 78 OSVERSIONINFO osvi; 79 int status; 80 81 ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); 82 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 83 status = GetVersionEx(&osvi); 84 if (status == 0) 85 return 1; 86 87 if (osvi.dwMajorVersion >= 6) 88 pfIcmpCallback = icmpwin_callback_apc; 89 else 90 pfIcmpCallback = icmpwin_callback_old; 91 } 92 28 93 pData->icmp_socket.sh = IcmpCreateFile(); 29 94 pData->phEvents[VBOX_ICMP_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL); … … 41 106 RTMemFree(pData->pvIcmpBuffer); 42 107 } 108 109 110 /* 111 * Outgoing ping from guest. 112 */ 113 void 114 icmpwin_ping(PNATState pData, struct mbuf *m, int hlen) 115 { 116 struct ip *ip = mtod(m, struct ip *); 117 int icmplen = ip->ip_len - hlen; 118 uint8_t ttl; 119 size_t bufsize; 120 struct pong *pong; 121 IPAddr dst; 122 IP_OPTION_INFORMATION opts; 123 void *reqdata; 124 size_t reqsize; 125 int status; 126 127 ttl = ip->ip_ttl; 128 AssertReturnVoid(ttl > 1); /* should've been dealt with in the caller */ 129 --ttl; 130 131 bufsize = sizeof(ICMP_ECHO_REPLY) + icmplen; 132 pong = RTMemAlloc(RT_OFFSETOF(struct pong, buf) + bufsize); 133 if (RT_UNLIKELY(pong == NULL)) 134 return; 135 136 pong->pData = pData; 137 pong->bufsize = bufsize; 138 m_copydata(m, 0, hlen, (caddr_t)&pong->reqiph); 139 m_copydata(m, hlen, sizeof(struct icmp_echo), (caddr_t)&pong->reqicmph); 140 AssertReturnVoid(pong->reqicmph.icmp_type == ICMP_ECHO); 141 142 reqsize = icmplen - sizeof(struct icmp_echo); /* just the payload */ 143 if (m->m_next == NULL) 144 { 145 /* already in single contiguous buffer */ 146 reqdata = mtod(m, char *) + sizeof(struct ip) + sizeof(struct icmp_echo); 147 } 148 else 149 { 150 /* use reply buffer as temporary storage */ 151 reqdata = pong->buf; 152 m_copydata(m, sizeof(struct ip) + sizeof(struct icmp_echo), 153 reqsize, reqdata); 154 } 155 156 dst = ip->ip_dst.s_addr; 157 158 opts.Ttl = ttl; 159 opts.Tos = ip->ip_tos; /* affected by DisableUserTOSSetting key */ 160 opts.Flags = (ip->ip_off & IP_DF) != 0 ? IP_FLAG_DF : 0; 161 opts.OptionsSize = 0; 162 opts.OptionsData = 0; 163 164 165 status = IcmpSendEcho2(pData->icmp_socket.sh, NULL, 166 pfIcmpCallback, pong, 167 dst, reqdata, (WORD)reqsize, &opts, 168 pong->buf, (DWORD)pong->bufsize, 169 5 * 1000 /* ms */); 170 171 if (RT_UNLIKELY(status != 0)) 172 { 173 Log2(("NAT: IcmpSendEcho2: unexpected status %d\n", status)); 174 } 175 else if ((status = GetLastError()) != ERROR_IO_PENDING) 176 { 177 int code; 178 179 Log2(("NAT: IcmpSendEcho2: error %d\n", status)); 180 switch (status) { 181 case ERROR_NETWORK_UNREACHABLE: 182 code = ICMP_UNREACH_NET; 183 break; 184 case ERROR_HOST_UNREACHABLE: 185 code = ICMP_UNREACH_HOST; 186 break; 187 default: 188 code = -1; 189 break; 190 } 191 192 if (code != -1) /* send icmp error */ 193 { 194 struct mbuf *em = icmpwin_get_error(pong, ICMP_UNREACH, code); 195 if (em != NULL) 196 { 197 struct ip *eip = mtod(em, struct ip *); 198 eip->ip_src = alias_addr; 199 ip_output(pData, NULL, em); 200 } 201 } 202 } 203 else /* success */ 204 { 205 Log2(("NAT: pong %p for ping %RTnaipv4 id 0x%04x seq %d len %d\n", 206 pong, dst, 207 RT_N2H_U16(pong->reqicmph.icmp_echo_id), 208 RT_N2H_U16(pong->reqicmph.icmp_echo_seq), 209 reqsize)); 210 pong = NULL; /* callback owns it now */ 211 } 212 213 if (pong != NULL) 214 RTMemFree(pong); 215 } 216 217 218 static VOID WINAPI 219 icmpwin_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved) 220 { 221 struct pong *pong = (struct pong *)ctx; 222 223 if (pong != NULL) 224 { 225 icmpwin_callback(pong); 226 RTMemFree(pong); 227 } 228 } 229 230 231 static VOID WINAPI 232 icmpwin_callback_old(void *ctx) 233 { 234 struct pong *pong = (struct pong *)ctx; 235 236 if (pong != NULL) 237 { 238 icmpwin_callback(pong); 239 RTMemFree(pong); 240 } 241 } 242 243 244 /* 245 * Actual callback code for IcmpSendEcho2(). OS version specific 246 * trampoline will free "pong" argument for us. 247 */ 248 static void 249 icmpwin_callback(struct pong *pong) 250 { 251 PNATState pData; 252 DWORD nreplies; 253 ICMP_ECHO_REPLY *reply; 254 struct mbuf *m; 255 struct ip *ip; 256 struct icmp_echo *icmp; 257 size_t reqsize; 258 259 pData = pong->pData; /* to make slirp_state.h macro hackery work */ 260 261 nreplies = IcmpParseReplies(pong->buf, (DWORD)pong->bufsize); 262 if (nreplies == 0) 263 { 264 DWORD error = GetLastError(); 265 if (error == IP_REQ_TIMED_OUT) 266 Log2(("NAT: ping %p timed out\n", (void *)pong)); 267 else 268 Log2(("NAT: ping %p: IcmpParseReplies: error %d\n", 269 (void *)pong, error)); 270 return; 271 } 272 273 reply = (ICMP_ECHO_REPLY *)pong->buf; 274 275 if (reply->Status == IP_SUCCESS) 276 { 277 if (reply->Options.OptionsSize != 0) /* don't do options */ 278 return; 279 280 /* need to remap &reply->Address ? */ 281 if (/* not a mapped loopback */ 1) 282 { 283 if (reply->Options.Ttl <= 1) 284 return; 285 --reply->Options.Ttl; 286 } 287 288 reqsize = reply->DataSize; 289 if ( (reply->Options.Flags & IP_FLAG_DF) != 0 290 && sizeof(struct ip) + sizeof(struct icmp_echo) + reqsize > if_mtu) 291 return; 292 293 m = icmpwin_get_mbuf(pData, reqsize); 294 if (m == NULL) 295 return; 296 297 ip = mtod(m, struct ip *); 298 icmp = (struct icmp_echo *)(mtod(m, char *) + sizeof(*ip)); 299 300 /* fill in ip (ip_output0() does the boilerplate for us) */ 301 ip->ip_tos = reply->Options.Tos; 302 ip->ip_len = sizeof(*ip) + sizeof(*icmp) + reqsize; 303 ip->ip_off = 0; 304 ip->ip_ttl = reply->Options.Ttl; 305 ip->ip_p = IPPROTO_ICMP; 306 ip->ip_src.s_addr = reply->Address; 307 ip->ip_dst = pong->reqiph.ip_src; 308 309 icmp->icmp_type = ICMP_ECHOREPLY; 310 icmp->icmp_code = 0; 311 icmp->icmp_cksum = 0; 312 icmp->icmp_echo_id = pong->reqicmph.icmp_echo_id; 313 icmp->icmp_echo_seq = pong->reqicmph.icmp_echo_seq; 314 315 m_append(pData, m, reqsize, reply->Data); 316 317 icmp->icmp_cksum = in_cksum_skip(m, sizeof(*icmp) + reqsize, sizeof(*ip)); 318 } 319 else { 320 uint8_t type, code; 321 322 switch (reply->Status) { 323 case IP_DEST_NET_UNREACHABLE: 324 type = ICMP_UNREACH; code = ICMP_UNREACH_NET; 325 break; 326 case IP_DEST_HOST_UNREACHABLE: 327 type = ICMP_UNREACH; code = ICMP_UNREACH_HOST; 328 break; 329 case IP_DEST_PROT_UNREACHABLE: 330 type = ICMP_UNREACH; code = ICMP_UNREACH_PROTOCOL; 331 break; 332 case IP_PACKET_TOO_BIG: 333 type = ICMP_UNREACH; code = ICMP_UNREACH_NEEDFRAG; 334 break; 335 case IP_SOURCE_QUENCH: 336 type = ICMP_SOURCEQUENCH; code = 0; 337 break; 338 case IP_TTL_EXPIRED_TRANSIT: 339 type = ICMP_TIMXCEED; code = ICMP_TIMXCEED_INTRANS; 340 break; 341 case IP_TTL_EXPIRED_REASSEM: 342 type = ICMP_TIMXCEED; code = ICMP_TIMXCEED_REASS; 343 break; 344 default: 345 Log2(("NAT: ping reply status %d, dropped\n", reply->Status)); 346 return; 347 } 348 349 Log2(("NAT: ping status %d -> type %d/code %d\n", 350 reply->Status, type, code)); 351 352 /* 353 * XXX: we don't know the TTL of the request at the time this 354 * ICMP error was generated (we can guess it was 1 for ttl 355 * exceeded, but don't bother faking it). 356 */ 357 m = icmpwin_get_error(pong, type, code); 358 if (m == NULL) 359 return; 360 361 ip = mtod(m, struct ip *); 362 363 ip->ip_tos = reply->Options.Tos; 364 ip->ip_ttl = reply->Options.Ttl; /* XXX: decrement */ 365 ip->ip_src.s_addr = reply->Address; 366 } 367 368 Assert(ip->ip_len == m_length(m, NULL)); 369 ip_output(pData, NULL, m); 370 } 371 372 373 /* 374 * Prepare mbuf with ICMP error type/code. 375 * IP source must be filled by the caller. 376 */ 377 static struct mbuf * 378 icmpwin_get_error(struct pong *pong, int type, int code) 379 { 380 PNATState pData = pong->pData; 381 struct mbuf *m; 382 struct ip *ip; 383 struct icmp_echo *icmp; 384 size_t reqsize; 385 386 Log2(("NAT: ping error type %d/code %d\n", type, code)); 387 388 reqsize = sizeof(pong->reqiph) + sizeof(pong->reqicmph); 389 390 m = icmpwin_get_mbuf(pData, reqsize); 391 if (m == NULL) 392 return NULL; 393 394 ip = mtod(m, struct ip *); 395 icmp = (struct icmp_echo *)(mtod(m, char *) + sizeof(*ip)); 396 397 ip->ip_tos = 0; 398 ip->ip_len = sizeof(*ip) + sizeof(*icmp) + reqsize; 399 ip->ip_off = 0; 400 ip->ip_ttl = IPDEFTTL; 401 ip->ip_p = IPPROTO_ICMP; 402 ip->ip_src.s_addr = 0; /* NB */ 403 ip->ip_dst = pong->reqiph.ip_src; 404 405 icmp->icmp_type = type; 406 icmp->icmp_code = code; 407 icmp->icmp_cksum = 0; 408 icmp->icmp_echo_id = 0; 409 icmp->icmp_echo_seq = 0; 410 411 m_append(pData, m, sizeof(pong->reqiph), (caddr_t)&pong->reqiph); 412 m_append(pData, m, sizeof(pong->reqicmph), (caddr_t)&pong->reqicmph); 413 414 icmp->icmp_cksum = in_cksum_skip(m, sizeof(*icmp) + reqsize, sizeof(*ip)); 415 416 return m; 417 } 418 419 420 /* 421 * Replacing original simple slirp mbufs with real mbufs from freebsd 422 * was a bit messy since assumption are different. This leads to 423 * rather ugly code at times. Hide the gore here. 424 */ 425 static struct mbuf * 426 icmpwin_get_mbuf(PNATState pData, size_t reqsize) 427 { 428 struct mbuf *m; 429 430 reqsize += if_maxlinkhdr; 431 reqsize += sizeof(struct ip) + sizeof(struct icmp_echo); 432 433 if (reqsize <= MHLEN) 434 /* good pings come in small packets */ 435 m = m_gethdr(pData, M_NOWAIT, MT_HEADER); 436 else 437 m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, slirp_size(pData)); 438 439 if (m == NULL) 440 return NULL; 441 442 m->m_flags |= M_SKIP_FIREWALL; 443 m->m_data += if_maxlinkhdr; /* reserve leading space for ethernet header */ 444 445 m->m_pkthdr.header = mtod(m, void *); 446 m->m_len = sizeof(struct ip) + sizeof(struct icmp_echo); 447 448 return m; 449 }
Note:
See TracChangeset
for help on using the changeset viewer.