VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/proxytest.c@ 48387

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

Disable stateless DHCPv6 server for now. It only tells guests the
nameserver IPv6 address, but HostDnsService doesn't support IPv6
nameserver addresses yet. Do not set "O" (other configuration) flag
in router advertisements, accordingly. This should avoid confusion
for the beta.

Add "TODO" comments in DHCPv6 server code where the nameserver address
should be picked up - it currently has leftover testing kludge, that
advertised mapped host loopback as IPv6 nameserver (with a
corresponding hack to remap IPv6 to IPv4 in pxudp.c that has been
disabled since).

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