VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvCloudTunnel.cpp@ 99785

Last change on this file since 99785 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 69.5 KB
Line 
1/* $Id: DrvCloudTunnel.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * DrvCloudTunnel - Cloud tunnel network transport driver
4 *
5 * Based on code contributed by Christophe Devriese
6 */
7
8/*
9 * Copyright (C) 2022-2023 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#define LOG_GROUP LOG_GROUP_DRV_CTUN
35#include <VBox/log.h>
36#include <VBox/vmm/pdmdrv.h>
37#include <VBox/vmm/pdmnetifs.h>
38#include <VBox/vmm/pdmnetinline.h>
39
40#include <iprt/asm.h>
41#include <iprt/assert.h>
42#include <iprt/ctype.h>
43#include <iprt/mem.h>
44#include <iprt/path.h>
45#include <iprt/uuid.h>
46#include <iprt/req.h>
47#include <iprt/stream.h>
48#include <iprt/string.h>
49#include <iprt/critsect.h>
50
51#include "VBoxDD.h"
52
53#ifdef RT_OS_WINDOWS
54# include <iprt/win/windows.h>
55typedef int socklen_t;
56#else
57# include <errno.h>
58 typedef int SOCKET;
59# define closesocket close
60# define INVALID_SOCKET -1
61# define SOCKET_ERROR -1
62DECLINLINE(int) WSAGetLastError() { return errno; }
63#endif
64
65/* Prevent inclusion of Winsock2.h */
66#define _WINSOCK2API_
67#include <libssh/libssh.h>
68#include <libssh/callbacks.h>
69
70
71/*********************************************************************************************************************************
72* Structures and Typedefs *
73*********************************************************************************************************************************/
74/**
75 * Cloud tunnel driver instance data.
76 *
77 * @implements PDMINETWORKUP
78 */
79typedef struct DRVCLOUDTUNNEL
80{
81 /** The network interface. */
82 PDMINETWORKUP INetworkUp;
83 /** The network interface. */
84 PPDMINETWORKDOWN pIAboveNet;
85 /** Pointer to the driver instance. */
86 PPDMDRVINS pDrvIns;
87 /** Cloud instance private key. */
88 ssh_key SshKey;
89 /** Cloud instance user. */
90 char *pszUser;
91 /** Cloud instance primary IP address. */
92 char *pszPrimaryIP;
93 /** Cloud instance primary IP address. */
94 char *pszSecondaryIP;
95 /** MAC address to set on cloud primary interface. */
96 RTMAC targetMac;
97 /** SSH connection timeout in seconds. */
98 long ulTimeoutInSecounds;
99
100 /** Primary proxy type. */
101 char *pszPrimaryProxyType;
102 /** Primary proxy server IP address. */
103 char *pszPrimaryProxyHost;
104 /** Primary proxy server port. */
105 uint16_t u16PrimaryProxyPort;
106 /** Primary proxy user. */
107 char *pszPrimaryProxyUser;
108 /** Primary proxy password. */
109 char *pszPrimaryProxyPassword;
110
111 /** Secondary proxy type. */
112 char *pszSecondaryProxyType;
113 /** Secondary proxy server IP address. */
114 char *pszSecondaryProxyHost;
115 /** Secondary proxy server port. */
116 uint16_t u16SecondaryProxyPort;
117 /** Secondary proxy user. */
118 char *pszSecondaryProxyUser;
119 /** Secondary proxy password. */
120 char *pszSecondaryProxyPassword;
121
122 /** Cloud tunnel instance string. */
123 char *pszInstance;
124 /** Cloud tunnel I/O thread unique name. */
125 char *pszInstanceIo;
126 /** Cloud tunnel device thread unique name. */
127 char *pszInstanceDev;
128
129 /** Command assembly buffer. */
130 char *pszCommandBuffer;
131 /** Command output buffer. */
132 char *pszOutputBuffer;
133 /** Name of primary interface of cloud instance. */
134 char *pszCloudPrimaryInterface;
135
136 /** Cloud destination address. */
137 RTNETADDR DestAddress;
138 /** Transmit lock used by drvCloudTunnelUp_BeginXmit. */
139 RTCRITSECT XmitLock;
140 /** Server data structure for Cloud communication. */
141// PRTCLOUDSERVER pServer;
142
143 /** RX thread for delivering packets to attached device. */
144 PPDMTHREAD pDevThread;
145 /** Queue for device-thread requests. */
146 RTREQQUEUE hDevReqQueue;
147 /** I/O thread for tunnel channel. */
148 PPDMTHREAD pIoThread;
149 /** Queue for I/O-thread requests. */
150 RTREQQUEUE hIoReqQueue;
151 /** I/O thread notification socket pair (in). */
152 SOCKET iSocketIn;
153 /** I/O thread notification socket pair (out). */
154 SOCKET iSocketOut;
155
156 /** SSH private key. */
157
158 /** SSH Log Verbosity: 0 - No log, 1 - warnings, 2 - protocol, 3 - packet, 4 - functions */
159 int iSshVerbosity;
160 /** SSH Session. */
161 ssh_session pSshSession;
162 /** SSH Tunnel Channel. */
163 ssh_channel pSshChannel;
164 /** SSH Packet Receive Callback Structure. */
165 struct ssh_channel_callbacks_struct Callbacks;
166
167 /** Flag whether the link is down. */
168 bool volatile fLinkDown;
169
170#ifdef VBOX_WITH_STATISTICS
171 /** Number of sent packets. */
172 STAMCOUNTER StatPktSent;
173 /** Number of sent bytes. */
174 STAMCOUNTER StatPktSentBytes;
175 /** Number of received packets. */
176 STAMCOUNTER StatPktRecv;
177 /** Number of received bytes. */
178 STAMCOUNTER StatPktRecvBytes;
179 /** Profiling packet transmit runs. */
180 STAMPROFILEADV StatTransmit;
181 /** Profiling packet receive runs. */
182 STAMPROFILEADV StatReceive;
183 /** Profiling packet receive device (both actual receive and waiting). */
184 STAMPROFILE StatDevRecv;
185 /** Profiling packet receive device waiting. */
186 STAMPROFILE StatDevRecvWait;
187#endif /* VBOX_WITH_STATISTICS */
188
189#ifdef LOG_ENABLED
190 /** The nano ts of the last transfer. */
191 uint64_t u64LastTransferTS;
192 /** The nano ts of the last receive. */
193 uint64_t u64LastReceiveTS;
194#endif
195} DRVCLOUDTUNNEL, *PDRVCLOUDTUNNEL;
196
197
198/** Converts a pointer to CLOUDTUNNEL::INetworkUp to a PRDVCLOUDTUNNEL. */
199#define PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface) ( (PDRVCLOUDTUNNEL)((uintptr_t)pInterface - RT_UOFFSETOF(DRVCLOUDTUNNEL, INetworkUp)) )
200
201
202/*********************************************************************************************************************************
203* Internal Functions *
204*********************************************************************************************************************************/
205
206/**
207 * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
208 */
209static DECLCALLBACK(int) drvCloudTunnelUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
210{
211 RT_NOREF(fOnWorkerThread);
212 PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
213 int rc = RTCritSectTryEnter(&pThis->XmitLock);
214 if (RT_FAILURE(rc))
215 {
216 /** @todo XMIT thread */
217 rc = VERR_TRY_AGAIN;
218 }
219 return rc;
220}
221
222/**
223 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
224 */
225static DECLCALLBACK(int) drvCloudTunnelUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
226 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
227{
228 PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
229 Assert(RTCritSectIsOwner(&pThis->XmitLock)); NOREF(pThis);
230
231 /*
232 * Allocate a scatter / gather buffer descriptor that is immediately
233 * followed by the buffer space of its single segment. The GSO context
234 * comes after that again.
235 */
236 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16)
237 + RT_ALIGN_Z(cbMin, 16)
238 + (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
239 if (!pSgBuf)
240 return VERR_NO_MEMORY;
241
242 /*
243 * Initialize the S/G buffer and return.
244 */
245 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
246 pSgBuf->cbUsed = 0;
247 pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
248 pSgBuf->pvAllocator = NULL;
249 if (!pGso)
250 pSgBuf->pvUser = NULL;
251 else
252 {
253 pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
254 *(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
255 }
256 pSgBuf->cSegs = 1;
257 pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
258 pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
259
260#if 0 /* poison */
261 memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
262#endif
263 *ppSgBuf = pSgBuf;
264 return VINF_SUCCESS;
265}
266
267
268/**
269 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
270 */
271static DECLCALLBACK(int) drvCloudTunnelUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
272{
273 PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
274 Assert(RTCritSectIsOwner(&pThis->XmitLock)); NOREF(pThis);
275 if (pSgBuf)
276 {
277 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
278 pSgBuf->fFlags = 0;
279 RTMemFree(pSgBuf);
280 }
281 return VINF_SUCCESS;
282}
283
284static int createConnectedSockets(PDRVCLOUDTUNNEL pThis)
285{
286 LogFlow(("%s: creating a pair of connected sockets...\n", pThis->pszInstance));
287 struct sockaddr_in inaddr;
288 struct sockaddr addr;
289 SOCKET lst = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
290 memset(&inaddr, 0, sizeof(inaddr));
291 memset(&addr, 0, sizeof(addr));
292 inaddr.sin_family = AF_INET;
293 inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
294 inaddr.sin_port = 0;
295 int yes = 1;
296 setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
297 bind(lst, (struct sockaddr *)&inaddr, sizeof(inaddr));
298 listen(lst, 1);
299 socklen_t len=sizeof(inaddr);
300 getsockname(lst, &addr, &len);
301 pThis->iSocketOut = socket(AF_INET, SOCK_STREAM, 0);
302 connect(pThis->iSocketOut, &addr, len);
303 pThis->iSocketIn = accept(lst, 0, 0);
304 closesocket(lst);
305 Log2(("%s: socket(%d) <= socket(%d) created successfully.\n", pThis->pszInstance, pThis->iSocketIn, pThis->iSocketOut));
306 return VINF_SUCCESS;
307}
308
309
310static void destroyConnectedSockets(PDRVCLOUDTUNNEL pThis)
311{
312 if (pThis->iSocketOut != INVALID_SOCKET)
313 {
314 LogFlow(("%s: destroying output socket (%d)...\n", pThis->pszInstance, pThis->iSocketOut));
315 closesocket(pThis->iSocketOut);
316 }
317 if (pThis->iSocketIn != INVALID_SOCKET)
318 {
319 LogFlow(("%s: destroying input socket (%d)...\n", pThis->pszInstance, pThis->iSocketIn));
320 closesocket(pThis->iSocketIn);
321 }
322}
323
324
325DECLINLINE(void) drvCloudTunnelFreeSgBuf(PDRVCLOUDTUNNEL pThis, PPDMSCATTERGATHER pSgBuf)
326{
327 RT_NOREF(pThis);
328 RTMemFree(pSgBuf);
329}
330
331DECLINLINE(void) drvCloudTunnelNotifyIoThread(PDRVCLOUDTUNNEL pThis, const char *pszWho)
332{
333 RT_NOREF(pszWho);
334 int cBytes = send(pThis->iSocketOut, " ", 1, 0);
335 if (cBytes == SOCKET_ERROR)
336 LogRel(("Failed to send a signalling packet, error code %d", WSAGetLastError())); // @todo!
337
338}
339
340
341/**
342 * Worker function for sending packets on I/O thread.
343 *
344 * @param pThis Pointer to the cloud tunnel instance.
345 * @param pSgBuf The scatter/gather buffer.
346 * @thread I/O
347 */
348static DECLCALLBACK(void) drvCloudTunnelSendWorker(PDRVCLOUDTUNNEL pThis, PPDMSCATTERGATHER pSgBuf)
349{
350 // int rc = VINF_SUCCESS;
351 if (!pSgBuf->pvUser)
352 {
353#ifdef LOG_ENABLED
354 uint64_t u64Now = RTTimeProgramNanoTS();
355 LogFunc(("%-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
356 pSgBuf->cbUsed, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
357 pThis->u64LastTransferTS = u64Now;
358#endif
359 Log2(("writing to tunnel channel: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n%.*Rhxd\n",
360 pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed, pSgBuf->aSegs[0].pvSeg));
361
362 int cBytes = ssh_channel_write(pThis->pSshChannel, pSgBuf->aSegs[0].pvSeg, (uint32_t)pSgBuf->cbUsed);
363 if (cBytes == SSH_ERROR)
364 LogRel(("%s: ssh_channel_write failed\n", pThis->pszInstance));
365 }
366 else
367 {
368 uint8_t abHdrScratch[256];
369 uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
370 PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
371 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
372 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
373 {
374 uint32_t cbSegFrame;
375 void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
376 iSeg, cSegs, &cbSegFrame);
377 Log2(("writing to tunnel channel: pvSegFrame=%p cbSegFrame=%#x\n%.*Rhxd\n",
378 pvSegFrame, cbSegFrame, cbSegFrame, pvSegFrame));
379 int cBytes = ssh_channel_write(pThis->pSshChannel, pvSegFrame, cbSegFrame);
380 if (cBytes == SSH_ERROR)
381 LogRel(("%s: ssh_channel_write failed\n", pThis->pszInstance));
382 }
383 }
384
385 pSgBuf->fFlags = 0;
386 RTMemFree(pSgBuf);
387
388 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
389 // AssertRC(rc);
390 // if (RT_FAILURE(rc))
391 // {
392 // if (rc == VERR_NO_MEMORY)
393 // rc = VERR_NET_NO_BUFFER_SPACE;
394 // else
395 // rc = VERR_NET_DOWN;
396 // }
397 // return rc;
398}
399
400
401/**
402 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
403 */
404static DECLCALLBACK(int) drvCloudTunnelUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
405{
406 RT_NOREF(fOnWorkerThread);
407 PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
408 STAM_COUNTER_INC(&pThis->StatPktSent);
409 STAM_COUNTER_ADD(&pThis->StatPktSentBytes, pSgBuf->cbUsed);
410 STAM_PROFILE_ADV_START(&pThis->StatTransmit, a);
411
412 AssertPtr(pSgBuf);
413 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
414 Assert(RTCritSectIsOwner(&pThis->XmitLock));
415
416 int rc = VINF_SUCCESS;
417 if (pThis->pIoThread && pThis->pIoThread->enmState == PDMTHREADSTATE_RUNNING)
418 {
419 Log2(("%s: submitting TX request (pvSeg=%p, %u bytes) to I/O queue...\n",
420 pThis->pszInstance, pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed));
421 rc = RTReqQueueCallEx(pThis->hIoReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
422 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
423 (PFNRT)drvCloudTunnelSendWorker, 2, pThis, pSgBuf);
424
425 if (RT_SUCCESS(rc))
426 {
427 drvCloudTunnelNotifyIoThread(pThis, "drvCloudTunnelUp_SendBuf");
428 return VINF_SUCCESS;
429 }
430
431 rc = VERR_NET_NO_BUFFER_SPACE;
432 }
433 else
434 rc = VERR_NET_DOWN;
435 drvCloudTunnelFreeSgBuf(pThis, pSgBuf);
436 return rc;
437}
438
439
440/**
441 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
442 */
443static DECLCALLBACK(void) drvCloudTunnelUp_EndXmit(PPDMINETWORKUP pInterface)
444{
445 PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
446 RTCritSectLeave(&pThis->XmitLock);
447}
448
449
450/**
451 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
452 */
453static DECLCALLBACK(void) drvCloudTunnelUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
454{
455 RT_NOREF(pInterface, fPromiscuous);
456 LogFlowFunc(("fPromiscuous=%d\n", fPromiscuous));
457 /* nothing to do */
458}
459
460
461/**
462 * Notification on link status changes.
463 *
464 * @param pInterface Pointer to the interface structure containing the called function pointer.
465 * @param enmLinkState The new link state.
466 * @thread EMT
467 */
468static DECLCALLBACK(void) drvCloudTunnelUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
469{
470 LogFlowFunc(("enmLinkState=%d\n", enmLinkState));
471 PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
472
473 bool fLinkDown;
474 switch (enmLinkState)
475 {
476 case PDMNETWORKLINKSTATE_DOWN:
477 case PDMNETWORKLINKSTATE_DOWN_RESUME:
478 fLinkDown = true;
479 break;
480 default:
481 AssertMsgFailed(("enmLinkState=%d\n", enmLinkState));
482 RT_FALL_THRU();
483 case PDMNETWORKLINKSTATE_UP:
484 fLinkDown = false;
485 break;
486 }
487 ASMAtomicXchgSize(&pThis->fLinkDown, fLinkDown);
488}
489
490
491
492/* -=-=-=-=- PDMIBASE -=-=-=-=- */
493
494/**
495 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
496 */
497static DECLCALLBACK(void *) drvCloudTunnelQueryInterface(PPDMIBASE pInterface, const char *pszIID)
498{
499 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
500 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
501
502 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
503 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
504 return NULL;
505}
506
507
508/**
509 * I/O thread handling the libssh I/O.
510 *
511 * The libssh implementation is single-threaded so we perform I/O in a
512 * dedicated thread. We take care that this thread does not become the
513 * bottleneck: If the guest wants to send, a request is enqueued into the
514 * hIoReqQueue and is handled asynchronously by this thread. TODO:If this thread
515 * wants to deliver packets to the guest, it enqueues a request into
516 * hRecvReqQueue which is later handled by the Recv thread.
517 */
518static DECLCALLBACK(int) drvCloudTunnelIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
519{
520 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
521 // int nFDs = -1;
522
523 LogFlow(("%s: started I/O thread %p\n", pThis->pszInstance, pThread));
524
525 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
526 return VINF_SUCCESS;
527
528 // if (pThis->enmLinkStateWant != pThis->enmLinkState)
529 // drvNATNotifyLinkChangedWorker(pThis, pThis->enmLinkStateWant);
530
531 /*
532 * Polling loop.
533 */
534 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
535 {
536 /*
537 * To prevent concurrent execution of sending/receiving threads
538 */
539//#ifndef RT_OS_WINDOWS
540 // /* process _all_ outstanding requests but don't wait */
541 // RTReqQueueProcess(pThis->hIoReqQueue, 0);
542 // RTMemFree(polls);
543//#else /* RT_OS_WINDOWS */
544
545 struct timeval timeout;
546 ssh_channel in_channels[2], out_channels[2];
547 fd_set fds;
548 int maxfd;
549
550 timeout.tv_sec = 30;
551 timeout.tv_usec = 0;
552 in_channels[0] = pThis->pSshChannel;
553 in_channels[1] = NULL;
554 FD_ZERO(&fds);
555 FD_SET(pThis->iSocketIn, &fds);
556 maxfd = pThis->iSocketIn + 1;
557
558 ssh_select(in_channels, out_channels, maxfd, &fds, &timeout);
559
560 /* Poll will call the receive callback on each packet coming from the tunnel. */
561 if (out_channels[0] != NULL)
562 ssh_channel_poll(pThis->pSshChannel, false);
563
564 /* Did we get notified by drvCloudTunnelNotifyIoThread() via connected sockets? */
565 if (FD_ISSET(pThis->iSocketIn, &fds))
566 {
567 char buf[2];
568 recv(pThis->iSocketIn, buf, 1, 0);
569 /* process all outstanding requests but don't wait */
570 RTReqQueueProcess(pThis->hIoReqQueue, 0);
571 }
572//#endif /* RT_OS_WINDOWS */
573 }
574
575 LogFlow(("%s: I/O thread %p terminated\n", pThis->pszInstance, pThread));
576
577 return VINF_SUCCESS;
578}
579
580
581/**
582 * Unblock the I/O thread so it can respond to a state change.
583 *
584 * @returns VBox status code.
585 * @param pDevIns The pcnet device instance.
586 * @param pThread The send thread.
587 */
588static DECLCALLBACK(int) drvCloudTunnelIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
589{
590 RT_NOREF(pThread);
591 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
592
593 LogFlow(("%s: waking up I/O thread %p...\n", pThis->pszInstance, pThread));
594
595 drvCloudTunnelNotifyIoThread(pThis, "drvCloudTunnelIoWakeup");
596 return VINF_SUCCESS;
597}
598
599
600/*
601 * Remove the following cut&paste code after a while, when
602 * we are positive that no frames get coalesced!
603 */
604#define VBOX_CTUN_COALESCED_FRAME_DETECTION
605#ifdef VBOX_CTUN_COALESCED_FRAME_DETECTION
606struct ssh_buffer_struct {
607 bool secure;
608 size_t used;
609 size_t allocated;
610 size_t pos;
611 uint8_t *data;
612};
613
614/** @internal
615 * Describes the different possible states in a
616 * outgoing (client) channel request
617 */
618enum ssh_channel_request_state_e {
619 /** No request has been made */
620 SSH_CHANNEL_REQ_STATE_NONE = 0,
621 /** A request has been made and answer is pending */
622 SSH_CHANNEL_REQ_STATE_PENDING,
623 /** A request has been replied and accepted */
624 SSH_CHANNEL_REQ_STATE_ACCEPTED,
625 /** A request has been replied and refused */
626 SSH_CHANNEL_REQ_STATE_DENIED,
627 /** A request has been replied and an error happend */
628 SSH_CHANNEL_REQ_STATE_ERROR
629};
630
631enum ssh_channel_state_e {
632 SSH_CHANNEL_STATE_NOT_OPEN = 0,
633 SSH_CHANNEL_STATE_OPENING,
634 SSH_CHANNEL_STATE_OPEN_DENIED,
635 SSH_CHANNEL_STATE_OPEN,
636 SSH_CHANNEL_STATE_CLOSED
637};
638
639/* The channel has been closed by the remote side */
640#define SSH_CHANNEL_FLAG_CLOSED_REMOTE 0x0001
641
642/* The channel has been closed locally */
643#define SSH_CHANNEL_FLAG_CLOSED_LOCAL 0x0002
644
645/* The channel has been freed by the calling program */
646#define SSH_CHANNEL_FLAG_FREED_LOCAL 0x0004
647
648/* the channel has not yet been bound to a remote one */
649#define SSH_CHANNEL_FLAG_NOT_BOUND 0x0008
650
651struct ssh_channel_struct {
652 ssh_session session; /* SSH_SESSION pointer */
653 uint32_t local_channel;
654 uint32_t local_window;
655 int local_eof;
656 uint32_t local_maxpacket;
657
658 uint32_t remote_channel;
659 uint32_t remote_window;
660 int remote_eof; /* end of file received */
661 uint32_t remote_maxpacket;
662 enum ssh_channel_state_e state;
663 int delayed_close;
664 int flags;
665 ssh_buffer stdout_buffer;
666 ssh_buffer stderr_buffer;
667 void *userarg;
668 int exit_status;
669 enum ssh_channel_request_state_e request_state;
670 struct ssh_list *callbacks; /* list of ssh_channel_callbacks */
671
672 /* counters */
673 ssh_counter counter;
674};
675#endif /* VBOX_CTUN_COALESCED_FRAME_DETECTION */
676
677/**
678 * Worker function for delivering receive packets to the attached device.
679 *
680 * @param pThis Pointer to the cloud tunnel instance.
681 * @param pbData Packet data.
682 * @param u32Len Packet length.
683 * @thread Dev
684 */
685static DECLCALLBACK(void) drvCloudTunnelReceiveWorker(PDRVCLOUDTUNNEL pThis, uint8_t *pbData, uint32_t u32Len)
686{
687 AssertPtrReturnVoid(pbData);
688 AssertReturnVoid(u32Len!=0);
689
690 STAM_PROFILE_START(&pThis->StatDevRecv, a);
691
692 Log2(("%s: waiting until device is ready to receive...\n", pThis->pszInstance));
693 STAM_PROFILE_START(&pThis->StatDevRecvWait, b);
694 int rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
695 STAM_PROFILE_STOP(&pThis->StatDevRecvWait, b);
696
697 if (RT_SUCCESS(rc))
698 {
699 Log2(("%s: delivering %u-byte packet to attached device...\n", pThis->pszInstance, u32Len));
700 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pbData, u32Len);
701 AssertRC(rc);
702 }
703
704 RTMemFree(pbData);
705 STAM_PROFILE_STOP(&pThis->StatDevRecv, a);
706 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
707}
708
709static int drvCloudTunnelReceiveCallback(ssh_session session, ssh_channel channel, void* data, uint32_t len, int is_stderr, void* userdata)
710{
711 RT_NOREF(session);
712 PDRVCLOUDTUNNEL pThis = (PDRVCLOUDTUNNEL)userdata;
713
714 Log2(("drvCloudTunnelReceiveCallback: len=%d is_stderr=%s\n", len, is_stderr ? "true" : "false"));
715 if (ASMAtomicReadBool(&pThis->fLinkDown))
716 {
717 Log2(("drvCloudTunnelReceiveCallback: ignoring packet as the link is down\n"));
718 return len;
719 }
720
721#ifdef VBOX_CTUN_COALESCED_FRAME_DETECTION
722 if (channel->stdout_buffer->data != data)
723 LogRel(("drvCloudTunnelReceiveCallback: coalesced frames!\n"));
724#endif /* VBOX_CTUN_COALESCED_FRAME_DETECTION */
725
726 if (is_stderr)
727 {
728 LogRel(("%s: [REMOTE] %.*s", pThis->pszInstance, len, data));
729 return 0;
730 }
731
732 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
733
734 if (pThis->iSshVerbosity >= SSH_LOG_PACKET)
735 Log2(("%.*Rhxd\n", len, data));
736
737 /** @todo Validate len! */
738 void *pvPacket = RTMemDup(data, len);
739 if (!pvPacket)
740 {
741 LogRel(("%s: failed to allocate %d bytes\n", pThis->pszInstance, len));
742 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
743 return len;
744 }
745 int rc = RTReqQueueCallEx(pThis->hDevReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
746 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
747 (PFNRT)drvCloudTunnelReceiveWorker, 3, pThis, pvPacket, len);
748 if (RT_FAILURE(rc))
749 {
750 LogRel(("%s: failed to enqueue device request - %Rrc\n", pThis->pszInstance, rc));
751 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
752 }
753
754 return len;
755}
756
757static int channelWriteWontblockCallback(ssh_session, ssh_channel, size_t, void *)
758{
759 return 0;
760}
761
762
763
764/**
765 * This thread feeds the attached device with the packets received from the tunnel.
766 *
767 * This thread is needed because we cannot block I/O thread waiting for the attached
768 * device to become ready to receive packets coming from the tunnel.
769 */
770static DECLCALLBACK(int) drvCloudTunnelDevThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
771{
772 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
773
774 LogFlow(("%s: device thread %p started\n", pThis->pszInstance, pThread));
775
776 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
777 return VINF_SUCCESS;
778
779 /*
780 * Request processing loop.
781 */
782 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
783 {
784 int rc = RTReqQueueProcess(pThis->hDevReqQueue, RT_INDEFINITE_WAIT);
785 Log2(("drvCloudTunnelDevThread: RTReqQueueProcess returned '%Rrc'\n", rc));
786 if (RT_FAILURE(rc))
787 LogRel(("%s: failed to process device request with '%Rrc'\n", pThis->pszInstance, rc));
788 }
789
790 LogFlow(("%s: device thread %p terminated\n", pThis->pszInstance, pThread));
791 return VINF_SUCCESS;
792}
793
794
795static DECLCALLBACK(int) drvCloudTunnelReceiveWakeup(PDRVCLOUDTUNNEL pThis)
796{
797 NOREF(pThis);
798 /* Returning a VINF_* will cause RTReqQueueProcess return. */
799 return VWRN_STATE_CHANGED;
800}
801
802/**
803 * Unblock the I/O thread so it can respond to a state change.
804 *
805 * @returns VBox status code.
806 * @param pDevIns The pcnet device instance.
807 * @param pThread The send thread.
808 */
809static DECLCALLBACK(int) drvCloudTunnelDevWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
810{
811 RT_NOREF(pThread);
812 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
813 LogFlow(("%s: waking up device thread %p...\n", pThis->pszInstance, pThread));
814
815 /* Wake up device thread. */
816 PRTREQ pReq;
817 int rc = RTReqQueueCall(pThis->hDevReqQueue, &pReq, 10000 /*cMillies*/,
818 (PFNRT)drvCloudTunnelReceiveWakeup, 1, pThis);
819 if (RT_FAILURE(rc))
820 LogRel(("%s: failed to wake up device thread - %Rrc\n", pThis->pszInstance, rc));
821 if (RT_SUCCESS(rc))
822 RTReqRelease(pReq);
823
824 return rc;
825}
826
827#define DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE 1024
828#define DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE 65536
829
830static int drvCloudTunnelExecuteRemoteCommandNoOutput(PDRVCLOUDTUNNEL pThis, const char *pcszCommand, ...)
831{
832 va_list va;
833 va_start(va, pcszCommand);
834
835 size_t cb = RTStrPrintfV(pThis->pszCommandBuffer, DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE, pcszCommand, va);
836 if (cb == 0)
837 {
838 Log(("%s: Failed to process '%s'\n", pThis->pszInstance, pcszCommand));
839 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
840 N_("Failed to compose command line"));
841 }
842
843 LogFlow(("%s: [REMOTE] executing '%s'...\n", pThis->pszInstance, pThis->pszCommandBuffer));
844
845 ssh_channel channel = ssh_channel_new(pThis->pSshSession);
846 if (channel == NULL)
847 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
848 N_("Failed to allocate new channel"));
849
850 int rc = ssh_channel_open_session(channel);
851 if (rc != SSH_OK)
852 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
853 N_("Failed to open session channel"));
854 else
855 {
856 rc = ssh_channel_request_exec(channel, pThis->pszCommandBuffer);
857 if (rc != SSH_OK)
858 {
859 LogRel(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
860 Log(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
861 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
862 N_("Execute request failed with %d"), rc);
863 }
864 ssh_channel_close(channel);
865 }
866 ssh_channel_free(channel);
867
868 return VINF_SUCCESS;
869}
870
871
872static int drvCloudTunnelExecuteRemoteCommand(PDRVCLOUDTUNNEL pThis, const char *pcszCommand, ...)
873{
874 va_list va;
875 va_start(va, pcszCommand);
876
877 size_t cb = RTStrPrintfV(pThis->pszCommandBuffer, DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE, pcszCommand, va);
878 if (cb == 0)
879 {
880 Log(("%s: Failed to process '%s'\n", pThis->pszInstance, pcszCommand));
881 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
882 N_("Failed to compose command line"));
883 }
884
885 LogFlow(("%s: [REMOTE] executing '%s'...\n", pThis->pszInstance, pThis->pszCommandBuffer));
886
887 ssh_channel channel = ssh_channel_new(pThis->pSshSession);
888 if (channel == NULL)
889 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
890 N_("Failed to allocate new channel"));
891
892 int rc = ssh_channel_open_session(channel);
893 if (rc != SSH_OK)
894 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
895 N_("Failed to open session channel"));
896 else
897 {
898 rc = ssh_channel_request_exec(channel, pThis->pszCommandBuffer);
899 if (rc != SSH_OK)
900 {
901 LogRel(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
902 Log(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
903 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
904 N_("Execute request failed with %d"), rc);
905 }
906 else
907 {
908 int cbSpaceLeft = DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE;
909 int cbStdOut = 0;
910 char *pszBuffer = pThis->pszOutputBuffer;
911 int cBytes = ssh_channel_read_timeout(channel, pszBuffer, cbSpaceLeft, 0, 60000 /* ms */); /* Is 60 seconds really enough? */
912 while (cBytes > 0)
913 {
914 cbStdOut += cBytes;
915 pszBuffer += cBytes;
916 cbSpaceLeft -= cBytes;
917 if (cbSpaceLeft <= 0)
918 break;
919 cBytes = ssh_channel_read_timeout(channel, pszBuffer, cbSpaceLeft, 0, 60000 /* ms */); /* Is 60 seconds really enough? */
920 }
921 if (cBytes < 0)
922 {
923 LogRel(("%s: while executing '%s' ssh_channel_read_timeout returned error\n", pThis->pszInstance, pThis->pszCommandBuffer));
924 Log(("%s: while executing '%s' ssh_channel_read_timeout returned error\n", pThis->pszInstance, pThis->pszCommandBuffer));
925 rc = VERR_INTERNAL_ERROR;
926 }
927 else
928 {
929 /* Make sure the buffer is terminated. */
930 if (cbStdOut < DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE)
931 if (cbStdOut > 1 && pThis->pszOutputBuffer[cbStdOut - 1] == '\n')
932 pThis->pszOutputBuffer[cbStdOut - 1] = 0; /* Trim newline */
933 else
934 pThis->pszOutputBuffer[cbStdOut] = 0;
935 else
936 pThis->pszOutputBuffer[DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE - 1] = 0; /* No choice but to eat up last character. Could have returned warning though. */
937 if (cbStdOut == 0)
938 Log(("%s: received no output from remote console\n", pThis->pszInstance));
939 else
940 Log(("%s: received output from remote console:\n%s\n", pThis->pszInstance, pThis->pszOutputBuffer));
941 rc = VINF_SUCCESS;
942
943 char *pszErrorBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE);
944 if (pszErrorBuffer == NULL)
945 {
946 LogRel(("%s: Failed to allocate error buffer\n", pThis->pszInstance));
947 rc = VERR_INTERNAL_ERROR;
948 }
949 else
950 {
951 /* Report errors if there were any */
952 cBytes = ssh_channel_read_timeout(channel, pszErrorBuffer, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE, 1, 0); /* Peek at stderr */
953 if (cBytes > 0)
954 {
955 LogRel(("%s: WARNING! While executing '%s' remote console reported errors:\n", pThis->pszInstance, pThis->pszCommandBuffer));
956 Log(("%s: WARNING! While executing '%s' remote console reported errors:\n", pThis->pszInstance, pThis->pszCommandBuffer));
957 }
958 while (cBytes > 0)
959 {
960 LogRel(("%.*s", cBytes, pszErrorBuffer));
961 Log(("%.*s", cBytes, pszErrorBuffer));
962 cBytes = ssh_channel_read_timeout(channel, pszErrorBuffer, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE, 1, 1000); /* Wait for a second for more error output */
963 }
964 RTMemFree(pszErrorBuffer);
965 }
966 }
967 ssh_channel_send_eof(channel);
968 }
969 ssh_channel_close(channel);
970 }
971 ssh_channel_free(channel);
972
973 return VINF_SUCCESS;
974}
975
976
977static int drvCloudTunnelCloudInstanceInitialConfig(PDRVCLOUDTUNNEL pThis)
978{
979 LogFlow(("%s: configuring cloud instance...\n", pThis->pszInstance));
980
981 int rc = drvCloudTunnelExecuteRemoteCommand(pThis, "python3 -c \"from oci_utils.vnicutils import VNICUtils; cfg = VNICUtils().get_network_config(); print('CONFIG:', [i['IFACE'] for i in cfg if 'IS_PRIMARY' in i][0], [i['IFACE']+' '+i['VIRTRT'] for i in cfg if not 'IS_PRIMARY' in i][0])\"");
982 if (RT_FAILURE(rc))
983 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
984 N_("Failed to get network config via console channel"));
985 else
986 {
987 char *pszConfig = RTStrStr(pThis->pszOutputBuffer, "CONFIG: ");
988 if (!pszConfig)
989 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
990 N_("Failed to parse network config"));
991 else
992 {
993 char **ppapszTokens;
994 size_t cTokens;
995 rc = RTStrSplit(pszConfig + 8, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE - (pszConfig - pThis->pszOutputBuffer) - 8,
996 " ", &ppapszTokens, &cTokens);
997 if (RT_SUCCESS(rc))
998 {
999 /*
1000 * There should be exactly three tokens:
1001 * 1) Primary network interface name;
1002 * 2) Secondary network interface name;
1003 * 3) Secondary network gateway address.
1004 */
1005 if (cTokens != 3)
1006 Log(("%s: Got %u tokes instead of three while parsing '%s'\n", pThis->pszInstance, cTokens, pThis->pszOutputBuffer));
1007 else
1008 {
1009 char *pszSecondaryInterface = NULL;
1010 char *pszSecondaryGateway = NULL;
1011
1012 if (pThis->pszCloudPrimaryInterface)
1013 RTStrFree(pThis->pszCloudPrimaryInterface);
1014 pThis->pszCloudPrimaryInterface = RTStrDup(ppapszTokens[0]);
1015 pszSecondaryInterface = ppapszTokens[1];
1016 pszSecondaryGateway = ppapszTokens[2];
1017 Log(("%s: primary=%s secondary=%s gateway=%s\n", pThis->pszInstance, pThis->pszCloudPrimaryInterface, pszSecondaryInterface, pszSecondaryGateway));
1018
1019 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo oci-network-config -c");
1020 if (RT_SUCCESS(rc))
1021 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip tuntap add dev tap0 mod tap user opc");
1022 if (RT_SUCCESS(rc))
1023 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo sh -c 'echo \"PermitTunnel yes\" >> /etc/ssh/sshd_config'");
1024 if (RT_SUCCESS(rc))
1025 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo kill -SIGHUP $(pgrep -f \"sshd -D\")");
1026 if (RT_SUCCESS(rc))
1027 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link add name br0 type bridge");
1028 if (RT_SUCCESS(rc))
1029 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev tap0 master br0");
1030 if (RT_SUCCESS(rc))
1031 rc = drvCloudTunnelExecuteRemoteCommandNoOutput(pThis, "sudo ip route change default via %s dev %s", pszSecondaryGateway, pszSecondaryInterface);
1032 if (RT_FAILURE(rc))
1033 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1034 N_("Failed to execute network config command via console channel"));
1035 }
1036
1037 for (size_t i = 0; i < cTokens; i++)
1038 RTStrFree(ppapszTokens[i]);
1039 RTMemFree(ppapszTokens);
1040 }
1041 }
1042 }
1043
1044 return rc;
1045}
1046
1047
1048static int drvCloudTunnelCloudInstanceFinalConfig(PDRVCLOUDTUNNEL pThis)
1049{
1050 if (pThis->pszCloudPrimaryInterface == NULL)
1051 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1052 N_("Failed to finalize cloud instance config because of unknown primary interface name!"));
1053
1054 LogFlow(("%s: finalizing cloud instance configuration...\n", pThis->pszInstance));
1055
1056 int rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s down", pThis->pszCloudPrimaryInterface);
1057 if (RT_SUCCESS(rc))
1058 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s address %RTmac", pThis->pszCloudPrimaryInterface, pThis->targetMac.au8);
1059 if (RT_SUCCESS(rc))
1060 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ifconfig %s 0.0.0.0", pThis->pszCloudPrimaryInterface); /* Make sure no IP is configured on primary */
1061 if (RT_SUCCESS(rc))
1062 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s master br0", pThis->pszCloudPrimaryInterface);
1063 if (RT_SUCCESS(rc))
1064 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s up", pThis->pszCloudPrimaryInterface);
1065 if (RT_SUCCESS(rc))
1066 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev tap0 up");
1067 if (RT_SUCCESS(rc))
1068 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev br0 up");
1069 if (RT_FAILURE(rc))
1070 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1071 N_("Failed to execute network config command via console channel"));
1072
1073 return rc;
1074}
1075
1076
1077static int drvCloudTunnelOpenTunnelChannel(PDRVCLOUDTUNNEL pThis)
1078{
1079 LogFlow(("%s: opening tunnel channel...\n", pThis->pszInstance));
1080 pThis->pSshChannel = ssh_channel_new(pThis->pSshSession);
1081 if (pThis->pSshChannel == NULL)
1082 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1083 N_("Failed to allocate new channel"));
1084 int rc = ssh_channel_open_tunnel(pThis->pSshChannel, 0);
1085 if (rc < 0)
1086 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1087 N_("Failed to open tunnel channel"));
1088 else
1089 {
1090 /* Set packet receive callback. */
1091 rc = ssh_set_channel_callbacks(pThis->pSshChannel, &pThis->Callbacks);
1092 if (rc != SSH_OK)
1093 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1094 N_("Failed to set packet receive callback"));
1095 }
1096
1097 return rc;
1098}
1099
1100
1101static void closeTunnelChannel(PDRVCLOUDTUNNEL pThis)
1102{
1103 if (pThis->pSshChannel)
1104 {
1105 LogFlow(("%s: closing tunnel channel %p\n", pThis->pszInstance, pThis->pSshChannel));
1106 ssh_channel_close(pThis->pSshChannel);
1107 ssh_channel_free(pThis->pSshChannel);
1108 pThis->pSshChannel = NULL;
1109 }
1110}
1111
1112
1113static int drvCloudTunnelStartIoThread(PDRVCLOUDTUNNEL pThis)
1114{
1115 LogFlow(("%s: starting I/O thread...\n", pThis->pszInstance));
1116 int rc = createConnectedSockets(pThis);
1117 if (RT_FAILURE(rc))
1118 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1119 N_("CloudTunnel: Failed to create a pair of connected sockets"));
1120
1121 /*
1122 * Start the cloud I/O thread.
1123 */
1124 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pIoThread,
1125 pThis, drvCloudTunnelIoThread, drvCloudTunnelIoWakeup,
1126 64 * _1K, RTTHREADTYPE_IO, pThis->pszInstanceIo);
1127 if (RT_FAILURE(rc))
1128 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1129 N_("CloudTunnel: Failed to start I/O thread"));
1130
1131 return rc;
1132}
1133
1134static void drvCloudTunnelStopIoThread(PDRVCLOUDTUNNEL pThis)
1135{
1136 if (pThis->pIoThread)
1137 {
1138 LogFlow(("%s: stopping I/O thread...\n", pThis->pszInstance));
1139 int rc = PDMDrvHlpThreadDestroy(pThis->pDrvIns, pThis->pIoThread, NULL);
1140 AssertRC(rc);
1141 pThis->pIoThread = NULL;
1142 }
1143 destroyConnectedSockets(pThis);
1144
1145}
1146
1147static int destroyTunnel(PDRVCLOUDTUNNEL pThis)
1148{
1149 if (pThis->pSshChannel)
1150 {
1151 int rc = ssh_remove_channel_callbacks(pThis->pSshChannel, &pThis->Callbacks);
1152 if (rc != SSH_OK)
1153 LogRel(("%s: WARNING! Failed to remove tunnel channel callbacks.\n", pThis->pszInstance));
1154 }
1155 drvCloudTunnelStopIoThread(pThis);
1156 closeTunnelChannel(pThis);
1157 ssh_disconnect(pThis->pSshSession);
1158 ssh_free(pThis->pSshSession);
1159 pThis->pSshSession = NULL;
1160 return VINF_SUCCESS;
1161}
1162
1163
1164static int drvCloudTunnelNewSession(PDRVCLOUDTUNNEL pThis, bool fPrimary)
1165{
1166 pThis->pSshSession = ssh_new();
1167 if (pThis->pSshSession == NULL)
1168 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1169 N_("CloudTunnel: Failed to allocate new SSH session"));
1170 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_LOG_VERBOSITY, &pThis->iSshVerbosity) < 0)
1171 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1172 N_("Failed to set SSH_OPTIONS_LOG_VERBOSITY"));
1173 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_USER, pThis->pszUser) < 0)
1174 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1175 N_("Failed to set SSH_OPTIONS_USER"));
1176 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_HOST, fPrimary ? pThis->pszPrimaryIP : pThis->pszSecondaryIP) < 0)
1177 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1178 N_("Failed to set SSH_OPTIONS_HOST"));
1179
1180 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_TIMEOUT, &pThis->ulTimeoutInSecounds) < 0)
1181 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1182 N_("Failed to set SSH_OPTIONS_TIMEOUT"));
1183
1184 const char *pcszProxyType = fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType;
1185 if (pcszProxyType)
1186 {
1187 char szProxyCmd[1024];
1188
1189 const char *pcszProxyUser = fPrimary ? pThis->pszPrimaryProxyUser : pThis->pszSecondaryProxyUser;
1190 if (pcszProxyUser)
1191 RTStrPrintf(szProxyCmd, sizeof(szProxyCmd), "#VBoxProxy%s %s %u %s %s",
1192 fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType,
1193 fPrimary ? pThis->pszPrimaryProxyHost : pThis->pszSecondaryProxyHost,
1194 fPrimary ? pThis->u16PrimaryProxyPort : pThis->u16SecondaryProxyPort,
1195 fPrimary ? pThis->pszPrimaryProxyUser : pThis->pszSecondaryProxyUser,
1196 fPrimary ? pThis->pszPrimaryProxyPassword : pThis->pszSecondaryProxyPassword);
1197 else
1198 RTStrPrintf(szProxyCmd, sizeof(szProxyCmd), "#VBoxProxy%s %s %u",
1199 fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType,
1200 fPrimary ? pThis->pszPrimaryProxyHost : pThis->pszSecondaryProxyHost,
1201 fPrimary ? pThis->u16PrimaryProxyPort : pThis->u16SecondaryProxyPort);
1202 LogRel(("%s: using proxy command '%s'\n", pThis->pszInstance, szProxyCmd));
1203 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_PROXYCOMMAND, szProxyCmd) < 0)
1204 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1205 N_("Failed to set SSH_OPTIONS_PROXYCOMMAND"));
1206 }
1207
1208 int rc = ssh_connect(pThis->pSshSession);
1209 for (int cAttempt = 1; rc != SSH_OK && cAttempt <= 5; cAttempt++)
1210 {
1211 ssh_disconnect(pThis->pSshSession);
1212 /* One more time, just to be sure. */
1213 LogRel(("%s: failed to connect to %s, retrying(#%d)...\n", pThis->pszInstance,
1214 fPrimary ? pThis->pszPrimaryIP : pThis->pszSecondaryIP, cAttempt));
1215 RTThreadSleep(10000); /* Sleep 10 seconds, then retry */
1216 rc = ssh_connect(pThis->pSshSession);
1217 }
1218 if (rc != SSH_OK)
1219 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1220 N_("CloudTunnel: Failed to connect to %s interface"), fPrimary ? "primary" : "secondary");
1221
1222 rc = ssh_userauth_publickey(pThis->pSshSession, NULL, pThis->SshKey);
1223 if (rc != SSH_AUTH_SUCCESS)
1224 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1225 N_("Failed to authenticate with public key"));
1226
1227 return VINF_SUCCESS;
1228}
1229
1230static int drvCloudTunnelSwitchToSecondary(PDRVCLOUDTUNNEL pThis)
1231{
1232 int rc = drvCloudTunnelNewSession(pThis, true /* fPrimary */);
1233 /*
1234 * Establish temporary console channel and configure the cloud instance
1235 * to bridge the tunnel channel to instance's primary interface.
1236 */
1237 if (RT_SUCCESS(rc))
1238 rc = drvCloudTunnelCloudInstanceInitialConfig(pThis);
1239
1240 ssh_disconnect(pThis->pSshSession);
1241 ssh_free(pThis->pSshSession);
1242 pThis->pSshSession = NULL;
1243
1244 return rc;
1245}
1246
1247
1248static int establishTunnel(PDRVCLOUDTUNNEL pThis)
1249{
1250 int rc = drvCloudTunnelNewSession(pThis, false /* fPrimary */);
1251 if (RT_SUCCESS(rc))
1252 rc = drvCloudTunnelCloudInstanceFinalConfig(pThis);
1253 if (RT_SUCCESS(rc))
1254 rc = drvCloudTunnelOpenTunnelChannel(pThis);
1255 if (RT_SUCCESS(rc))
1256 rc = drvCloudTunnelStartIoThread(pThis);
1257 if (RT_FAILURE(rc))
1258 {
1259 destroyTunnel(pThis);
1260 return rc;
1261 }
1262
1263 return rc;
1264}
1265
1266
1267static DECL_NOTHROW(void) drvCloudTunnelSshLogCallback(int priority, const char *function, const char *buffer, void *userdata)
1268{
1269 PDRVCLOUDTUNNEL pThis = (PDRVCLOUDTUNNEL)userdata;
1270#ifdef LOG_ENABLED
1271 const char *pcszVerbosity;
1272 switch (priority)
1273 {
1274 case SSH_LOG_WARNING:
1275 pcszVerbosity = "WARNING";
1276 break;
1277 case SSH_LOG_PROTOCOL:
1278 pcszVerbosity = "PROTOCOL";
1279 break;
1280 case SSH_LOG_PACKET:
1281 pcszVerbosity = "PACKET";
1282 break;
1283 case SSH_LOG_FUNCTIONS:
1284 pcszVerbosity = "FUNCTIONS";
1285 break;
1286 default:
1287 pcszVerbosity = "UNKNOWN";
1288 break;
1289 }
1290 Log3(("%s: SSH-%s: %s: %s\n", pThis->pszInstance, pcszVerbosity, function, buffer));
1291#else
1292 RT_NOREF(priority);
1293 LogRel(("%s: SSH %s: %s\n", pThis->pszInstance, function, buffer));
1294#endif
1295}
1296
1297/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
1298
1299DECLINLINE(void) drvCloudTunnelStrFree(char **ppszString)
1300{
1301 if (*ppszString)
1302 {
1303 RTStrFree(*ppszString);
1304 *ppszString = NULL;
1305 }
1306}
1307
1308DECLINLINE(void) drvCloudTunnelHeapFree(PPDMDRVINS pDrvIns, char **ppszString)
1309{
1310 if (*ppszString)
1311 {
1312 PDMDrvHlpMMHeapFree(pDrvIns, *ppszString);
1313 *ppszString = NULL;
1314 }
1315}
1316
1317/**
1318 * Destruct a driver instance.
1319 *
1320 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1321 * resources can be freed correctly.
1322 *
1323 * @param pDrvIns The driver instance data.
1324 */
1325static DECLCALLBACK(void) drvCloudTunnelDestruct(PPDMDRVINS pDrvIns)
1326{
1327 LogFlowFunc(("\n"));
1328 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
1329 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1330
1331 ASMAtomicXchgSize(&pThis->fLinkDown, true);
1332
1333 destroyTunnel(pThis);
1334
1335 if (pThis->hIoReqQueue != NIL_RTREQQUEUE)
1336 {
1337 RTReqQueueDestroy(pThis->hIoReqQueue);
1338 pThis->hIoReqQueue = NIL_RTREQQUEUE;
1339 }
1340
1341 drvCloudTunnelStrFree(&pThis->pszCloudPrimaryInterface);
1342
1343 drvCloudTunnelHeapFree(pDrvIns, &pThis->pszPrimaryProxyType);
1344 drvCloudTunnelStrFree(&pThis->pszPrimaryProxyHost);
1345 drvCloudTunnelHeapFree(pDrvIns, &pThis->pszPrimaryProxyUser);
1346 drvCloudTunnelStrFree(&pThis->pszPrimaryProxyPassword);
1347
1348 drvCloudTunnelHeapFree(pDrvIns, &pThis->pszSecondaryProxyType);
1349 drvCloudTunnelStrFree(&pThis->pszSecondaryProxyHost);
1350 drvCloudTunnelHeapFree(pDrvIns, &pThis->pszSecondaryProxyUser);
1351 drvCloudTunnelStrFree(&pThis->pszSecondaryProxyPassword);
1352
1353 drvCloudTunnelStrFree(&pThis->pszSecondaryIP);
1354 drvCloudTunnelStrFree(&pThis->pszPrimaryIP);
1355 drvCloudTunnelStrFree(&pThis->pszUser);
1356
1357 drvCloudTunnelStrFree(&pThis->pszInstanceDev);
1358 drvCloudTunnelStrFree(&pThis->pszInstanceIo);
1359 drvCloudTunnelStrFree(&pThis->pszInstance);
1360
1361 drvCloudTunnelStrFree(&pThis->pszOutputBuffer);
1362 drvCloudTunnelStrFree(&pThis->pszCommandBuffer);
1363
1364 ssh_key_free(pThis->SshKey);
1365
1366 ssh_finalize();
1367 //OPENSSL_cleanup();
1368
1369 // if (pThis->pServer)
1370 // {
1371 // RTUdpServerDestroy(pThis->pServer);
1372 // pThis->pServer = NULL;
1373 // }
1374
1375 /*
1376 * Kill the xmit lock.
1377 */
1378 if (RTCritSectIsInitialized(&pThis->XmitLock))
1379 RTCritSectDelete(&pThis->XmitLock);
1380
1381#ifdef VBOX_WITH_STATISTICS
1382 /*
1383 * Deregister statistics.
1384 */
1385 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSent);
1386 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSentBytes);
1387 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecv);
1388 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecvBytes);
1389 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
1390 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
1391 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatDevRecv);
1392 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatDevRecvWait);
1393#endif /* VBOX_WITH_STATISTICS */
1394}
1395
1396
1397/**
1398 * Construct a Cloud tunnel network transport driver instance.
1399 *
1400 * @copydoc FNPDMDRVCONSTRUCT
1401 */
1402static DECLCALLBACK(int) drvCloudTunnelConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1403{
1404 RT_NOREF(fFlags);
1405 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1406 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
1407 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1408
1409 /*
1410 * Init the static parts.
1411 */
1412 pThis->pDrvIns = pDrvIns;
1413 pThis->pszCommandBuffer = NULL;
1414 pThis->pszOutputBuffer = NULL;
1415 pThis->pszInstance = NULL;
1416 pThis->pszPrimaryIP = NULL;
1417 pThis->pszSecondaryIP = NULL;
1418 pThis->pszUser = NULL;
1419 pThis->SshKey = 0;
1420
1421 /* IBase */
1422 pDrvIns->IBase.pfnQueryInterface = drvCloudTunnelQueryInterface;
1423 /* INetwork */
1424 pThis->INetworkUp.pfnBeginXmit = drvCloudTunnelUp_BeginXmit;
1425 pThis->INetworkUp.pfnAllocBuf = drvCloudTunnelUp_AllocBuf;
1426 pThis->INetworkUp.pfnFreeBuf = drvCloudTunnelUp_FreeBuf;
1427 pThis->INetworkUp.pfnSendBuf = drvCloudTunnelUp_SendBuf;
1428 pThis->INetworkUp.pfnEndXmit = drvCloudTunnelUp_EndXmit;
1429 pThis->INetworkUp.pfnSetPromiscuousMode = drvCloudTunnelUp_SetPromiscuousMode;
1430 pThis->INetworkUp.pfnNotifyLinkChanged = drvCloudTunnelUp_NotifyLinkChanged;
1431
1432 /* ??? */
1433 pThis->iSocketIn = INVALID_SOCKET;
1434 pThis->iSocketOut = INVALID_SOCKET;
1435 pThis->pSshSession = 0;
1436 pThis->pSshChannel = 0;
1437
1438 pThis->pDevThread = 0;
1439 pThis->pIoThread = 0;
1440 pThis->hIoReqQueue = NIL_RTREQQUEUE;
1441
1442 pThis->fLinkDown = false;
1443
1444 pThis->pszCloudPrimaryInterface = NULL;
1445
1446#ifdef VBOX_WITH_STATISTICS
1447 /*
1448 * Statistics.
1449 */
1450 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/CloudTunnel%d/Packets/Sent", pDrvIns->iInstance);
1451 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/CloudTunnel%d/Bytes/Sent", pDrvIns->iInstance);
1452 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/CloudTunnel%d/Packets/Received", pDrvIns->iInstance);
1453 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/CloudTunnel%d/Bytes/Received", pDrvIns->iInstance);
1454 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/CloudTunnel%d/Transmit", pDrvIns->iInstance);
1455 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/CloudTunnel%d/Receive", pDrvIns->iInstance);
1456 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatDevRecv, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling device receive runs.", "/Drivers/CloudTunnel%d/DeviceReceive", pDrvIns->iInstance);
1457 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatDevRecvWait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling device receive waits.", "/Drivers/CloudTunnel%d/DeviceReceiveWait", pDrvIns->iInstance);
1458#endif /* VBOX_WITH_STATISTICS */
1459
1460 /*
1461 * Validate the config.
1462 */
1463 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "SshKey"
1464 "|PrimaryIP"
1465 "|SecondaryIP"
1466 "|TargetMAC"
1467
1468 "|PrimaryProxyType"
1469 "|PrimaryProxyHost"
1470 "|PrimaryProxyPort"
1471 "|PrimaryProxyUser"
1472 "|PrimaryProxyPassword"
1473 "|SecondaryProxyType"
1474 "|SecondaryProxyHost"
1475 "|SecondaryProxyPort"
1476 "|SecondaryProxyUser"
1477 "|SecondaryProxyPassword"
1478
1479 ,"");
1480
1481 /*
1482 * Check that no-one is attached to us.
1483 */
1484 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1485 ("Configuration error: Not possible to attach anything to this driver!\n"),
1486 VERR_PDM_DRVINS_NO_ATTACH);
1487
1488 /*
1489 * Query the network port interface.
1490 */
1491 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1492 if (!pThis->pIAboveNet)
1493 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1494 N_("Configuration error: The above device/driver didn't export the network port interface"));
1495
1496 /*
1497 * Read the configuration.
1498 */
1499 int rc;
1500
1501 char szVal[2048];
1502 RTNETADDRIPV4 tmpAddr;
1503 rc = pHlp->pfnCFGMQueryString(pCfg, "PrimaryIP", szVal, sizeof(szVal));
1504 if (RT_FAILURE(rc))
1505 return PDMDRV_SET_ERROR(pDrvIns, rc,
1506 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryIP\" as string failed"));
1507 rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
1508 if (RT_FAILURE(rc))
1509 return PDMDRV_SET_ERROR(pDrvIns, rc,
1510 N_("DrvCloudTunnel: Configuration error: \"PrimaryIP\" is not valid"));
1511 else
1512 pThis->pszPrimaryIP = RTStrDup(szVal);
1513
1514 rc = pHlp->pfnCFGMQueryString(pCfg, "SecondaryIP", szVal, sizeof(szVal));
1515 if (RT_FAILURE(rc))
1516 return PDMDRV_SET_ERROR(pDrvIns, rc,
1517 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryIP\" as string failed"));
1518 rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
1519 if (RT_FAILURE(rc))
1520 return PDMDRV_SET_ERROR(pDrvIns, rc,
1521 N_("DrvCloudTunnel: Configuration error: \"SecondaryIP\" is not valid"));
1522 else
1523 pThis->pszSecondaryIP = RTStrDup(szVal);
1524 rc = pHlp->pfnCFGMQueryBytes(pCfg, "TargetMAC", pThis->targetMac.au8, sizeof(pThis->targetMac.au8));
1525 if (RT_FAILURE(rc))
1526 return PDMDRV_SET_ERROR(pDrvIns, rc,
1527 N_("DrvCloudTunnel: Configuration error: Failed to get target MAC address"));
1528 /** @todo In the near future we will want to include proxy settings here! */
1529 // Do we want to pass the user name via CFGM?
1530 pThis->pszUser = RTStrDup("opc");
1531 // Is it safe to expose verbosity via CFGM?
1532#ifdef LOG_ENABLED
1533 pThis->iSshVerbosity = SSH_LOG_PACKET; //SSH_LOG_FUNCTIONS;
1534#else
1535 pThis->iSshVerbosity = SSH_LOG_WARNING;
1536#endif
1537
1538 pThis->ulTimeoutInSecounds = 30; /* The default 10-second timeout is too short? */
1539
1540 rc = pHlp->pfnCFGMQueryPassword(pCfg, "SshKey", szVal, sizeof(szVal));
1541 if (RT_FAILURE(rc))
1542 return PDMDRV_SET_ERROR(pDrvIns, rc,
1543 N_("DrvCloudTunnel: Configuration error: Querying \"SshKey\" as password failed"));
1544 rc = ssh_pki_import_privkey_base64(szVal, NULL, NULL, NULL, &pThis->SshKey);
1545 RTMemWipeThoroughly(szVal, sizeof(szVal), 10);
1546 if (rc != SSH_OK)
1547 return PDMDRV_SET_ERROR(pDrvIns, VERR_INVALID_BASE64_ENCODING,
1548 N_("DrvCloudTunnel: Configuration error: Converting \"SshKey\" from base64 failed"));
1549
1550 /* PrimaryProxyType is optional */
1551 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "PrimaryProxyType", &pThis->pszPrimaryProxyType, NULL);
1552 if (RT_FAILURE(rc))
1553 return PDMDRV_SET_ERROR(pDrvIns, rc,
1554 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyType\" as string failed"));
1555 if (pThis->pszPrimaryProxyType)
1556 {
1557 rc = pHlp->pfnCFGMQueryString(pCfg, "PrimaryProxyHost", szVal, sizeof(szVal));
1558 if (RT_FAILURE(rc))
1559 return PDMDRV_SET_ERROR(pDrvIns, rc,
1560 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyHost\" as string failed"));
1561 rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
1562 if (RT_FAILURE(rc))
1563 return PDMDRV_SET_ERROR(pDrvIns, rc,
1564 N_("DrvCloudTunnel: Configuration error: \"PrimaryProxyHost\" is not valid"));
1565 else
1566 pThis->pszPrimaryProxyHost = RTStrDup(szVal);
1567
1568 uint64_t u64Val;
1569 rc = pHlp->pfnCFGMQueryInteger(pCfg, "PrimaryProxyPort", &u64Val);
1570 if (RT_FAILURE(rc))
1571 return PDMDRV_SET_ERROR(pDrvIns, rc,
1572 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyPort\" as integer failed"));
1573 if (u64Val > 0xFFFF)
1574 return PDMDRV_SET_ERROR(pDrvIns, rc,
1575 N_("DrvCloudTunnel: Configuration error: \"PrimaryProxyPort\" is not valid"));
1576 pThis->u16PrimaryProxyPort = (uint16_t)u64Val;
1577
1578 /* PrimaryProxyUser is optional */
1579 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "PrimaryProxyUser", &pThis->pszPrimaryProxyUser, NULL);
1580 if (RT_FAILURE(rc))
1581 return PDMDRV_SET_ERROR(pDrvIns, rc,
1582 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyUser\" as string failed"));
1583 /* PrimaryProxyPassword must be present if PrimaryProxyUser is present */
1584 if (pThis->pszPrimaryProxyUser)
1585 {
1586 rc = pHlp->pfnCFGMQueryPassword(pCfg, "PrimaryProxyPassword", szVal, sizeof(szVal));
1587 if (RT_FAILURE(rc))
1588 return PDMDRV_SET_ERROR(pDrvIns, rc,
1589 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyPassword\" as string failed"));
1590 pThis->pszPrimaryProxyPassword = RTStrDup(szVal);
1591 }
1592 }
1593
1594 /* SecondaryProxyType is optional */
1595 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "SecondaryProxyType", &pThis->pszSecondaryProxyType, NULL);
1596 if (RT_FAILURE(rc))
1597 return PDMDRV_SET_ERROR(pDrvIns, rc,
1598 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyType\" as string failed"));
1599 if (pThis->pszSecondaryProxyType)
1600 {
1601 if (RT_FAILURE(rc))
1602 return PDMDRV_SET_ERROR(pDrvIns, rc,
1603 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyType\" as string failed"));
1604
1605 rc = pHlp->pfnCFGMQueryString(pCfg, "SecondaryProxyHost", szVal, sizeof(szVal));
1606 if (RT_FAILURE(rc))
1607 return PDMDRV_SET_ERROR(pDrvIns, rc,
1608 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyHost\" as string failed"));
1609 rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
1610 if (RT_FAILURE(rc))
1611 return PDMDRV_SET_ERROR(pDrvIns, rc,
1612 N_("DrvCloudTunnel: Configuration error: \"SecondaryProxyHost\" is not valid"));
1613 else
1614 pThis->pszSecondaryProxyHost = RTStrDup(szVal);
1615
1616 uint64_t u64Val;
1617 rc = pHlp->pfnCFGMQueryInteger(pCfg, "SecondaryProxyPort", &u64Val);
1618 if (RT_FAILURE(rc))
1619 return PDMDRV_SET_ERROR(pDrvIns, rc,
1620 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyPort\" as integer failed"));
1621 if (u64Val > 0xFFFF)
1622 return PDMDRV_SET_ERROR(pDrvIns, rc,
1623 N_("DrvCloudTunnel: Configuration error: \"SecondaryProxyPort\" is not valid"));
1624 pThis->u16SecondaryProxyPort = (uint16_t)u64Val;
1625
1626 /* SecondaryProxyUser is optional */
1627 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "SecondaryProxyUser", &pThis->pszSecondaryProxyUser, NULL);
1628 if (RT_FAILURE(rc))
1629 return PDMDRV_SET_ERROR(pDrvIns, rc,
1630 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyUser\" as string failed"));
1631 /* SecondaryProxyPassword must be present if SecondaryProxyUser is present */
1632 if (pThis->pszSecondaryProxyUser)
1633 {
1634 rc = pHlp->pfnCFGMQueryPassword(pCfg, "SecondaryProxyPassword", szVal, sizeof(szVal));
1635 if (RT_FAILURE(rc))
1636 return PDMDRV_SET_ERROR(pDrvIns, rc,
1637 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyPassword\" as string failed"));
1638 pThis->pszSecondaryProxyPassword = RTStrDup(szVal);
1639 }
1640 }
1641
1642 pThis->pszCommandBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE);
1643 if (pThis->pszCommandBuffer == NULL)
1644 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_OPEN_FAILED,
1645 N_("DrvCloudTunnel: Failed to allocate command buffer"));
1646 pThis->pszOutputBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE);
1647 if (pThis->pszOutputBuffer == NULL)
1648 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_OPEN_FAILED,
1649 N_("DrvCloudTunnel: Failed to allocate output buffer"));
1650 /*
1651 * Create unique instance name for logging.
1652 */
1653 rc = RTStrAPrintf(&pThis->pszInstance, "CT#%d", pDrvIns->iInstance);
1654 AssertRC(rc);
1655
1656 LogRel(("%s: primary=%s secondary=%s target-mac=%RTmac\n", pThis->pszInstance, pThis->pszPrimaryIP, pThis->pszSecondaryIP, pThis->targetMac.au8));
1657
1658 /*
1659 * Create unique thread name for cloud I/O.
1660 */
1661 rc = RTStrAPrintf(&pThis->pszInstanceIo, "CTunIO%d", pDrvIns->iInstance);
1662 AssertRC(rc);
1663
1664 /*
1665 * Create unique thread name for device receive function.
1666 */
1667 rc = RTStrAPrintf(&pThis->pszInstanceDev, "CTunDev%d", pDrvIns->iInstance);
1668 AssertRC(rc);
1669
1670 /*
1671 * Create the transmit lock.
1672 */
1673 rc = RTCritSectInit(&pThis->XmitLock);
1674 AssertRCReturn(rc, rc);
1675
1676 /*
1677 * Create the request queue for I/O requests.
1678 */
1679 rc = RTReqQueueCreate(&pThis->hIoReqQueue);
1680 AssertLogRelRCReturn(rc, rc);
1681
1682 /*
1683 * Create the request queue for attached device requests.
1684 */
1685 rc = RTReqQueueCreate(&pThis->hDevReqQueue);
1686 AssertLogRelRCReturn(rc, rc);
1687
1688 /*
1689 * Start the device output thread.
1690 */
1691 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pDevThread,
1692 pThis, drvCloudTunnelDevThread, drvCloudTunnelDevWakeup,
1693 64 * _1K, RTTHREADTYPE_IO, pThis->pszInstanceDev);
1694 if (RT_FAILURE(rc))
1695 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1696 N_("CloudTunnel: Failed to start device thread"));
1697
1698 rc = ssh_init();
1699 if (rc != SSH_OK)
1700 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1701 N_("CloudTunnel: Failed to initialize libssh"));
1702
1703 memset(&pThis->Callbacks, 0, sizeof(pThis->Callbacks));
1704#ifdef PACKET_CAPTURE_ENABLED
1705 pThis->Callbacks.channel_data_function = drvCloudTunnelReceiveCallbackWithPacketCapture;
1706#else
1707 pThis->Callbacks.channel_data_function = drvCloudTunnelReceiveCallback;
1708#endif
1709 pThis->Callbacks.userdata = pThis;
1710 pThis->Callbacks.channel_write_wontblock_function = channelWriteWontblockCallback;
1711 ssh_callbacks_init(&pThis->Callbacks);
1712
1713 rc = ssh_set_log_callback(drvCloudTunnelSshLogCallback);
1714 if (rc != SSH_OK)
1715 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1716 N_("CloudTunnel: Failed to set libssh log callback"));
1717 rc = ssh_set_log_userdata(pThis);
1718 if (rc != SSH_OK)
1719 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1720 N_("CloudTunnel: Failed to set libssh log userdata"));
1721
1722 rc = drvCloudTunnelSwitchToSecondary(pThis);
1723 if (RT_SUCCESS(rc))
1724 rc = establishTunnel(pThis);
1725
1726 return rc;
1727}
1728
1729
1730#if 0
1731/**
1732 * Suspend notification.
1733 *
1734 * @param pDrvIns The driver instance.
1735 */
1736static DECLCALLBACK(void) drvCloudTunnelSuspend(PPDMDRVINS pDrvIns)
1737{
1738 LogFlowFunc(("\n"));
1739 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
1740
1741 RT_NOREF(pThis);
1742 // if (pThis->pServer)
1743 // {
1744 // RTUdpServerDestroy(pThis->pServer);
1745 // pThis->pServer = NULL;
1746 // }
1747}
1748
1749
1750/**
1751 * Resume notification.
1752 *
1753 * @param pDrvIns The driver instance.
1754 */
1755static DECLCALLBACK(void) drvCloudTunnelResume(PPDMDRVINS pDrvIns)
1756{
1757 LogFlowFunc(("\n"));
1758 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
1759
1760 int rc = RTUdpServerCreate("", pThis->uSrcPort, RTTHREADTYPE_IO, pThis->pszInstance,
1761 drvCloudTunnelReceive, pDrvIns, &pThis->pServer);
1762 if (RT_FAILURE(rc))
1763 PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1764 N_("CloudTunnel: Failed to start the Cloud tunnel server"));
1765
1766}
1767#endif
1768
1769/**
1770 * Cloud tunnel network transport driver registration record.
1771 */
1772const PDMDRVREG g_DrvCloudTunnel =
1773{
1774 /* u32Version */
1775 PDM_DRVREG_VERSION,
1776 /* szName */
1777 "CloudTunnel",
1778 /* szRCMod */
1779 "",
1780 /* szR0Mod */
1781 "",
1782 /* pszDescription */
1783 "Cloud Tunnel Network Transport Driver",
1784 /* fFlags */
1785 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1786 /* fClass. */
1787 PDM_DRVREG_CLASS_NETWORK,
1788 /* cMaxInstances */
1789 ~0U,
1790 /* cbInstance */
1791 sizeof(DRVCLOUDTUNNEL),
1792 /* pfnConstruct */
1793 drvCloudTunnelConstruct,
1794 /* pfnDestruct */
1795 drvCloudTunnelDestruct,
1796 /* pfnRelocate */
1797 NULL,
1798 /* pfnIOCtl */
1799 NULL,
1800 /* pfnPowerOn */
1801 NULL,
1802 /* pfnReset */
1803 NULL,
1804 /* pfnSuspend */
1805 NULL, // drvCloudTunnelSuspend,
1806 /* pfnResume */
1807 NULL, // drvCloudTunnelResume,
1808 /* pfnAttach */
1809 NULL,
1810 /* pfnDetach */
1811 NULL,
1812 /* pfnPowerOff */
1813 NULL,
1814 /* pfnSoftReset */
1815 NULL,
1816 /* u32EndVersion */
1817 PDM_DRVREG_VERSION
1818};
1819
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