VirtualBox

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

Last change on this file since 95433 was 94521, checked in by vboxsync, 3 years ago

libs/libssh,Main,FE/VBoxManage,Devices/Network/DrvCloudTunnel|ai: Add support for proxies, bugref:9469

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