VirtualBox

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

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

NAT/Net: #define LOG_GROUP LOG_GROUP_NAT_SERVICE

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