VirtualBox

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

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

Build fixes for r149382.

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