VirtualBox

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

Last change on this file was 103346, checked in by vboxsync, 3 months ago

DrvCloudTunnel: Switch the RTStrPrintfV calls with RTStrPrintf2V which has easier overflow handling. bugref:10539

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 69.8 KB
Line 
1/* $Id: DrvCloudTunnel.cpp 103346 2024-02-14 02:06:13Z 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
757
758/* See ssh_channel_write_wontblock_callback in libssh/callbacks.h. */
759#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,10,0)
760static int channelWriteWontblockCallback(ssh_session, ssh_channel, uint32_t, void *)
761#else
762static int channelWriteWontblockCallback(ssh_session, ssh_channel, size_t, void *)
763#endif
764{
765 return 0;
766}
767
768
769
770/**
771 * This thread feeds the attached device with the packets received from the tunnel.
772 *
773 * This thread is needed because we cannot block I/O thread waiting for the attached
774 * device to become ready to receive packets coming from the tunnel.
775 */
776static DECLCALLBACK(int) drvCloudTunnelDevThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
777{
778 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
779
780 LogFlow(("%s: device thread %p started\n", pThis->pszInstance, pThread));
781
782 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
783 return VINF_SUCCESS;
784
785 /*
786 * Request processing loop.
787 */
788 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
789 {
790 int rc = RTReqQueueProcess(pThis->hDevReqQueue, RT_INDEFINITE_WAIT);
791 Log2(("drvCloudTunnelDevThread: RTReqQueueProcess returned '%Rrc'\n", rc));
792 if (RT_FAILURE(rc))
793 LogRel(("%s: failed to process device request with '%Rrc'\n", pThis->pszInstance, rc));
794 }
795
796 LogFlow(("%s: device thread %p terminated\n", pThis->pszInstance, pThread));
797 return VINF_SUCCESS;
798}
799
800
801static DECLCALLBACK(int) drvCloudTunnelReceiveWakeup(PDRVCLOUDTUNNEL pThis)
802{
803 NOREF(pThis);
804 /* Returning a VINF_* will cause RTReqQueueProcess return. */
805 return VWRN_STATE_CHANGED;
806}
807
808/**
809 * Unblock the I/O thread so it can respond to a state change.
810 *
811 * @returns VBox status code.
812 * @param pDevIns The pcnet device instance.
813 * @param pThread The send thread.
814 */
815static DECLCALLBACK(int) drvCloudTunnelDevWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
816{
817 RT_NOREF(pThread);
818 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
819 LogFlow(("%s: waking up device thread %p...\n", pThis->pszInstance, pThread));
820
821 /* Wake up device thread. */
822 PRTREQ pReq;
823 int rc = RTReqQueueCall(pThis->hDevReqQueue, &pReq, 10000 /*cMillies*/,
824 (PFNRT)drvCloudTunnelReceiveWakeup, 1, pThis);
825 if (RT_FAILURE(rc))
826 LogRel(("%s: failed to wake up device thread - %Rrc\n", pThis->pszInstance, rc));
827 if (RT_SUCCESS(rc))
828 RTReqRelease(pReq);
829
830 return rc;
831}
832
833#define DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE 1024
834#define DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE 65536
835
836static int drvCloudTunnelExecuteRemoteCommandNoOutput(PDRVCLOUDTUNNEL pThis, const char *pcszCommand, ...)
837{
838 va_list va;
839 va_start(va, pcszCommand);
840 ssize_t const cch = RTStrPrintf2V(pThis->pszCommandBuffer, DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE, pcszCommand, va);
841 va_end(va);
842 if (cch <= 0)
843 {
844 Log(("%s: Failed to process '%s'\n", pThis->pszInstance, pcszCommand));
845 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
846 N_("Failed to compose command line"));
847 }
848
849 LogFlow(("%s: [REMOTE] executing '%s'...\n", pThis->pszInstance, pThis->pszCommandBuffer));
850
851 ssh_channel channel = ssh_channel_new(pThis->pSshSession);
852 if (channel == NULL)
853 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
854 N_("Failed to allocate new channel"));
855
856 int rc = ssh_channel_open_session(channel);
857 if (rc != SSH_OK)
858 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
859 N_("Failed to open session channel"));
860 else
861 {
862 rc = ssh_channel_request_exec(channel, pThis->pszCommandBuffer);
863 if (rc != SSH_OK)
864 {
865 LogRel(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
866 Log(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
867 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
868 N_("Execute request failed with %d"), rc);
869 }
870 ssh_channel_close(channel);
871 }
872 ssh_channel_free(channel);
873
874 return VINF_SUCCESS;
875}
876
877
878static int drvCloudTunnelExecuteRemoteCommand(PDRVCLOUDTUNNEL pThis, const char *pcszCommand, ...)
879{
880 va_list va;
881 va_start(va, pcszCommand);
882 ssize_t const cch = RTStrPrintf2V(pThis->pszCommandBuffer, DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE, pcszCommand, va);
883 va_end(va);
884 if (cch <= 0)
885 {
886 Log(("%s: Failed to process '%s'\n", pThis->pszInstance, pcszCommand));
887 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
888 N_("Failed to compose command line"));
889 }
890
891 LogFlow(("%s: [REMOTE] executing '%s'...\n", pThis->pszInstance, pThis->pszCommandBuffer));
892
893 ssh_channel channel = ssh_channel_new(pThis->pSshSession);
894 if (channel == NULL)
895 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
896 N_("Failed to allocate new channel"));
897
898 int rc = ssh_channel_open_session(channel);
899 if (rc != SSH_OK)
900 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
901 N_("Failed to open session channel"));
902 else
903 {
904 rc = ssh_channel_request_exec(channel, pThis->pszCommandBuffer);
905 if (rc != SSH_OK)
906 {
907 LogRel(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
908 Log(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
909 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
910 N_("Execute request failed with %d"), rc);
911 }
912 else
913 {
914 int cbSpaceLeft = DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE;
915 int cbStdOut = 0;
916 char *pszBuffer = pThis->pszOutputBuffer;
917 int cBytes = ssh_channel_read_timeout(channel, pszBuffer, cbSpaceLeft, 0, 60000 /* ms */); /* Is 60 seconds really enough? */
918 while (cBytes > 0)
919 {
920 cbStdOut += cBytes;
921 pszBuffer += cBytes;
922 cbSpaceLeft -= cBytes;
923 if (cbSpaceLeft <= 0)
924 break;
925 cBytes = ssh_channel_read_timeout(channel, pszBuffer, cbSpaceLeft, 0, 60000 /* ms */); /* Is 60 seconds really enough? */
926 }
927 if (cBytes < 0)
928 {
929 LogRel(("%s: while executing '%s' ssh_channel_read_timeout returned error\n", pThis->pszInstance, pThis->pszCommandBuffer));
930 Log(("%s: while executing '%s' ssh_channel_read_timeout returned error\n", pThis->pszInstance, pThis->pszCommandBuffer));
931 rc = VERR_INTERNAL_ERROR;
932 }
933 else
934 {
935 /* Make sure the buffer is terminated. */
936 if (cbStdOut < DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE)
937 if (cbStdOut > 1 && pThis->pszOutputBuffer[cbStdOut - 1] == '\n')
938 pThis->pszOutputBuffer[cbStdOut - 1] = 0; /* Trim newline */
939 else
940 pThis->pszOutputBuffer[cbStdOut] = 0;
941 else
942 pThis->pszOutputBuffer[DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE - 1] = 0; /* No choice but to eat up last character. Could have returned warning though. */
943 if (cbStdOut == 0)
944 Log(("%s: received no output from remote console\n", pThis->pszInstance));
945 else
946 Log(("%s: received output from remote console:\n%s\n", pThis->pszInstance, pThis->pszOutputBuffer));
947 rc = VINF_SUCCESS;
948
949 char *pszErrorBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE);
950 if (pszErrorBuffer == NULL)
951 {
952 LogRel(("%s: Failed to allocate error buffer\n", pThis->pszInstance));
953 rc = VERR_INTERNAL_ERROR;
954 }
955 else
956 {
957 /* Report errors if there were any */
958 cBytes = ssh_channel_read_timeout(channel, pszErrorBuffer, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE, 1, 0); /* Peek at stderr */
959 if (cBytes > 0)
960 {
961 LogRel(("%s: WARNING! While executing '%s' remote console reported errors:\n", pThis->pszInstance, pThis->pszCommandBuffer));
962 Log(("%s: WARNING! While executing '%s' remote console reported errors:\n", pThis->pszInstance, pThis->pszCommandBuffer));
963 }
964 while (cBytes > 0)
965 {
966 LogRel(("%.*s", cBytes, pszErrorBuffer));
967 Log(("%.*s", cBytes, pszErrorBuffer));
968 cBytes = ssh_channel_read_timeout(channel, pszErrorBuffer, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE, 1, 1000); /* Wait for a second for more error output */
969 }
970 RTMemFree(pszErrorBuffer);
971 }
972 }
973 ssh_channel_send_eof(channel);
974 }
975 ssh_channel_close(channel);
976 }
977 ssh_channel_free(channel);
978
979 return VINF_SUCCESS;
980}
981
982
983static int drvCloudTunnelCloudInstanceInitialConfig(PDRVCLOUDTUNNEL pThis)
984{
985 LogFlow(("%s: configuring cloud instance...\n", pThis->pszInstance));
986
987 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])\"");
988 if (RT_FAILURE(rc))
989 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
990 N_("Failed to get network config via console channel"));
991 else
992 {
993 char *pszConfig = RTStrStr(pThis->pszOutputBuffer, "CONFIG: ");
994 if (!pszConfig)
995 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
996 N_("Failed to parse network config"));
997 else
998 {
999 char **ppapszTokens;
1000 size_t cTokens;
1001 rc = RTStrSplit(pszConfig + 8, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE - (pszConfig - pThis->pszOutputBuffer) - 8,
1002 " ", &ppapszTokens, &cTokens);
1003 if (RT_SUCCESS(rc))
1004 {
1005 /*
1006 * There should be exactly three tokens:
1007 * 1) Primary network interface name;
1008 * 2) Secondary network interface name;
1009 * 3) Secondary network gateway address.
1010 */
1011 if (cTokens != 3)
1012 Log(("%s: Got %u tokes instead of three while parsing '%s'\n", pThis->pszInstance, cTokens, pThis->pszOutputBuffer));
1013 else
1014 {
1015 char *pszSecondaryInterface = NULL;
1016 char *pszSecondaryGateway = NULL;
1017
1018 if (pThis->pszCloudPrimaryInterface)
1019 RTStrFree(pThis->pszCloudPrimaryInterface);
1020 pThis->pszCloudPrimaryInterface = RTStrDup(ppapszTokens[0]);
1021 pszSecondaryInterface = ppapszTokens[1];
1022 pszSecondaryGateway = ppapszTokens[2];
1023 Log(("%s: primary=%s secondary=%s gateway=%s\n", pThis->pszInstance, pThis->pszCloudPrimaryInterface, pszSecondaryInterface, pszSecondaryGateway));
1024
1025 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo oci-network-config -c");
1026 if (RT_SUCCESS(rc))
1027 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip tuntap add dev tap0 mod tap user opc");
1028 if (RT_SUCCESS(rc))
1029 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo sh -c 'echo \"PermitTunnel yes\" >> /etc/ssh/sshd_config'");
1030 if (RT_SUCCESS(rc))
1031 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo kill -SIGHUP $(pgrep -f \"sshd -D\")");
1032 if (RT_SUCCESS(rc))
1033 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link add name br0 type bridge");
1034 if (RT_SUCCESS(rc))
1035 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev tap0 master br0");
1036 if (RT_SUCCESS(rc))
1037 rc = drvCloudTunnelExecuteRemoteCommandNoOutput(pThis, "sudo ip route change default via %s dev %s", pszSecondaryGateway, pszSecondaryInterface);
1038 if (RT_FAILURE(rc))
1039 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1040 N_("Failed to execute network config command via console channel"));
1041 }
1042
1043 for (size_t i = 0; i < cTokens; i++)
1044 RTStrFree(ppapszTokens[i]);
1045 RTMemFree(ppapszTokens);
1046 }
1047 }
1048 }
1049
1050 return rc;
1051}
1052
1053
1054static int drvCloudTunnelCloudInstanceFinalConfig(PDRVCLOUDTUNNEL pThis)
1055{
1056 if (pThis->pszCloudPrimaryInterface == NULL)
1057 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1058 N_("Failed to finalize cloud instance config because of unknown primary interface name!"));
1059
1060 LogFlow(("%s: finalizing cloud instance configuration...\n", pThis->pszInstance));
1061
1062 int rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s down", pThis->pszCloudPrimaryInterface);
1063 if (RT_SUCCESS(rc))
1064 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s address %RTmac", pThis->pszCloudPrimaryInterface, pThis->targetMac.au8);
1065 if (RT_SUCCESS(rc))
1066 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ifconfig %s 0.0.0.0", pThis->pszCloudPrimaryInterface); /* Make sure no IP is configured on primary */
1067 if (RT_SUCCESS(rc))
1068 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s master br0", pThis->pszCloudPrimaryInterface);
1069 if (RT_SUCCESS(rc))
1070 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s up", pThis->pszCloudPrimaryInterface);
1071 if (RT_SUCCESS(rc))
1072 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev tap0 up");
1073 if (RT_SUCCESS(rc))
1074 rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev br0 up");
1075 if (RT_FAILURE(rc))
1076 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1077 N_("Failed to execute network config command via console channel"));
1078
1079 return rc;
1080}
1081
1082
1083static int drvCloudTunnelOpenTunnelChannel(PDRVCLOUDTUNNEL pThis)
1084{
1085 LogFlow(("%s: opening tunnel channel...\n", pThis->pszInstance));
1086 pThis->pSshChannel = ssh_channel_new(pThis->pSshSession);
1087 if (pThis->pSshChannel == NULL)
1088 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1089 N_("Failed to allocate new channel"));
1090 int rc = ssh_channel_open_tunnel(pThis->pSshChannel, 0);
1091 if (rc < 0)
1092 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1093 N_("Failed to open tunnel channel"));
1094 else
1095 {
1096 /* Set packet receive callback. */
1097 rc = ssh_set_channel_callbacks(pThis->pSshChannel, &pThis->Callbacks);
1098 if (rc != SSH_OK)
1099 rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1100 N_("Failed to set packet receive callback"));
1101 }
1102
1103 return rc;
1104}
1105
1106
1107static void closeTunnelChannel(PDRVCLOUDTUNNEL pThis)
1108{
1109 if (pThis->pSshChannel)
1110 {
1111 LogFlow(("%s: closing tunnel channel %p\n", pThis->pszInstance, pThis->pSshChannel));
1112 ssh_channel_close(pThis->pSshChannel);
1113 ssh_channel_free(pThis->pSshChannel);
1114 pThis->pSshChannel = NULL;
1115 }
1116}
1117
1118
1119static int drvCloudTunnelStartIoThread(PDRVCLOUDTUNNEL pThis)
1120{
1121 LogFlow(("%s: starting I/O thread...\n", pThis->pszInstance));
1122 int rc = createConnectedSockets(pThis);
1123 if (RT_FAILURE(rc))
1124 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1125 N_("CloudTunnel: Failed to create a pair of connected sockets"));
1126
1127 /*
1128 * Start the cloud I/O thread.
1129 */
1130 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pIoThread,
1131 pThis, drvCloudTunnelIoThread, drvCloudTunnelIoWakeup,
1132 64 * _1K, RTTHREADTYPE_IO, pThis->pszInstanceIo);
1133 if (RT_FAILURE(rc))
1134 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1135 N_("CloudTunnel: Failed to start I/O thread"));
1136
1137 return rc;
1138}
1139
1140static void drvCloudTunnelStopIoThread(PDRVCLOUDTUNNEL pThis)
1141{
1142 if (pThis->pIoThread)
1143 {
1144 LogFlow(("%s: stopping I/O thread...\n", pThis->pszInstance));
1145 int rc = PDMDrvHlpThreadDestroy(pThis->pDrvIns, pThis->pIoThread, NULL);
1146 AssertRC(rc);
1147 pThis->pIoThread = NULL;
1148 }
1149 destroyConnectedSockets(pThis);
1150
1151}
1152
1153static int destroyTunnel(PDRVCLOUDTUNNEL pThis)
1154{
1155 if (pThis->pSshChannel)
1156 {
1157 int rc = ssh_remove_channel_callbacks(pThis->pSshChannel, &pThis->Callbacks);
1158 if (rc != SSH_OK)
1159 LogRel(("%s: WARNING! Failed to remove tunnel channel callbacks.\n", pThis->pszInstance));
1160 }
1161 drvCloudTunnelStopIoThread(pThis);
1162 closeTunnelChannel(pThis);
1163 ssh_disconnect(pThis->pSshSession);
1164 ssh_free(pThis->pSshSession);
1165 pThis->pSshSession = NULL;
1166 return VINF_SUCCESS;
1167}
1168
1169
1170static int drvCloudTunnelNewSession(PDRVCLOUDTUNNEL pThis, bool fPrimary)
1171{
1172 pThis->pSshSession = ssh_new();
1173 if (pThis->pSshSession == NULL)
1174 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1175 N_("CloudTunnel: Failed to allocate new SSH session"));
1176 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_LOG_VERBOSITY, &pThis->iSshVerbosity) < 0)
1177 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1178 N_("Failed to set SSH_OPTIONS_LOG_VERBOSITY"));
1179 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_USER, pThis->pszUser) < 0)
1180 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1181 N_("Failed to set SSH_OPTIONS_USER"));
1182 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_HOST, fPrimary ? pThis->pszPrimaryIP : pThis->pszSecondaryIP) < 0)
1183 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1184 N_("Failed to set SSH_OPTIONS_HOST"));
1185
1186 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_TIMEOUT, &pThis->ulTimeoutInSecounds) < 0)
1187 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1188 N_("Failed to set SSH_OPTIONS_TIMEOUT"));
1189
1190 const char *pcszProxyType = fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType;
1191 if (pcszProxyType)
1192 {
1193 char szProxyCmd[1024];
1194
1195 const char *pcszProxyUser = fPrimary ? pThis->pszPrimaryProxyUser : pThis->pszSecondaryProxyUser;
1196 if (pcszProxyUser)
1197 RTStrPrintf(szProxyCmd, sizeof(szProxyCmd), "#VBoxProxy%s %s %u %s %s",
1198 fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType,
1199 fPrimary ? pThis->pszPrimaryProxyHost : pThis->pszSecondaryProxyHost,
1200 fPrimary ? pThis->u16PrimaryProxyPort : pThis->u16SecondaryProxyPort,
1201 fPrimary ? pThis->pszPrimaryProxyUser : pThis->pszSecondaryProxyUser,
1202 fPrimary ? pThis->pszPrimaryProxyPassword : pThis->pszSecondaryProxyPassword);
1203 else
1204 RTStrPrintf(szProxyCmd, sizeof(szProxyCmd), "#VBoxProxy%s %s %u",
1205 fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType,
1206 fPrimary ? pThis->pszPrimaryProxyHost : pThis->pszSecondaryProxyHost,
1207 fPrimary ? pThis->u16PrimaryProxyPort : pThis->u16SecondaryProxyPort);
1208 LogRel(("%s: using proxy command '%s'\n", pThis->pszInstance, szProxyCmd));
1209 if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_PROXYCOMMAND, szProxyCmd) < 0)
1210 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1211 N_("Failed to set SSH_OPTIONS_PROXYCOMMAND"));
1212 }
1213
1214 int rc = ssh_connect(pThis->pSshSession);
1215 for (int cAttempt = 1; rc != SSH_OK && cAttempt <= 5; cAttempt++)
1216 {
1217 ssh_disconnect(pThis->pSshSession);
1218 /* One more time, just to be sure. */
1219 LogRel(("%s: failed to connect to %s, retrying(#%d)...\n", pThis->pszInstance,
1220 fPrimary ? pThis->pszPrimaryIP : pThis->pszSecondaryIP, cAttempt));
1221 RTThreadSleep(10000); /* Sleep 10 seconds, then retry */
1222 rc = ssh_connect(pThis->pSshSession);
1223 }
1224 if (rc != SSH_OK)
1225 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1226 N_("CloudTunnel: Failed to connect to %s interface"), fPrimary ? "primary" : "secondary");
1227
1228 rc = ssh_userauth_publickey(pThis->pSshSession, NULL, pThis->SshKey);
1229 if (rc != SSH_AUTH_SUCCESS)
1230 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1231 N_("Failed to authenticate with public key"));
1232
1233 return VINF_SUCCESS;
1234}
1235
1236static int drvCloudTunnelSwitchToSecondary(PDRVCLOUDTUNNEL pThis)
1237{
1238 int rc = drvCloudTunnelNewSession(pThis, true /* fPrimary */);
1239 /*
1240 * Establish temporary console channel and configure the cloud instance
1241 * to bridge the tunnel channel to instance's primary interface.
1242 */
1243 if (RT_SUCCESS(rc))
1244 rc = drvCloudTunnelCloudInstanceInitialConfig(pThis);
1245
1246 ssh_disconnect(pThis->pSshSession);
1247 ssh_free(pThis->pSshSession);
1248 pThis->pSshSession = NULL;
1249
1250 return rc;
1251}
1252
1253
1254static int establishTunnel(PDRVCLOUDTUNNEL pThis)
1255{
1256 int rc = drvCloudTunnelNewSession(pThis, false /* fPrimary */);
1257 if (RT_SUCCESS(rc))
1258 rc = drvCloudTunnelCloudInstanceFinalConfig(pThis);
1259 if (RT_SUCCESS(rc))
1260 rc = drvCloudTunnelOpenTunnelChannel(pThis);
1261 if (RT_SUCCESS(rc))
1262 rc = drvCloudTunnelStartIoThread(pThis);
1263 if (RT_FAILURE(rc))
1264 {
1265 destroyTunnel(pThis);
1266 return rc;
1267 }
1268
1269 return rc;
1270}
1271
1272
1273static DECL_NOTHROW(void) drvCloudTunnelSshLogCallback(int priority, const char *function, const char *buffer, void *userdata)
1274{
1275 PDRVCLOUDTUNNEL pThis = (PDRVCLOUDTUNNEL)userdata;
1276#ifdef LOG_ENABLED
1277 const char *pcszVerbosity;
1278 switch (priority)
1279 {
1280 case SSH_LOG_WARNING:
1281 pcszVerbosity = "WARNING";
1282 break;
1283 case SSH_LOG_PROTOCOL:
1284 pcszVerbosity = "PROTOCOL";
1285 break;
1286 case SSH_LOG_PACKET:
1287 pcszVerbosity = "PACKET";
1288 break;
1289 case SSH_LOG_FUNCTIONS:
1290 pcszVerbosity = "FUNCTIONS";
1291 break;
1292 default:
1293 pcszVerbosity = "UNKNOWN";
1294 break;
1295 }
1296 Log3(("%s: SSH-%s: %s: %s\n", pThis->pszInstance, pcszVerbosity, function, buffer));
1297#else
1298 RT_NOREF(priority);
1299 LogRel(("%s: SSH %s: %s\n", pThis->pszInstance, function, buffer));
1300#endif
1301}
1302
1303/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
1304
1305DECLINLINE(void) drvCloudTunnelStrFree(char **ppszString)
1306{
1307 if (*ppszString)
1308 {
1309 RTStrFree(*ppszString);
1310 *ppszString = NULL;
1311 }
1312}
1313
1314DECLINLINE(void) drvCloudTunnelHeapFree(PPDMDRVINS pDrvIns, char **ppszString)
1315{
1316 if (*ppszString)
1317 {
1318 PDMDrvHlpMMHeapFree(pDrvIns, *ppszString);
1319 *ppszString = NULL;
1320 }
1321}
1322
1323/**
1324 * Destruct a driver instance.
1325 *
1326 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1327 * resources can be freed correctly.
1328 *
1329 * @param pDrvIns The driver instance data.
1330 */
1331static DECLCALLBACK(void) drvCloudTunnelDestruct(PPDMDRVINS pDrvIns)
1332{
1333 LogFlowFunc(("\n"));
1334 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
1335 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1336
1337 ASMAtomicXchgSize(&pThis->fLinkDown, true);
1338
1339 destroyTunnel(pThis);
1340
1341 if (pThis->hIoReqQueue != NIL_RTREQQUEUE)
1342 {
1343 RTReqQueueDestroy(pThis->hIoReqQueue);
1344 pThis->hIoReqQueue = NIL_RTREQQUEUE;
1345 }
1346
1347 drvCloudTunnelStrFree(&pThis->pszCloudPrimaryInterface);
1348
1349 drvCloudTunnelHeapFree(pDrvIns, &pThis->pszPrimaryProxyType);
1350 drvCloudTunnelStrFree(&pThis->pszPrimaryProxyHost);
1351 drvCloudTunnelHeapFree(pDrvIns, &pThis->pszPrimaryProxyUser);
1352 drvCloudTunnelStrFree(&pThis->pszPrimaryProxyPassword);
1353
1354 drvCloudTunnelHeapFree(pDrvIns, &pThis->pszSecondaryProxyType);
1355 drvCloudTunnelStrFree(&pThis->pszSecondaryProxyHost);
1356 drvCloudTunnelHeapFree(pDrvIns, &pThis->pszSecondaryProxyUser);
1357 drvCloudTunnelStrFree(&pThis->pszSecondaryProxyPassword);
1358
1359 drvCloudTunnelStrFree(&pThis->pszSecondaryIP);
1360 drvCloudTunnelStrFree(&pThis->pszPrimaryIP);
1361 drvCloudTunnelStrFree(&pThis->pszUser);
1362
1363 drvCloudTunnelStrFree(&pThis->pszInstanceDev);
1364 drvCloudTunnelStrFree(&pThis->pszInstanceIo);
1365 drvCloudTunnelStrFree(&pThis->pszInstance);
1366
1367 drvCloudTunnelStrFree(&pThis->pszOutputBuffer);
1368 drvCloudTunnelStrFree(&pThis->pszCommandBuffer);
1369
1370 ssh_key_free(pThis->SshKey);
1371
1372 ssh_finalize();
1373 //OPENSSL_cleanup();
1374
1375 // if (pThis->pServer)
1376 // {
1377 // RTUdpServerDestroy(pThis->pServer);
1378 // pThis->pServer = NULL;
1379 // }
1380
1381 /*
1382 * Kill the xmit lock.
1383 */
1384 if (RTCritSectIsInitialized(&pThis->XmitLock))
1385 RTCritSectDelete(&pThis->XmitLock);
1386
1387#ifdef VBOX_WITH_STATISTICS
1388 /*
1389 * Deregister statistics.
1390 */
1391 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSent);
1392 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSentBytes);
1393 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecv);
1394 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecvBytes);
1395 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
1396 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
1397 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatDevRecv);
1398 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatDevRecvWait);
1399#endif /* VBOX_WITH_STATISTICS */
1400}
1401
1402
1403/**
1404 * Construct a Cloud tunnel network transport driver instance.
1405 *
1406 * @copydoc FNPDMDRVCONSTRUCT
1407 */
1408static DECLCALLBACK(int) drvCloudTunnelConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1409{
1410 RT_NOREF(fFlags);
1411 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1412 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
1413 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1414
1415 /*
1416 * Init the static parts.
1417 */
1418 pThis->pDrvIns = pDrvIns;
1419 pThis->pszCommandBuffer = NULL;
1420 pThis->pszOutputBuffer = NULL;
1421 pThis->pszInstance = NULL;
1422 pThis->pszPrimaryIP = NULL;
1423 pThis->pszSecondaryIP = NULL;
1424 pThis->pszUser = NULL;
1425 pThis->SshKey = 0;
1426
1427 /* IBase */
1428 pDrvIns->IBase.pfnQueryInterface = drvCloudTunnelQueryInterface;
1429 /* INetwork */
1430 pThis->INetworkUp.pfnBeginXmit = drvCloudTunnelUp_BeginXmit;
1431 pThis->INetworkUp.pfnAllocBuf = drvCloudTunnelUp_AllocBuf;
1432 pThis->INetworkUp.pfnFreeBuf = drvCloudTunnelUp_FreeBuf;
1433 pThis->INetworkUp.pfnSendBuf = drvCloudTunnelUp_SendBuf;
1434 pThis->INetworkUp.pfnEndXmit = drvCloudTunnelUp_EndXmit;
1435 pThis->INetworkUp.pfnSetPromiscuousMode = drvCloudTunnelUp_SetPromiscuousMode;
1436 pThis->INetworkUp.pfnNotifyLinkChanged = drvCloudTunnelUp_NotifyLinkChanged;
1437
1438 /* ??? */
1439 pThis->iSocketIn = INVALID_SOCKET;
1440 pThis->iSocketOut = INVALID_SOCKET;
1441 pThis->pSshSession = 0;
1442 pThis->pSshChannel = 0;
1443
1444 pThis->pDevThread = 0;
1445 pThis->pIoThread = 0;
1446 pThis->hIoReqQueue = NIL_RTREQQUEUE;
1447
1448 pThis->fLinkDown = false;
1449
1450 pThis->pszCloudPrimaryInterface = NULL;
1451
1452#ifdef VBOX_WITH_STATISTICS
1453 /*
1454 * Statistics.
1455 */
1456 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/CloudTunnel%d/Packets/Sent", pDrvIns->iInstance);
1457 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/CloudTunnel%d/Bytes/Sent", pDrvIns->iInstance);
1458 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/CloudTunnel%d/Packets/Received", pDrvIns->iInstance);
1459 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/CloudTunnel%d/Bytes/Received", pDrvIns->iInstance);
1460 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/CloudTunnel%d/Transmit", pDrvIns->iInstance);
1461 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/CloudTunnel%d/Receive", pDrvIns->iInstance);
1462 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatDevRecv, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling device receive runs.", "/Drivers/CloudTunnel%d/DeviceReceive", pDrvIns->iInstance);
1463 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatDevRecvWait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling device receive waits.", "/Drivers/CloudTunnel%d/DeviceReceiveWait", pDrvIns->iInstance);
1464#endif /* VBOX_WITH_STATISTICS */
1465
1466 /*
1467 * Validate the config.
1468 */
1469 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "SshKey"
1470 "|PrimaryIP"
1471 "|SecondaryIP"
1472 "|TargetMAC"
1473
1474 "|PrimaryProxyType"
1475 "|PrimaryProxyHost"
1476 "|PrimaryProxyPort"
1477 "|PrimaryProxyUser"
1478 "|PrimaryProxyPassword"
1479 "|SecondaryProxyType"
1480 "|SecondaryProxyHost"
1481 "|SecondaryProxyPort"
1482 "|SecondaryProxyUser"
1483 "|SecondaryProxyPassword"
1484
1485 ,"");
1486
1487 /*
1488 * Check that no-one is attached to us.
1489 */
1490 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1491 ("Configuration error: Not possible to attach anything to this driver!\n"),
1492 VERR_PDM_DRVINS_NO_ATTACH);
1493
1494 /*
1495 * Query the network port interface.
1496 */
1497 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1498 if (!pThis->pIAboveNet)
1499 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1500 N_("Configuration error: The above device/driver didn't export the network port interface"));
1501
1502 /*
1503 * Read the configuration.
1504 */
1505 int rc;
1506
1507 char szVal[2048];
1508 RTNETADDRIPV4 tmpAddr;
1509 rc = pHlp->pfnCFGMQueryString(pCfg, "PrimaryIP", szVal, sizeof(szVal));
1510 if (RT_FAILURE(rc))
1511 return PDMDRV_SET_ERROR(pDrvIns, rc,
1512 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryIP\" as string failed"));
1513 rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
1514 if (RT_FAILURE(rc))
1515 return PDMDRV_SET_ERROR(pDrvIns, rc,
1516 N_("DrvCloudTunnel: Configuration error: \"PrimaryIP\" is not valid"));
1517 else
1518 pThis->pszPrimaryIP = RTStrDup(szVal);
1519
1520 rc = pHlp->pfnCFGMQueryString(pCfg, "SecondaryIP", szVal, sizeof(szVal));
1521 if (RT_FAILURE(rc))
1522 return PDMDRV_SET_ERROR(pDrvIns, rc,
1523 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryIP\" as string failed"));
1524 rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
1525 if (RT_FAILURE(rc))
1526 return PDMDRV_SET_ERROR(pDrvIns, rc,
1527 N_("DrvCloudTunnel: Configuration error: \"SecondaryIP\" is not valid"));
1528 else
1529 pThis->pszSecondaryIP = RTStrDup(szVal);
1530 rc = pHlp->pfnCFGMQueryBytes(pCfg, "TargetMAC", pThis->targetMac.au8, sizeof(pThis->targetMac.au8));
1531 if (RT_FAILURE(rc))
1532 return PDMDRV_SET_ERROR(pDrvIns, rc,
1533 N_("DrvCloudTunnel: Configuration error: Failed to get target MAC address"));
1534 /** @todo In the near future we will want to include proxy settings here! */
1535 // Do we want to pass the user name via CFGM?
1536 pThis->pszUser = RTStrDup("opc");
1537 // Is it safe to expose verbosity via CFGM?
1538#ifdef LOG_ENABLED
1539 pThis->iSshVerbosity = SSH_LOG_PACKET; //SSH_LOG_FUNCTIONS;
1540#else
1541 pThis->iSshVerbosity = SSH_LOG_WARNING;
1542#endif
1543
1544 pThis->ulTimeoutInSecounds = 30; /* The default 10-second timeout is too short? */
1545
1546 rc = pHlp->pfnCFGMQueryPassword(pCfg, "SshKey", szVal, sizeof(szVal));
1547 if (RT_FAILURE(rc))
1548 return PDMDRV_SET_ERROR(pDrvIns, rc,
1549 N_("DrvCloudTunnel: Configuration error: Querying \"SshKey\" as password failed"));
1550 rc = ssh_pki_import_privkey_base64(szVal, NULL, NULL, NULL, &pThis->SshKey);
1551 RTMemWipeThoroughly(szVal, sizeof(szVal), 10);
1552 if (rc != SSH_OK)
1553 return PDMDRV_SET_ERROR(pDrvIns, VERR_INVALID_BASE64_ENCODING,
1554 N_("DrvCloudTunnel: Configuration error: Converting \"SshKey\" from base64 failed"));
1555
1556 /* PrimaryProxyType is optional */
1557 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "PrimaryProxyType", &pThis->pszPrimaryProxyType, NULL);
1558 if (RT_FAILURE(rc))
1559 return PDMDRV_SET_ERROR(pDrvIns, rc,
1560 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyType\" as string failed"));
1561 if (pThis->pszPrimaryProxyType)
1562 {
1563 rc = pHlp->pfnCFGMQueryString(pCfg, "PrimaryProxyHost", szVal, sizeof(szVal));
1564 if (RT_FAILURE(rc))
1565 return PDMDRV_SET_ERROR(pDrvIns, rc,
1566 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyHost\" as string failed"));
1567 rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
1568 if (RT_FAILURE(rc))
1569 return PDMDRV_SET_ERROR(pDrvIns, rc,
1570 N_("DrvCloudTunnel: Configuration error: \"PrimaryProxyHost\" is not valid"));
1571 else
1572 pThis->pszPrimaryProxyHost = RTStrDup(szVal);
1573
1574 uint64_t u64Val;
1575 rc = pHlp->pfnCFGMQueryInteger(pCfg, "PrimaryProxyPort", &u64Val);
1576 if (RT_FAILURE(rc))
1577 return PDMDRV_SET_ERROR(pDrvIns, rc,
1578 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyPort\" as integer failed"));
1579 if (u64Val > 0xFFFF)
1580 return PDMDRV_SET_ERROR(pDrvIns, rc,
1581 N_("DrvCloudTunnel: Configuration error: \"PrimaryProxyPort\" is not valid"));
1582 pThis->u16PrimaryProxyPort = (uint16_t)u64Val;
1583
1584 /* PrimaryProxyUser is optional */
1585 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "PrimaryProxyUser", &pThis->pszPrimaryProxyUser, NULL);
1586 if (RT_FAILURE(rc))
1587 return PDMDRV_SET_ERROR(pDrvIns, rc,
1588 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyUser\" as string failed"));
1589 /* PrimaryProxyPassword must be present if PrimaryProxyUser is present */
1590 if (pThis->pszPrimaryProxyUser)
1591 {
1592 rc = pHlp->pfnCFGMQueryPassword(pCfg, "PrimaryProxyPassword", szVal, sizeof(szVal));
1593 if (RT_FAILURE(rc))
1594 return PDMDRV_SET_ERROR(pDrvIns, rc,
1595 N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyPassword\" as string failed"));
1596 pThis->pszPrimaryProxyPassword = RTStrDup(szVal);
1597 }
1598 }
1599
1600 /* SecondaryProxyType is optional */
1601 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "SecondaryProxyType", &pThis->pszSecondaryProxyType, NULL);
1602 if (RT_FAILURE(rc))
1603 return PDMDRV_SET_ERROR(pDrvIns, rc,
1604 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyType\" as string failed"));
1605 if (pThis->pszSecondaryProxyType)
1606 {
1607 if (RT_FAILURE(rc))
1608 return PDMDRV_SET_ERROR(pDrvIns, rc,
1609 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyType\" as string failed"));
1610
1611 rc = pHlp->pfnCFGMQueryString(pCfg, "SecondaryProxyHost", szVal, sizeof(szVal));
1612 if (RT_FAILURE(rc))
1613 return PDMDRV_SET_ERROR(pDrvIns, rc,
1614 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyHost\" as string failed"));
1615 rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
1616 if (RT_FAILURE(rc))
1617 return PDMDRV_SET_ERROR(pDrvIns, rc,
1618 N_("DrvCloudTunnel: Configuration error: \"SecondaryProxyHost\" is not valid"));
1619 else
1620 pThis->pszSecondaryProxyHost = RTStrDup(szVal);
1621
1622 uint64_t u64Val;
1623 rc = pHlp->pfnCFGMQueryInteger(pCfg, "SecondaryProxyPort", &u64Val);
1624 if (RT_FAILURE(rc))
1625 return PDMDRV_SET_ERROR(pDrvIns, rc,
1626 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyPort\" as integer failed"));
1627 if (u64Val > 0xFFFF)
1628 return PDMDRV_SET_ERROR(pDrvIns, rc,
1629 N_("DrvCloudTunnel: Configuration error: \"SecondaryProxyPort\" is not valid"));
1630 pThis->u16SecondaryProxyPort = (uint16_t)u64Val;
1631
1632 /* SecondaryProxyUser is optional */
1633 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "SecondaryProxyUser", &pThis->pszSecondaryProxyUser, NULL);
1634 if (RT_FAILURE(rc))
1635 return PDMDRV_SET_ERROR(pDrvIns, rc,
1636 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyUser\" as string failed"));
1637 /* SecondaryProxyPassword must be present if SecondaryProxyUser is present */
1638 if (pThis->pszSecondaryProxyUser)
1639 {
1640 rc = pHlp->pfnCFGMQueryPassword(pCfg, "SecondaryProxyPassword", szVal, sizeof(szVal));
1641 if (RT_FAILURE(rc))
1642 return PDMDRV_SET_ERROR(pDrvIns, rc,
1643 N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyPassword\" as string failed"));
1644 pThis->pszSecondaryProxyPassword = RTStrDup(szVal);
1645 }
1646 }
1647
1648 pThis->pszCommandBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE);
1649 if (pThis->pszCommandBuffer == NULL)
1650 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_OPEN_FAILED,
1651 N_("DrvCloudTunnel: Failed to allocate command buffer"));
1652 pThis->pszOutputBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE);
1653 if (pThis->pszOutputBuffer == NULL)
1654 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_OPEN_FAILED,
1655 N_("DrvCloudTunnel: Failed to allocate output buffer"));
1656 /*
1657 * Create unique instance name for logging.
1658 */
1659 rc = RTStrAPrintf(&pThis->pszInstance, "CT#%d", pDrvIns->iInstance);
1660 AssertRC(rc);
1661
1662 LogRel(("%s: primary=%s secondary=%s target-mac=%RTmac\n", pThis->pszInstance, pThis->pszPrimaryIP, pThis->pszSecondaryIP, pThis->targetMac.au8));
1663
1664 /*
1665 * Create unique thread name for cloud I/O.
1666 */
1667 rc = RTStrAPrintf(&pThis->pszInstanceIo, "CTunIO%d", pDrvIns->iInstance);
1668 AssertRC(rc);
1669
1670 /*
1671 * Create unique thread name for device receive function.
1672 */
1673 rc = RTStrAPrintf(&pThis->pszInstanceDev, "CTunDev%d", pDrvIns->iInstance);
1674 AssertRC(rc);
1675
1676 /*
1677 * Create the transmit lock.
1678 */
1679 rc = RTCritSectInit(&pThis->XmitLock);
1680 AssertRCReturn(rc, rc);
1681
1682 /*
1683 * Create the request queue for I/O requests.
1684 */
1685 rc = RTReqQueueCreate(&pThis->hIoReqQueue);
1686 AssertLogRelRCReturn(rc, rc);
1687
1688 /*
1689 * Create the request queue for attached device requests.
1690 */
1691 rc = RTReqQueueCreate(&pThis->hDevReqQueue);
1692 AssertLogRelRCReturn(rc, rc);
1693
1694 /*
1695 * Start the device output thread.
1696 */
1697 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pDevThread,
1698 pThis, drvCloudTunnelDevThread, drvCloudTunnelDevWakeup,
1699 64 * _1K, RTTHREADTYPE_IO, pThis->pszInstanceDev);
1700 if (RT_FAILURE(rc))
1701 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1702 N_("CloudTunnel: Failed to start device thread"));
1703
1704 rc = ssh_init();
1705 if (rc != SSH_OK)
1706 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1707 N_("CloudTunnel: Failed to initialize libssh"));
1708
1709 memset(&pThis->Callbacks, 0, sizeof(pThis->Callbacks));
1710#ifdef PACKET_CAPTURE_ENABLED
1711 pThis->Callbacks.channel_data_function = drvCloudTunnelReceiveCallbackWithPacketCapture;
1712#else
1713 pThis->Callbacks.channel_data_function = drvCloudTunnelReceiveCallback;
1714#endif
1715 pThis->Callbacks.userdata = pThis;
1716 pThis->Callbacks.channel_write_wontblock_function = channelWriteWontblockCallback;
1717 ssh_callbacks_init(&pThis->Callbacks);
1718
1719 rc = ssh_set_log_callback(drvCloudTunnelSshLogCallback);
1720 if (rc != SSH_OK)
1721 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1722 N_("CloudTunnel: Failed to set libssh log callback"));
1723 rc = ssh_set_log_userdata(pThis);
1724 if (rc != SSH_OK)
1725 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1726 N_("CloudTunnel: Failed to set libssh log userdata"));
1727
1728 rc = drvCloudTunnelSwitchToSecondary(pThis);
1729 if (RT_SUCCESS(rc))
1730 rc = establishTunnel(pThis);
1731
1732 return rc;
1733}
1734
1735
1736#if 0
1737/**
1738 * Suspend notification.
1739 *
1740 * @param pDrvIns The driver instance.
1741 */
1742static DECLCALLBACK(void) drvCloudTunnelSuspend(PPDMDRVINS pDrvIns)
1743{
1744 LogFlowFunc(("\n"));
1745 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
1746
1747 RT_NOREF(pThis);
1748 // if (pThis->pServer)
1749 // {
1750 // RTUdpServerDestroy(pThis->pServer);
1751 // pThis->pServer = NULL;
1752 // }
1753}
1754
1755
1756/**
1757 * Resume notification.
1758 *
1759 * @param pDrvIns The driver instance.
1760 */
1761static DECLCALLBACK(void) drvCloudTunnelResume(PPDMDRVINS pDrvIns)
1762{
1763 LogFlowFunc(("\n"));
1764 PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
1765
1766 int rc = RTUdpServerCreate("", pThis->uSrcPort, RTTHREADTYPE_IO, pThis->pszInstance,
1767 drvCloudTunnelReceive, pDrvIns, &pThis->pServer);
1768 if (RT_FAILURE(rc))
1769 PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
1770 N_("CloudTunnel: Failed to start the Cloud tunnel server"));
1771
1772}
1773#endif
1774
1775/**
1776 * Cloud tunnel network transport driver registration record.
1777 */
1778const PDMDRVREG g_DrvCloudTunnel =
1779{
1780 /* u32Version */
1781 PDM_DRVREG_VERSION,
1782 /* szName */
1783 "CloudTunnel",
1784 /* szRCMod */
1785 "",
1786 /* szR0Mod */
1787 "",
1788 /* pszDescription */
1789 "Cloud Tunnel Network Transport Driver",
1790 /* fFlags */
1791 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1792 /* fClass. */
1793 PDM_DRVREG_CLASS_NETWORK,
1794 /* cMaxInstances */
1795 ~0U,
1796 /* cbInstance */
1797 sizeof(DRVCLOUDTUNNEL),
1798 /* pfnConstruct */
1799 drvCloudTunnelConstruct,
1800 /* pfnDestruct */
1801 drvCloudTunnelDestruct,
1802 /* pfnRelocate */
1803 NULL,
1804 /* pfnIOCtl */
1805 NULL,
1806 /* pfnPowerOn */
1807 NULL,
1808 /* pfnReset */
1809 NULL,
1810 /* pfnSuspend */
1811 NULL, // drvCloudTunnelSuspend,
1812 /* pfnResume */
1813 NULL, // drvCloudTunnelResume,
1814 /* pfnAttach */
1815 NULL,
1816 /* pfnDetach */
1817 NULL,
1818 /* pfnPowerOff */
1819 NULL,
1820 /* pfnSoftReset */
1821 NULL,
1822 /* u32EndVersion */
1823 PDM_DRVREG_VERSION
1824};
1825
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use