1 /*
2  *  TAP-Windows -- A kernel driver to provide virtual tap
3  *                 device functionality on Windows.
4  *
5  *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
6  *
7  *  This source code is Copyright (C) 2002-2014 OpenVPN Technologies, Inc.,
8  *  and is released under the GPL version 2 (see below).
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2
12  *  as published by the Free Software Foundation.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program (see the file COPYING included with this
21  *  distribution); if not, write to the Free Software Foundation, Inc.,
22  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24 
25 //------------------
26 // Memory Management
27 //------------------
28 
29 #include "tap.h"
30 
31 PVOID
MemAlloc(__in ULONG p_Size,__in BOOLEAN zero)32 MemAlloc(
33     __in ULONG p_Size,
34     __in BOOLEAN zero
35     )
36 {
37     PVOID l_Return = NULL;
38 
39     if (p_Size)
40     {
41         __try
42         {
43             if (NdisAllocateMemoryWithTag (&l_Return, p_Size, 'APAT')
44                 == NDIS_STATUS_SUCCESS)
45             {
46                 if (zero)
47                 {
48                     NdisZeroMemory (l_Return, p_Size);
49                 }
50             }
51             else
52             {
53                 l_Return = NULL;
54             }
55         }
56         __except (EXCEPTION_EXECUTE_HANDLER)
57         {
58             l_Return = NULL;
59         }
60     }
61 
62     return l_Return;
63 }
64 
65 VOID
MemFree(__in PVOID p_Addr,__in ULONG p_Size)66 MemFree(
67     __in PVOID p_Addr,
68     __in ULONG p_Size
69     )
70 {
71     if (p_Addr && p_Size)
72     {
73         __try
74         {
75 #if DBG
76             NdisZeroMemory (p_Addr, p_Size);
77 #endif
78             NdisFreeMemory (p_Addr, p_Size, 0);
79         }
80         __except (EXCEPTION_EXECUTE_HANDLER)
81         {
82         }
83     }
84 }
85 
86 //======================================================================
87 // TAP Packet Queue Support
88 //======================================================================
89 
90 VOID
tapPacketQueueInsertTail(__in PTAP_PACKET_QUEUE TapPacketQueue,__in PTAP_PACKET TapPacket)91 tapPacketQueueInsertTail(
92     __in PTAP_PACKET_QUEUE  TapPacketQueue,
93     __in PTAP_PACKET        TapPacket
94     )
95 {
96     KIRQL  irql;
97 
98     KeAcquireSpinLock(&TapPacketQueue->QueueLock,&irql);
99 
100     InsertTailList(&TapPacketQueue->Queue,&TapPacket->QueueLink);
101 
102     // BUGBUG!!! Enforce PACKET_QUEUE_SIZE queue count limit???
103     // For NDIS 6 there is no per-packet status, so this will need to
104     // be handled on per-NBL basis in AdapterSendNetBufferLists...
105 
106     // Update counts
107     ++TapPacketQueue->Count;
108 
109     if(TapPacketQueue->Count > TapPacketQueue->MaxCount)
110     {
111         TapPacketQueue->MaxCount = TapPacketQueue->Count;
112 
113         DEBUGP (("[TAP] tapPacketQueueInsertTail: New MAX queued packet count = %d\n",
114             TapPacketQueue->MaxCount));
115     }
116 
117     KeReleaseSpinLock(&TapPacketQueue->QueueLock,irql);
118 }
119 
120 // Call with QueueLock held
121 PTAP_PACKET
tapPacketRemoveHeadLocked(__in PTAP_PACKET_QUEUE TapPacketQueue)122 tapPacketRemoveHeadLocked(
123     __in PTAP_PACKET_QUEUE  TapPacketQueue
124     )
125 {
126     PTAP_PACKET     tapPacket = NULL;
127     PLIST_ENTRY     listEntry;
128 
129     listEntry = RemoveHeadList(&TapPacketQueue->Queue);
130 
131     if(listEntry != &TapPacketQueue->Queue)
132     {
133         tapPacket = CONTAINING_RECORD(listEntry, TAP_PACKET, QueueLink);
134 
135         // Update counts
136         --TapPacketQueue->Count;
137     }
138 
139     return tapPacket;
140 }
141 
142 PTAP_PACKET
tapPacketRemoveHead(__in PTAP_PACKET_QUEUE TapPacketQueue)143 tapPacketRemoveHead(
144     __in PTAP_PACKET_QUEUE  TapPacketQueue
145     )
146 {
147     PTAP_PACKET     tapPacket = NULL;
148     KIRQL           irql;
149 
150     KeAcquireSpinLock(&TapPacketQueue->QueueLock,&irql);
151 
152     tapPacket = tapPacketRemoveHeadLocked(TapPacketQueue);
153 
154     KeReleaseSpinLock(&TapPacketQueue->QueueLock,irql);
155 
156     return tapPacket;
157 }
158 
159 VOID
tapPacketQueueInitialize(__in PTAP_PACKET_QUEUE TapPacketQueue)160 tapPacketQueueInitialize(
161     __in PTAP_PACKET_QUEUE  TapPacketQueue
162     )
163 {
164     KeInitializeSpinLock(&TapPacketQueue->QueueLock);
165 
166     NdisInitializeListHead(&TapPacketQueue->Queue);
167 }
168 
169 //======================================================================
170 // TAP Cancel-Safe Queue Support
171 //======================================================================
172 
173 VOID
tapIrpCsqInsert(__in struct _IO_CSQ * Csq,__in PIRP Irp)174 tapIrpCsqInsert (
175     __in struct _IO_CSQ    *Csq,
176     __in PIRP              Irp
177     )
178 {
179     PTAP_IRP_CSQ          tapIrpCsq;
180 
181     tapIrpCsq = (PTAP_IRP_CSQ )Csq;
182 
183     InsertTailList(
184         &tapIrpCsq->Queue,
185         &Irp->Tail.Overlay.ListEntry
186         );
187 
188     // Update counts
189     ++tapIrpCsq->Count;
190 
191     if(tapIrpCsq->Count > tapIrpCsq->MaxCount)
192     {
193         tapIrpCsq->MaxCount = tapIrpCsq->Count;
194 
195         DEBUGP (("[TAP] tapIrpCsqInsert: New MAX queued IRP count = %d\n",
196             tapIrpCsq->MaxCount));
197     }
198 }
199 
200 VOID
tapIrpCsqRemoveIrp(__in PIO_CSQ Csq,__in PIRP Irp)201 tapIrpCsqRemoveIrp(
202     __in PIO_CSQ Csq,
203     __in PIRP    Irp
204     )
205 {
206     PTAP_IRP_CSQ          tapIrpCsq;
207 
208     tapIrpCsq = (PTAP_IRP_CSQ )Csq;
209 
210     // Update counts
211     --tapIrpCsq->Count;
212 
213     RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
214 }
215 
216 
217 PIRP
tapIrpCsqPeekNextIrp(__in PIO_CSQ Csq,__in PIRP Irp,__in PVOID PeekContext)218 tapIrpCsqPeekNextIrp(
219     __in PIO_CSQ Csq,
220     __in PIRP    Irp,
221     __in PVOID   PeekContext
222     )
223 {
224     PTAP_IRP_CSQ          tapIrpCsq;
225     PIRP                    nextIrp = NULL;
226     PLIST_ENTRY             nextEntry;
227     PLIST_ENTRY             listHead;
228     PIO_STACK_LOCATION      irpStack;
229 
230     tapIrpCsq = (PTAP_IRP_CSQ )Csq;
231 
232     listHead = &tapIrpCsq->Queue;
233 
234     //
235     // If the IRP is NULL, we will start peeking from the listhead, else
236     // we will start from that IRP onwards. This is done under the
237     // assumption that new IRPs are always inserted at the tail.
238     //
239 
240     if (Irp == NULL)
241     {
242         nextEntry = listHead->Flink;
243     }
244     else
245     {
246         nextEntry = Irp->Tail.Overlay.ListEntry.Flink;
247     }
248 
249     while(nextEntry != listHead)
250     {
251         nextIrp = CONTAINING_RECORD(nextEntry, IRP, Tail.Overlay.ListEntry);
252 
253         irpStack = IoGetCurrentIrpStackLocation(nextIrp);
254 
255         //
256         // If context is present, continue until you find a matching one.
257         // Else you break out as you got next one.
258         //
259         if (PeekContext)
260         {
261             if (irpStack->FileObject == (PFILE_OBJECT) PeekContext)
262             {
263                 break;
264             }
265         }
266         else
267         {
268             break;
269         }
270 
271         nextIrp = NULL;
272         nextEntry = nextEntry->Flink;
273     }
274 
275     return nextIrp;
276 }
277 
278 //
279 // tapIrpCsqAcquireQueueLock modifies the execution level of the current processor.
280 //
281 // KeAcquireSpinLock raises the execution level to Dispatch Level and stores
282 // the current execution level in the Irql parameter to be restored at a later
283 // time.  KeAcqurieSpinLock also requires us to be running at no higher than
284 // Dispatch level when it is called.
285 //
286 // The annotations reflect these changes and requirments.
287 //
288 
289 __drv_raisesIRQL(DISPATCH_LEVEL)
__drv_maxIRQL(DISPATCH_LEVEL)290 __drv_maxIRQL(DISPATCH_LEVEL)
291 VOID
292 tapIrpCsqAcquireQueueLock(
293      __in PIO_CSQ Csq,
294      __out PKIRQL  Irql
295     )
296 {
297     PTAP_IRP_CSQ          tapIrpCsq;
298 
299     tapIrpCsq = (PTAP_IRP_CSQ )Csq;
300 
301     //
302     // Suppressing because the address below csq is valid since it's
303     // part of TAP_ADAPTER_CONTEXT structure.
304     //
305 #pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'adapter->PendingReadCsqQueueLock'")
306     KeAcquireSpinLock(&tapIrpCsq->QueueLock, Irql);
307 }
308 
309 //
310 // tapIrpCsqReleaseQueueLock modifies the execution level of the current processor.
311 //
312 // KeReleaseSpinLock assumes we already hold the spin lock and are therefore
313 // running at Dispatch level.  It will use the Irql parameter saved in a
314 // previous call to KeAcquireSpinLock to return the thread back to it's original
315 // execution level.
316 //
317 // The annotations reflect these changes and requirments.
318 //
319 
__drv_requiresIRQL(DISPATCH_LEVEL)320 __drv_requiresIRQL(DISPATCH_LEVEL)
321 VOID
322 tapIrpCsqReleaseQueueLock(
323      __in PIO_CSQ Csq,
324      __in KIRQL   Irql
325     )
326 {
327     PTAP_IRP_CSQ          tapIrpCsq;
328 
329     tapIrpCsq = (PTAP_IRP_CSQ )Csq;
330 
331     //
332     // Suppressing because the address below csq is valid since it's
333     // part of TAP_ADAPTER_CONTEXT structure.
334     //
335 #pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'adapter->PendingReadCsqQueueLock'")
336     KeReleaseSpinLock(&tapIrpCsq->QueueLock, Irql);
337 }
338 
339 VOID
tapIrpCsqCompleteCanceledIrp(__in PIO_CSQ pCsq,__in PIRP Irp)340 tapIrpCsqCompleteCanceledIrp(
341     __in  PIO_CSQ             pCsq,
342     __in  PIRP                Irp
343     )
344 {
345     UNREFERENCED_PARAMETER(pCsq);
346 
347     Irp->IoStatus.Status = STATUS_CANCELLED;
348     Irp->IoStatus.Information = 0;
349     IoCompleteRequest(Irp, IO_NO_INCREMENT);
350 }
351 
352 VOID
tapIrpCsqInitialize(__in PTAP_IRP_CSQ TapIrpCsq)353 tapIrpCsqInitialize(
354     __in PTAP_IRP_CSQ  TapIrpCsq
355     )
356 {
357     KeInitializeSpinLock(&TapIrpCsq->QueueLock);
358 
359     NdisInitializeListHead(&TapIrpCsq->Queue);
360 
361     IoCsqInitialize(
362         &TapIrpCsq->CsqQueue,
363         tapIrpCsqInsert,
364         tapIrpCsqRemoveIrp,
365         tapIrpCsqPeekNextIrp,
366         tapIrpCsqAcquireQueueLock,
367         tapIrpCsqReleaseQueueLock,
368         tapIrpCsqCompleteCanceledIrp
369         );
370 }
371 
372 VOID
tapIrpCsqFlush(__in PTAP_IRP_CSQ TapIrpCsq)373 tapIrpCsqFlush(
374     __in PTAP_IRP_CSQ  TapIrpCsq
375     )
376 {
377     PIRP    pendingIrp;
378 
379     //
380     // Flush the pending read IRP queue.
381     //
382     pendingIrp = IoCsqRemoveNextIrp(
383                     &TapIrpCsq->CsqQueue,
384                     NULL
385                     );
386 
387     while(pendingIrp)
388     {
389         // Cancel the IRP
390         pendingIrp->IoStatus.Information = 0;
391         pendingIrp->IoStatus.Status = STATUS_CANCELLED;
392         IoCompleteRequest(pendingIrp, IO_NO_INCREMENT);
393 
394         pendingIrp = IoCsqRemoveNextIrp(
395                         &TapIrpCsq->CsqQueue,
396                         NULL
397                         );
398     }
399 
400     ASSERT(IsListEmpty(&TapIrpCsq->Queue));
401 }
402