summaryrefslogtreecommitdiff
path: root/src/VBox/HostDrivers/VBoxNetFlt/win/VBoxNetFltCommon-win.h
blob: 42792a985f01c7417b05b51d9628e142c6b43aea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/* $Id: VBoxNetFltCommon-win.h 29108 2010-05-05 20:17:42Z vboxsync $ */
/** @file
 * VBoxNetFltCommon.h - Network Filter Driver (Host), Windows Specific Code. Common headeer with commonly used defines and decls
 */

/*
 * Copyright (C) 2008 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */
/*
 * Based in part on Microsoft DDK sample code for Ndis Intermediate Miniport passthru driver sample.
 * Copyright (c) 1993-1999, Microsoft Corporation
 */

#ifndef ___VBoxNetFltCommon_win_h___
#define ___VBoxNetFltCommon_win_h___

//#define NTSTRSAFE_LIB

#ifdef DEBUG
//# define DEBUG_NETFLT_PACKETS
# ifndef DEBUG_misha
#  define DEBUG_NETFLT_NOASSERT
# endif
/* # define DEBUG_NETFLT_LOOPBACK */

/* receive logic has several branches */
/* the DEBUG_NETFLT_RECV* macros used to debug the ProtocolReceive callback
 * which is typically not used in case the underlying miniport indicates the packets with NdisMIndicateReceivePacket
 * the best way to debug the ProtocolReceive (which in turn has several branches) is to enable the DEBUG_NETFLT_RECV
 * one by one in the below order, i.e.
 * first DEBUG_NETFLT_RECV
 * then DEBUG_NETFLT_RECV + DEBUG_NETFLT_RECV_NOPACKET */
//# define DEBUG_NETFLT_RECV
//# define DEBUG_NETFLT_RECV_NOPACKET
//# define DEBUG_NETFLT_RECV_TRANSFERDATA
#endif

#define LOG_GROUP LOG_GROUP_NET_FLT_DRV

#include <VBox/intnet.h>
#include <VBox/log.h>
#include <VBox/err.h>
#include <VBox/version.h>
#include <iprt/initterm.h>
#include <iprt/assert.h>
#include <iprt/spinlock.h>
#include <iprt/semaphore.h>
#include <iprt/process.h>
#include <iprt/alloc.h>
#include <iprt/alloca.h>
#include <iprt/time.h>
#include <iprt/net.h>

RT_C_DECLS_BEGIN
#include <ndis.h>
RT_C_DECLS_END



#define VBOXNETFLT_OS_SPECFIC 1

#ifdef VBOX_NETFLT_ONDEMAND_BIND
# define VBOXNETFLT_PROTOCOL_NAME L"VBoxNetFltPt"
#else
# ifndef VBOXNETADP
#  define VBOXNETFLT_PROTOCOL_NAME L"VBoxNetFlt"

/** this is to support ioctl interface */
#  define LINKNAME_STRING     L"\\DosDevices\\Global\\VBoxNetFlt"
#  define NTDEVICE_STRING     L"\\Device\\VBoxNetFlt"
# else
#  define LINKNAME_STRING     L"\\DosDevices\\Global\\VBoxNetAdp"
#  define NTDEVICE_STRING     L"\\Device\\VBoxNetAdp"
# endif
//# define VBOXNETFLT_WIN_IOCTL_INIT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER,  FILE_WRITE_ACCESS)
//# define VBOXNETFLT_WIN_IOCTL_FINI CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER,  FILE_WRITE_ACCESS)
#endif

/** version
 * NOTE: we are NOT using NDIS 5.1 features now, the code under "#ifdef NDIS51xxx" is not tested and may not work and should be removed soon */
#ifdef NDIS51_MINIPORT
# define VBOXNETFLT_MAJOR_NDIS_VERSION            5
# define VBOXNETFLT_MINOR_NDIS_VERSION            1
#else
# define VBOXNETFLT_MAJOR_NDIS_VERSION            5
# define VBOXNETFLT_MINOR_NDIS_VERSION            0
#endif

#ifdef NDIS51
# define VBOXNETFLT_PROT_MAJOR_NDIS_VERSION    5
# define VBOXNETFLT_PROT_MINOR_NDIS_VERSION    0
#else
# define VBOXNETFLT_PROT_MAJOR_NDIS_VERSION    5
# define VBOXNETFLT_PROT_MINOR_NDIS_VERSION    0
#endif

/** advance declaration */
typedef struct _ADAPT ADAPT, *PADAPT;

typedef struct VBOXNETFLTINS *PVBOXNETFLTINS;

/** configuration */

/** received packets queue size. the queue is used when the driver is working in a pass-thru mode */
#define MAX_RECEIVE_PACKET_ARRAY_SIZE           40

/** Ndis Packet pool settings
 * these are applied to both receive and send packet pools */
#define MAX_PACKET_POOL_SIZE 0x0000FFFF
#define MIN_PACKET_POOL_SIZE 0x000000FF

/** packet queue size used when the driver is working in the "active" mode */
#define PACKET_INFO_POOL_SIZE 0x0000FFFF

#ifndef VBOXNETADP
/** memory tag used for memory allocations
 * (VBNF stands for VBox NetFlt) */
# define MEM_TAG 'FNBV'
#else
/** memory tag used for memory allocations
 * (VBNA stands for VBox NetAdp) */
# define MEM_TAG 'ANBV'
#endif

/** receive and transmit Ndis buffer pool size */
#define TX_BUFFER_POOL_SIZE 128
#define RX_BUFFER_POOL_SIZE 128

#define ETH_HEADER_SIZE 14

#define PACKET_QUEUE_SG_SEGS_ALLOC 32

#define VBOX_NETFLT_PACKET_HEADER_MATCH_SIZE 24

#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
# define VBOXNETFLT_PACKETMATCH_LENGTH (ETH_HEADER_SIZE + 2)
#endif

#ifdef VBOXNETADP
#define     VBOXNETADP_HEADER_SIZE             14
#define     VBOXNETADP_MAX_DATA_SIZE           1500
#define     VBOXNETADP_MAX_PACKET_SIZE         VBOXNETADP_HEADER_SIZE + VBOXNETADP_MAX_DATA_SIZE
#define     VBOXNETADP_MIN_PACKET_SIZE         60
#define     VBOXNETADP_LINK_SPEED 1000000 //The unit of measurement is 100 bps, 100Mbps
#define VBOXNETADP_MAX_LOOKAHEAD_SIZE VBOXNETADP_MAX_DATA_SIZE
#define VBOXNETADP_VENDOR_ID 0x080027
#define VBOXNETADP_VENDOR_DRIVER_VERSION       0x00010000
#define VBOXNETADP_VENDOR_DESC "Sun"
#define VBOXNETADP_MAX_MCAST_LIST 32
#define VBOXNETADP_ETH_ADDRESS_LENGTH 6

//#define VBOXNETADP_REPORT_DISCONNECTED
#endif
/* type defs */

/** Flag specifying that the type of enqueued packet
 * if set the info contains the PINTNETSG packet
 * if clear the packet info contains the PNDIS_PACKET packet
 * Typically the packet queue we are maintaining contains PNDIS_PACKETs only,
 * however in case the underlying miniport indicates a packet with the NDIS_STATUS_RESOURCES status
 * we MUST return the packet back to the miniport immediately
 * this is why we are creating the INTNETSG, copying the ndis packet info there and enqueueing it */
#define PACKET_SG                   0x00000001

/** the flag specifying that the packet source
 * if set the packet comes from the host (upperlying protocol)
 * if clear the packet comes from the wire (underlying miniport) */
#define PACKET_SRC_HOST             0x00000002

#ifndef VBOXNETFLT_NO_PACKET_QUEUE
/** flag specifying the packet was originated by our driver
 * i.e. we could use it on our needs and should not return it
 * we are enqueueing "our" packets on ProtocolReceive call-back when
 * Ndis does not give us a receive acket (the driver below us has called NdisM..IndicateReceive)
 * this is supported for Ndis Packet only */
#define PACKET_MINE                 0x00000004

/** flag passed to vboxNetFltWinQuEnqueuePacket specifying that the packet should be copied
 * this is supported for Ndis Packet only */
#define PACKET_COPY                 0x00000008
#endif

/** packet queue element containing the packet info */
typedef struct _PACKET_INFO
{
    /** list entry used for enqueueing the info */
    LIST_ENTRY ListEntry;
    /** pointer to the pool containing this packet info */
    struct _PACKET_INFO_POOL * pPool;
    /** flags describing the referenced packet. Contains PACKET_xxx flags (i.e. PACKET_SG, PACKET_SRC_HOST) */
    uint32_t fFlags;
    /** pointer to the packet this info represents */
    PVOID pPacket;
}PACKET_INFO, *PPACKET_INFO;

/* paranoid check to make sure the elements in the packet info array are properly aligned */
C_ASSERT((sizeof(PACKET_INFO) & (sizeof(PVOID) - 1)) == 0);

/** represents the packet queue */
typedef LIST_ENTRY PACKET_QUEUE, *PPACKET_QUEUE;

/*
 * we are using non-interlocked versions of LIST_ENTRY-related operations macros and synchronize
 * access to the queue and its elements by aquiring/releasing a spinlock using Ndis[Acquire,Release]Spinlock
 *
 * we are NOT using interlocked versions of insert/remove head/tail list functions because we need to iterate though
 * the queue elements as well as remove elements from the midle of the queue
 *
 * * @todo: it seems that we can switch to using interlocked versions of list-entry functions
 * since we have removed all functionality (mentioned above, i.e. queue elements iteration, etc.) that might prevent us from doing this
 */
typedef struct _INTERLOCKED_PACKET_QUEUE
{
    /** queue */
    PACKET_QUEUE Queue;
    /** queue lock */
    NDIS_SPIN_LOCK Lock;
}INTERLOCKED_PACKET_QUEUE, *PINTERLOCKED_PACKET_QUEUE;

typedef struct _SINGLE_LIST
{
    /** queue */
    SINGLE_LIST_ENTRY Head;
    /** pointer to the list tail. used to enqueue elements to the tail of the list */
    PSINGLE_LIST_ENTRY pTail;
} SINGLE_LIST, *PSINGLE_LIST;

typedef struct _INTERLOCKED_SINGLE_LIST
{
    /** queue */
    SINGLE_LIST List;
    /** queue lock */
    NDIS_SPIN_LOCK Lock;
} INTERLOCKED_SINGLE_LIST, *PINTERLOCKED_SINGLE_LIST;

/** packet info pool contains free packet info elements to be used for the packet queue
 * we are using the pool mechanism to allocate packet queue elements
 * the pool mechanism is pretty simple now, we are allocating a bunch of memory
 * for maintaining PACKET_INFO_POOL_SIZE queue elements and just returning null when the pool is exhausted
 * This mechanism seems to be enough for now since we are using PACKET_INFO_POOL_SIZE = 0xffff which is
 * the maximum size of packets the ndis packet pool supports */
typedef struct _PACKET_INFO_POOL
{
    /** free packet info queue */
    INTERLOCKED_PACKET_QUEUE Queue;
    /** memory bugger used by the pool */
    PVOID pBuffer;
}PACKET_INFO_POOL, *PPACKET_INFO_POOL;

typedef enum VBOXNETDEVOPSTATE
{
    kVBoxNetDevOpState_InvalidValue = 0,
    kVBoxNetDevOpState_Initializing,
    kVBoxNetDevOpState_Initialized,
    kVBoxNetDevOpState_Deinitializing,
    kVBoxNetDevOpState_Deinitialized,

} VBOXNETDEVOPSTATE;

typedef enum VBOXADAPTSTATE
{
   /** The usual invalid state. */
    kVBoxAdaptState_Invalid = 0,
    /** Initialization. */
    kVBoxAdaptState_Connecting,
    /** Connected fuly functional state */
    kVBoxAdaptState_Connected,
    /** Disconnecting  */
    kVBoxAdaptState_Disconnecting,
    /** Disconnected  */
    kVBoxAdaptState_Disconnected,
} VBOXADAPTSTATE;

/** structure used to maintain the state and reference count of the miniport and protocol */
typedef struct _ADAPT_DEVICE
{
    /** initialize state */
    VBOXNETDEVOPSTATE              OpState;
    /** ndis power state */
    NDIS_DEVICE_POWER_STATE        PowerState;
    /** reference count */
    uint32_t                       cReferences;
/*    NDIS_HANDLE                    hHandle; */
} ADAPT_DEVICE, *PADAPT_DEVICE;

/* packet filter processing mode constants */
#define VBOXNETFLT_PFP_NETFLT   1
#define VBOXNETFLT_PFP_PASSTHRU 2

/** represents filter driver device context*/
typedef struct _ADAPT
{
#ifndef VBOXNETADP
    /** handle the lower miniport */
    NDIS_HANDLE                    hBindingHandle;
    /** Protocol's Device state */
    ADAPT_DEVICE             PTState;
#endif
#ifndef VBOX_NETFLT_ONDEMAND_BIND
    /** NDIS Handle to for miniport up-calls */
    NDIS_HANDLE                    hMiniportHandle;
    /** miniport device state */
    ADAPT_DEVICE             MPState;
    /** ndis packet pool used for receives */
    NDIS_HANDLE                    hRecvPacketPoolHandle;
    /** ndis buffer pool used for receives */
    NDIS_HANDLE                    hRecvBufferPoolHandle;
#ifndef VBOXNETADP
    /** This is used to wrap a request coming down to us.
     * This exploits the fact that requests are serialized down to us.*/
    NDIS_REQUEST                   Request;
    /** Ndis Request Bytes needed */
    PULONG                         BytesNeeded;
    /** Ndis Request Bytes Read or Written */
    PULONG                         BytesReadOrWritten;
#else
    volatile ULONG cTxSuccess;
    volatile ULONG cRxSuccess;
    volatile ULONG cTxError;
    volatile ULONG cRxError;
#endif
    /** driver bind adapter state. */
    VBOXADAPTSTATE                 enmState;
#ifndef VBOXNETADP
    /** true if we should indicate the receive complete used by the ProtocolReceeive mechanism */
    bool                        bIndicateRcvComplete;

    /** TRUE iff a request is pending at the miniport below */
    bool                        bOutstandingRequests;
    /**  TRUE iff a request is queued at this IM miniport*/
    bool                        bQueuedRequest;
    /** @todo join all boolean states to one field treated as flags bitmap */
    /** true iff we are processing Set packet filter OID */
    uint8_t                        fProcessingPacketFilter;
    /** true iff the upper protocol filter cache was initialized */
    bool                        bUpperProtSetFilterInitialized;
    /** trus if the adapter is closing */
    bool                        bClosingAdapter;
    /** Pending transfer data packet queue (i.e. packets that were indicated as pending on NdisTransferData call */
    INTERLOCKED_SINGLE_LIST     TransferDataList;
    /* mac options initialized on OID_GEN_MAC_OPTIONS */
    ULONG                          fMacOptions;
    /** For initializing the miniport edge */
    NDIS_STRING                    DeviceName;
    /** For blocking UnbindAdapter while an IM Init is in progress.*/
    NDIS_EVENT                     MiniportInitEvent;
    /** The last indicated media status */
    NDIS_STATUS                    LastIndicatedStatus;
    /** The latest suppressed media status */
    NDIS_STATUS                    LatestUnIndicateStatus;
    /** when working in the passthru mode the driver puts the received packets to this array
     * instead of passing them up immediately
     * we are flushing the packets on ProtocolReceiveComplete or when the underlying miniport
     * indicates NDIS_STATUS_RESOURCES or when this array is full */
    PNDIS_PACKET                   aReceivedPackets[MAX_RECEIVE_PACKET_ARRAY_SIZE];
    /** number of packets in the aReceivedPackets array*/
    ULONG                          cReceivedPacketCount;
    /** packet filter flags set by the upper protocols */
    ULONG                          fUpperProtocolSetFilter;
    /** packet filter flags set by the upper protocols */
    ULONG                          fSetFilterBuffer;
    /** packet filter flags set by us */
    ULONG                          fOurSetFilter;
#endif /* !VBOXNETADP */
#endif /* !VBOX_NETFLT_ONDEMAND_BIND */

#ifndef VBOXNETADP
#if defined(DEBUG_NETFLT_LOOPBACK) || !defined(VBOX_LOOPBACK_USEFLAGS)
    /** used for maintaining the pending send packets for handling packet loopback */
    INTERLOCKED_SINGLE_LIST SendPacketQueue;
#endif
    /** used for serializing calls to the NdisRequest in the vboxNetFltWinSynchNdisRequest */
    RTSEMFASTMUTEX                 hSynchRequestMutex;
    /** event used to synchronize with the Ndis Request completion in the vboxNetFltWinSynchNdisRequest */
    KEVENT                         hSynchCompletionEvent;
    /** status of the Ndis Request initiated by the vboxNetFltWinSynchNdisRequest */
    NDIS_STATUS   volatile         fSynchCompletionStatus;
    /** pointer to the Ndis Request being executed by the vboxNetFltWinSynchNdisRequest */
    PNDIS_REQUEST volatile         pSynchRequest;
    /** ndis packet pool used for sends */
    NDIS_HANDLE                    hSendPacketPoolHandle;
    /** ndis buffer pool used for sends */
    NDIS_HANDLE                    hSendBufferPoolHandle;
    /** open/close adapter status.
     * Since ndis adapter open and close requests may complete assynchronously,
     * we are using event mechanism to wait for open/close completion
     * the status field is being set by the completion call-back */
    NDIS_STATUS                    Status;
    /** open/close adaptor completion event */
    NDIS_EVENT                     hEvent;
    /** medium we are attached to */
    NDIS_MEDIUM                    Medium;
//    /** physical medium we are attached to */
//    NDIS_PHYSICAL_MEDIUM           PhMedium;
    /** True - When the miniport or protocol is transitioning from a D0 to Standby (>D0) State
     *  False - At all other times, - Flag is cleared after a transition to D0 */
    BOOLEAN                        bStandingBy;
#endif
} ADAPT, *PADAPT;

typedef struct _PACKET_QUEUE_WORKER
{
    /** this event is used to initiate a packet queue worker thread kill */
    KEVENT                         KillEvent;
    /** this event is used to notify a worker thread that the packets are added to the queue */
    KEVENT                         NotifyEvent;
    /** pointer to the packet queue worker thread object */
    PKTHREAD                       pThread;
    /** pointer to the SG used by the packet queue for IntNet receive notifications */
    PINTNETSG                      pSG;
    /** Packet queue */
    INTERLOCKED_PACKET_QUEUE       PacketQueue;
    /** Packet info pool, i.e. the pool for the packet queue elements */
    PACKET_INFO_POOL               PacketInfoPool;
} PACKET_QUEUE_WORKER, *PPACKET_QUEUE_WORKER;

/** Protocol reserved part of a sent packet that is allocated by us. */
typedef struct _SEND_RSVD
{
    /** original packet receiver from the upperlying protocol
     * can be null if the packet was originated by intnet */
    PNDIS_PACKET    pOriginalPkt;
    /** pointer to the buffer to be freed on send completion
     * can be null if no buffer is to be freed */
    PVOID           pBufToFree;
#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
    SINGLE_LIST_ENTRY ListEntry;
    /* true if the packet is from IntNet */
    bool bFromIntNet;
#endif
} SEND_RSVD, *PSEND_RSVD;

/** represents the data stored in the protocol recerved field of ndis packet on NdisTransferData processing*/
typedef struct _TRANSFERDATA_RSVD
{
    /** next packet in a list */
    SINGLE_LIST_ENTRY ListEntry;
    /* packet buffer start */
    PNDIS_BUFFER pOriginalBuffer;
} TRANSFERDATA_RSVD, *PTRANSFERDATA_RSVD;

/** Miniport reserved part of a received packet that is allocated by
 * us. Note that this should fit into the MiniportReserved space
 * in an NDIS_PACKET. */
typedef struct _RECV_RSVD
{
    /** original packet receiver from the underling miniport
     * can be null if the packet was originated by intnet */
    PNDIS_PACKET    pOriginalPkt;
    /** pointer to the buffer to be freed on receive completion
     * can be null if no buffer is to be freed */
    PVOID           pBufToFree;
} RECV_RSVD, *PRECV_RSVD;

#ifndef VBOX_NETFLT_ONDEMAND_BIND

C_ASSERT(sizeof(RECV_RSVD) <= sizeof(((PNDIS_PACKET)0)->MiniportReserved));
C_ASSERT(sizeof(TRANSFERDATA_RSVD) <= PROTOCOL_RESERVED_SIZE_IN_PACKET);
#endif

C_ASSERT(sizeof(NDIS_DEVICE_POWER_STATE) == sizeof(uint32_t));
C_ASSERT(sizeof(UINT) == sizeof(uint32_t));

#ifdef VBOX_LOOPBACK_USEFLAGS
#define NDIS_FLAGS_SKIP_LOOPBACK_W2K    0x400
#endif

#include "../VBoxNetFltInternal.h"
#include "VBoxNetFlt-win.h"
#ifndef VBOXNETADP
#include "VBoxNetFltPt-win.h"
#endif
#ifndef VBOX_NETFLT_ONDEMAND_BIND
# include "VBoxNetFltMp-win.h"
#endif

#ifdef DEBUG_NETFLT_NOASSERT
# ifdef Assert
#  undef Assert
# endif

# define Assert(_expr) do {} while (0)
#endif /* #ifdef DEBUG_NETFLT_NOASSERT */

#endif