VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/proxy.c@ 49713

Last change on this file since 49713 was 49713, checked in by vboxsync, 11 years ago

Enable ping proxy.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.5 KB
Line 
1/* -*- indent-tabs-mode: nil; -*- */
2#include "winutils.h"
3
4#include "proxy.h"
5#include "proxy_pollmgr.h"
6#include "portfwd.h"
7
8#include "lwip/opt.h"
9
10#include "lwip/sys.h"
11#include "lwip/tcpip.h"
12
13#ifndef RT_OS_WINDOWS
14#include <sys/poll.h>
15#include <sys/socket.h>
16#include <netinet/in.h>
17#include <arpa/inet.h>
18#include <fcntl.h>
19#include <stdio.h>
20#include <iprt/string.h>
21#include <unistd.h>
22#include <err.h>
23#else
24# include <iprt/string.h>
25#endif
26
27#if defined(SOCK_NONBLOCK) && defined(RT_OS_NETBSD) /* XXX: PR kern/47569 */
28# undef SOCK_NONBLOCK
29#endif
30
31#ifndef __arraycount
32# define __arraycount(a) (sizeof(a)/sizeof(a[0]))
33#endif
34
35static SOCKET proxy_create_socket(int, int);
36
37volatile struct proxy_options *g_proxy_options;
38static sys_thread_t pollmgr_tid;
39
40/* XXX: for mapping loopbacks to addresses in our network (ip4) */
41struct netif *g_proxy_netif;
42/*
43 * Called on the lwip thread (aka tcpip thread) from tcpip_init() via
44 * its "tcpip_init_done" callback. Raw API is ok to use here
45 * (e.g. rtadvd), but netconn API is not.
46 */
47void
48proxy_init(struct netif *proxy_netif, struct proxy_options *opts)
49{
50 int status;
51
52 LWIP_ASSERT1(opts != NULL);
53 LWIP_UNUSED_ARG(proxy_netif);
54
55 g_proxy_options = opts;
56 g_proxy_netif = proxy_netif;
57
58#if 1
59 proxy_rtadvd_start(proxy_netif);
60#endif
61
62 /*
63 * XXX: We use stateless DHCPv6 only to report IPv6 address(es) of
64 * nameserver(s). Since we don't yet support IPv6 addresses in
65 * HostDnsService, there's no point in running DHCPv6.
66 */
67#if 0
68 dhcp6ds_init(proxy_netif);
69#endif
70
71 if (opts->tftp_root != NULL) {
72 tftpd_init(proxy_netif, opts->tftp_root);
73 }
74
75 status = pollmgr_init();
76 if (status < 0) {
77 errx(EXIT_FAILURE, "failed to initialize poll manager");
78 /* NOTREACHED */
79 }
80
81 pxtcp_init();
82 pxudp_init();
83
84 portfwd_init();
85
86 pxdns_init(proxy_netif);
87
88 pxping_init(proxy_netif, opts->icmpsock4, opts->icmpsock6);
89
90 pollmgr_tid = sys_thread_new("pollmgr_thread",
91 pollmgr_thread, NULL,
92 DEFAULT_THREAD_STACKSIZE,
93 DEFAULT_THREAD_PRIO);
94 if (!pollmgr_tid) {
95 errx(EXIT_FAILURE, "failed to create poll manager thread");
96 /* NOTREACHED */
97 }
98}
99
100
101/**
102 * Send static callback message from poll manager thread to lwip
103 * thread, scheduling a function call in lwip thread context.
104 *
105 * XXX: Existing lwip api only provides non-blocking version for this.
106 * It may fail when lwip thread is not running (mbox invalid) or if
107 * post failed (mbox full). How to handle these?
108 */
109void
110proxy_lwip_post(struct tcpip_msg *msg)
111{
112 struct tcpip_callback_msg *m;
113 err_t error;
114
115 LWIP_ASSERT1(msg != NULL);
116
117 /*
118 * lwip plays games with fake incomplete struct tag to enforce API
119 */
120 m = (struct tcpip_callback_msg *)msg;
121 error = tcpip_callbackmsg(m);
122
123 if (error == ERR_VAL) {
124 /* XXX: lwip thread is not running (mbox invalid) */
125 LWIP_ASSERT1(error != ERR_VAL);
126 }
127
128 LWIP_ASSERT1(error == ERR_OK);
129}
130
131
132/**
133 * Create a non-blocking socket. Disable SIGPIPE for TCP sockets if
134 * possible. On Linux it's not possible and should be disabled for
135 * each send(2) individually.
136 */
137static SOCKET
138proxy_create_socket(int sdom, int stype)
139{
140 SOCKET s;
141 int stype_and_flags;
142 int status;
143
144 LWIP_UNUSED_ARG(status); /* depends on ifdefs */
145
146
147 stype_and_flags = stype;
148
149#if defined(SOCK_NONBLOCK)
150 stype_and_flags |= SOCK_NONBLOCK;
151#endif
152
153 /*
154 * Disable SIGPIPE on disconnected socket. It might be easier to
155 * forgo it and just use MSG_NOSIGNAL on each send*(2), since we
156 * have to do it for Linux anyway, but Darwin does NOT have that
157 * flag (but has SO_NOSIGPIPE socket option).
158 */
159#if !defined(SOCK_NOSIGPIPE) && !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
160#if 0 /* XXX: Solaris has neither, the program should ignore SIGPIPE globally */
161#error Need a way to disable SIGPIPE on connection oriented sockets!
162#endif
163#endif
164
165#if defined(SOCK_NOSIGPIPE)
166 if (stype == SOCK_STREAM) {
167 stype_and_flags |= SOCK_NOSIGPIPE;
168 }
169#endif
170
171 s = socket(sdom, stype_and_flags, 0);
172 if (s == INVALID_SOCKET) {
173 perror("socket");
174 return INVALID_SOCKET;
175 }
176
177#if !defined(SOCK_NONBLOCK) && !defined(RT_OS_WINDOWS)
178 {
179 int sflags;
180
181 status = fcntl(s, F_GETFL, &sflags);
182 if (status < 0) {
183 perror("F_GETFL");
184 closesocket(s);
185 return INVALID_SOCKET;
186 }
187
188 status = fcntl(s, F_SETFL, sflags | O_NONBLOCK);
189 if (status < 0) {
190 perror("O_NONBLOCK");
191 closesocket(s);
192 return INVALID_SOCKET;
193 }
194 }
195#endif
196
197#if !defined(SOCK_NOSIGPIPE) && defined(SO_NOSIGPIPE)
198 if (stype == SOCK_STREAM) {
199 int on = 1;
200 const socklen_t onlen = sizeof(on);
201
202 status = setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &on, onlen);
203 if (status < 0) {
204 perror("SO_NOSIGPIPE");
205 closesocket(s);
206 return INVALID_SOCKET;
207 }
208 }
209#endif
210
211#if defined(RT_OS_WINDOWS)
212 {
213 u_long mode = 0;
214 status = ioctlsocket(s, FIONBIO, &mode);
215 if (status == SOCKET_ERROR) {
216 warn("ioctl error: %d\n", WSAGetLastError());
217 return INVALID_SOCKET;
218 }
219 }
220#endif
221
222 return s;
223}
224
225
226/**
227 * Create a socket for outbound connection to dst_addr:dst_port.
228 *
229 * The socket is non-blocking and TCP sockets has SIGPIPE disabled if
230 * possible. On Linux it's not possible and should be disabled for
231 * each send(2) individually.
232 */
233SOCKET
234proxy_connected_socket(int sdom, int stype,
235 ipX_addr_t *dst_addr, u16_t dst_port)
236{
237 struct sockaddr_in6 dst_sin6;
238 struct sockaddr_in dst_sin;
239 struct sockaddr *pdst_sa;
240 socklen_t dst_sa_len;
241 void *pdst_addr;
242 const struct sockaddr *psrc_sa;
243 socklen_t src_sa_len;
244 int status;
245 SOCKET s;
246
247 LWIP_ASSERT1(sdom == PF_INET || sdom == PF_INET6);
248 LWIP_ASSERT1(stype == SOCK_STREAM || stype == SOCK_DGRAM);
249
250 if (sdom == PF_INET6) {
251 pdst_sa = (struct sockaddr *)&dst_sin6;
252 pdst_addr = (void *)&dst_sin6.sin6_addr;
253
254 memset(&dst_sin6, 0, sizeof(dst_sin6));
255#if HAVE_SA_LEN
256 dst_sin6.sin6_len =
257#endif
258 dst_sa_len = sizeof(dst_sin6);
259 dst_sin6.sin6_family = AF_INET6;
260 memcpy(&dst_sin6.sin6_addr, &dst_addr->ip6, sizeof(ip6_addr_t));
261 dst_sin6.sin6_port = htons(dst_port);
262 }
263 else { /* sdom = PF_INET */
264 pdst_sa = (struct sockaddr *)&dst_sin;
265 pdst_addr = (void *)&dst_sin.sin_addr;
266
267 memset(&dst_sin, 0, sizeof(dst_sin));
268#if HAVE_SA_LEN
269 dst_sin.sin_len =
270#endif
271 dst_sa_len = sizeof(dst_sin);
272 dst_sin.sin_family = AF_INET;
273 dst_sin.sin_addr.s_addr = dst_addr->ip4.addr; /* byte-order? */
274 dst_sin.sin_port = htons(dst_port);
275 }
276
277#if LWIP_PROXY_DEBUG && !RT_OS_WINDOWS
278 {
279 char addrbuf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
280 const char *addrstr;
281
282 addrstr = inet_ntop(sdom, pdst_addr, addrbuf, sizeof(addrbuf));
283 DPRINTF(("---> %s %s%s%s:%d ",
284 stype == SOCK_STREAM ? "TCP" : "UDP",
285 sdom == PF_INET6 ? "[" : "",
286 addrstr,
287 sdom == PF_INET6 ? "]" : "",
288 dst_port));
289 }
290#endif
291
292 s = proxy_create_socket(sdom, stype);
293 if (s == INVALID_SOCKET) {
294 return INVALID_SOCKET;
295 }
296 DPRINTF(("socket %d\n", s));
297
298 /* TODO: needs locking if dynamic modifyvm is allowed */
299 if (sdom == PF_INET6) {
300 psrc_sa = (const struct sockaddr *)g_proxy_options->src6;
301 src_sa_len = sizeof(struct sockaddr_in6);
302 }
303 else {
304 psrc_sa = (const struct sockaddr *)g_proxy_options->src4;
305 src_sa_len = sizeof(struct sockaddr_in);
306 }
307 if (psrc_sa != NULL) {
308 status = bind(s, psrc_sa, src_sa_len);
309 if (status == SOCKET_ERROR) {
310 DPRINTF(("socket %d: bind: %s\n", s, strerror(errno)));
311 closesocket(s);
312 return INVALID_SOCKET;
313 }
314 }
315
316 status = connect(s, pdst_sa, dst_sa_len);
317 if (status == SOCKET_ERROR && errno != EINPROGRESS) {
318 DPRINTF(("socket %d: connect: %s\n", s, strerror(errno)));
319 closesocket(s);
320 return INVALID_SOCKET;
321 }
322
323 return s;
324}
325
326
327/**
328 * Create a socket for inbound (port-forwarded) connections to
329 * src_addr (port is part of sockaddr, so not a separate argument).
330 *
331 * The socket is non-blocking and TCP sockets has SIGPIPE disabled if
332 * possible. On Linux it's not possible and should be disabled for
333 * each send(2) individually.
334 *
335 * TODO?: Support v6-mapped v4 so that user can specify she wants
336 * "udp" and get both versions?
337 */
338SOCKET
339proxy_bound_socket(int sdom, int stype, struct sockaddr *src_addr)
340{
341 SOCKET s;
342 int on;
343 const socklen_t onlen = sizeof(on);
344 int status;
345
346 s = proxy_create_socket(sdom, stype);
347 if (s == INVALID_SOCKET) {
348 return INVALID_SOCKET;
349 }
350 DPRINTF(("socket %d\n", s));
351
352 on = 1;
353 status = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, onlen);
354 if (status < 0) { /* not good, but not fatal */
355 warn("SO_REUSEADDR");
356 }
357
358 status = bind(s, src_addr,
359 sdom == PF_INET ?
360 sizeof(struct sockaddr_in)
361 : sizeof(struct sockaddr_in6));
362 if (status < 0) {
363 perror("bind");
364 closesocket(s);
365 return INVALID_SOCKET;
366 }
367
368 if (stype == SOCK_STREAM) {
369 status = listen(s, 5);
370 if (status < 0) {
371 perror("listen");
372 closesocket(s);
373 return INVALID_SOCKET;
374 }
375 }
376
377 return s;
378}
379
380
381void
382proxy_reset_socket(SOCKET s)
383{
384 struct linger linger;
385
386 linger.l_onoff = 1;
387 linger.l_linger = 0;
388
389 /* On Windows we can run into issue here, perhaps SO_LINGER isn't enough, and
390 * we should use WSA{Send,Recv}Disconnect instead.
391 *
392 * Links for the reference:
393 * http://msdn.microsoft.com/en-us/library/windows/desktop/ms738547%28v=vs.85%29.aspx
394 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4468997
395 */
396 setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof(linger));
397
398 closesocket(s);
399}
400
401
402void
403proxy_sendto(SOCKET sock, struct pbuf *p, void *name, size_t namelen)
404{
405 struct pbuf *q;
406 size_t i, clen;
407#ifndef RT_OS_WINDOWS
408 struct msghdr mh;
409 ssize_t nsent;
410#else
411 DWORD nsent;
412 int rc;
413#endif
414 IOVEC fixiov[8]; /* fixed size (typical case) */
415 const size_t fixiovsize = sizeof(fixiov)/sizeof(fixiov[0]);
416 IOVEC *dyniov; /* dynamically sized */
417 IOVEC *iov;
418
419 /*
420 * Static iov[] is usually enough since UDP protocols use small
421 * datagrams to avoid fragmentation, but be prepared.
422 */
423 clen = pbuf_clen(p);
424 if (clen > fixiovsize) {
425 /*
426 * XXX: TODO: check that clen is shorter than IOV_MAX
427 */
428 dyniov = (IOVEC *)malloc(clen * sizeof(*dyniov));
429 if (dyniov == NULL) {
430 goto out;
431 }
432 iov = dyniov;
433 }
434 else {
435 dyniov = NULL;
436 iov = fixiov;
437 }
438
439
440 for (q = p, i = 0; i < clen; q = q->next, ++i) {
441 LWIP_ASSERT1(q != NULL);
442
443 IOVEC_SET_BASE(iov[i], q->payload);
444 IOVEC_SET_LEN(iov[i], q->len);
445 }
446
447#ifndef RT_OS_WINDOWS
448 memset(&mh, 0, sizeof(mh));
449 mh.msg_name = name;
450 mh.msg_namelen = namelen;
451 mh.msg_iov = iov;
452 mh.msg_iovlen = clen;
453
454 nsent = sendmsg(sock, &mh, 0);
455 if (nsent < 0) {
456 DPRINTF(("%s: fd %d: sendmsg errno %d\n",
457 __func__, sock, errno));
458 }
459#else
460 rc = WSASendTo(sock, iov, (DWORD)clen, &nsent, 0,
461 name, (int)namelen, NULL, NULL);
462 if (rc == SOCKET_ERROR) {
463 DPRINTF(("%s: fd %d: sendmsg errno %d\n",
464 __func__, sock, WSAGetLastError()));
465 }
466#endif
467
468 out:
469 if (dyniov != NULL) {
470 free(dyniov);
471 }
472 pbuf_free(p);
473}
474
475
476static const char *lwiperr[] = {
477 "ERR_OK",
478 "ERR_MEM",
479 "ERR_BUF",
480 "ERR_TIMEOUT",
481 "ERR_RTE",
482 "ERR_INPROGRESS",
483 "ERR_VAL",
484 "ERR_WOULDBLOCK",
485 "ERR_USE",
486 "ERR_ISCONN",
487 "ERR_ABRT",
488 "ERR_RST",
489 "ERR_CLSD",
490 "ERR_CONN",
491 "ERR_ARG",
492 "ERR_IF"
493};
494
495
496const char *
497proxy_lwip_strerr(err_t error)
498{
499 static char buf[32];
500 int e = -error;
501
502 if (0 < e || e < (int)__arraycount(lwiperr)) {
503 return lwiperr[e];
504 }
505 else {
506 RTStrPrintf(buf, sizeof(buf), "unknown error %d", error);
507 return buf;
508 }
509}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette