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 // Include files.
27 //
28
29 #include "tap.h"
30
31 //======================================================================
32 // TAP Receive Path Support
33 //======================================================================
34
35 #ifdef ALLOC_PRAGMA
36 #pragma alloc_text( PAGE, TapDeviceWrite)
37 #endif // ALLOC_PRAGMA
38
39 //===============================================================
40 // Used in cases where internally generated packets such as
41 // ARP or DHCP replies must be returned to the kernel, to be
42 // seen as an incoming packet "arriving" on the interface.
43 //===============================================================
44
45 VOID
IndicateReceivePacket(__in PTAP_ADAPTER_CONTEXT Adapter,__in PUCHAR packetData,__in const unsigned int packetLength)46 IndicateReceivePacket(
47 __in PTAP_ADAPTER_CONTEXT Adapter,
48 __in PUCHAR packetData,
49 __in const unsigned int packetLength
50 )
51 {
52 PUCHAR injectBuffer;
53
54 //
55 // Handle miniport Pause
56 // ---------------------
57 // NDIS 6 miniports implement a temporary "Pause" state normally followed
58 // by the Restart. While in the Pause state it is forbidden for the miniport
59 // to indicate receive NBLs.
60 //
61 // That is: The device interface may be "up", but the NDIS miniport send/receive
62 // interface may be temporarily "down".
63 //
64 // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas inject path
65 // the code below will simply ignore inject packets passed to the driver while
66 // the miniport is in the Paused state.
67 //
68 // The correct implementation is to go ahead and build the NBLs corresponding
69 // to the inject packet - but queue them. When Restart is entered the
70 // queued NBLs would be dequeued and indicated to the host.
71 //
72 if(tapAdapterSendAndReceiveReady(Adapter) != NDIS_STATUS_SUCCESS)
73 {
74 DEBUGP (("[%s] Lying send in IndicateReceivePacket while adapter paused\n",
75 MINIPORT_INSTANCE_ID (Adapter)));
76
77 return;
78 }
79
80 // Allocate flat buffer for packet data.
81 injectBuffer = (PUCHAR )NdisAllocateMemoryWithTagPriority(
82 Adapter->MiniportAdapterHandle,
83 packetLength,
84 TAP_RX_INJECT_BUFFER_TAG,
85 NormalPoolPriority
86 );
87
88 if( injectBuffer)
89 {
90 PMDL mdl;
91
92 // Copy packet data to flat buffer.
93 NdisMoveMemory (injectBuffer, packetData, packetLength);
94
95 // Allocate MDL for flat buffer.
96 mdl = NdisAllocateMdl(
97 Adapter->MiniportAdapterHandle,
98 injectBuffer,
99 packetLength
100 );
101
102 if( mdl )
103 {
104 PNET_BUFFER_LIST netBufferList;
105
106 mdl->Next = NULL; // No next MDL
107
108 // Allocate the NBL and NB. Link MDL chain to NB.
109 netBufferList = NdisAllocateNetBufferAndNetBufferList(
110 Adapter->ReceiveNblPool,
111 0, // ContextSize
112 0, // ContextBackFill
113 mdl, // MDL chain
114 0,
115 packetLength
116 );
117
118 if(netBufferList != NULL)
119 {
120 ULONG receiveFlags = 0;
121 LONG nblCount;
122
123 NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL
124
125 if(KeGetCurrentIrql() == DISPATCH_LEVEL)
126 {
127 receiveFlags |= NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL;
128 }
129
130 // Set flag indicating that this is an injected packet
131 TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList);
132 TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED);
133
134 netBufferList->MiniportReserved[0] = NULL;
135 netBufferList->MiniportReserved[1] = NULL;
136
137 // Increment in-flight receive NBL count.
138 nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount);
139 ASSERT(nblCount > 0 );
140
141 netBufferList->SourceHandle = Adapter->MiniportAdapterHandle;
142
143 //
144 // Indicate the packet
145 // -------------------
146 // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length
147 // contains the complete packet including Ethernet header and payload.
148 //
149 NdisMIndicateReceiveNetBufferLists(
150 Adapter->MiniportAdapterHandle,
151 netBufferList,
152 NDIS_DEFAULT_PORT_NUMBER,
153 1, // NumberOfNetBufferLists
154 receiveFlags
155 );
156
157 return;
158 }
159 else
160 {
161 DEBUGP (("[%s] NdisAllocateNetBufferAndNetBufferList failed in IndicateReceivePacket\n",
162 MINIPORT_INSTANCE_ID (Adapter)));
163 NOTE_ERROR ();
164
165 NdisFreeMdl(mdl);
166 NdisFreeMemory(injectBuffer,0,0);
167 }
168 }
169 else
170 {
171 DEBUGP (("[%s] NdisAllocateMdl failed in IndicateReceivePacket\n",
172 MINIPORT_INSTANCE_ID (Adapter)));
173 NOTE_ERROR ();
174
175 NdisFreeMemory(injectBuffer,0,0);
176 }
177 }
178 else
179 {
180 DEBUGP (("[%s] NdisAllocateMemoryWithTagPriority failed in IndicateReceivePacket\n",
181 MINIPORT_INSTANCE_ID (Adapter)));
182 NOTE_ERROR ();
183 }
184 }
185
186 VOID
tapCompleteIrpAndFreeReceiveNetBufferList(__in PTAP_ADAPTER_CONTEXT Adapter,__in PNET_BUFFER_LIST NetBufferList,__in NTSTATUS IoCompletionStatus)187 tapCompleteIrpAndFreeReceiveNetBufferList(
188 __in PTAP_ADAPTER_CONTEXT Adapter,
189 __in PNET_BUFFER_LIST NetBufferList, // Only one NB here...
190 __in NTSTATUS IoCompletionStatus
191 )
192 {
193 PIRP irp;
194 ULONG frameType, netBufferCount, byteCount;
195 LONG nblCount;
196
197 // Fetch NB frame type.
198 frameType = tapGetNetBufferFrameType(NET_BUFFER_LIST_FIRST_NB(NetBufferList));
199
200 // Fetch statistics for all NBs linked to the NB.
201 netBufferCount = tapGetNetBufferCountsFromNetBufferList(
202 NetBufferList,
203 &byteCount
204 );
205
206 // Update statistics by frame type
207 if(IoCompletionStatus == STATUS_SUCCESS)
208 {
209 switch(frameType)
210 {
211 case NDIS_PACKET_TYPE_DIRECTED:
212 Adapter->FramesRxDirected += netBufferCount;
213 Adapter->BytesRxDirected += byteCount;
214 break;
215
216 case NDIS_PACKET_TYPE_BROADCAST:
217 Adapter->FramesRxBroadcast += netBufferCount;
218 Adapter->BytesRxBroadcast += byteCount;
219 break;
220
221 case NDIS_PACKET_TYPE_MULTICAST:
222 Adapter->FramesRxMulticast += netBufferCount;
223 Adapter->BytesRxMulticast += byteCount;
224 break;
225
226 default:
227 ASSERT(FALSE);
228 break;
229 }
230 }
231
232 //
233 // Handle P2P Packet
234 // -----------------
235 // Free MDL allocated for P2P Ethernet header.
236 //
237 if(TAP_RX_NBL_FLAG_TEST(NetBufferList,TAP_RX_NBL_FLAGS_IS_P2P))
238 {
239 PNET_BUFFER netBuffer;
240 PMDL mdl;
241
242 netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
243 mdl = NET_BUFFER_FIRST_MDL(netBuffer);
244 mdl->Next = NULL;
245
246 NdisFreeMdl(mdl);
247 }
248
249 //
250 // Handle Injected Packet
251 // -----------------------
252 // Free MDL and data buffer allocated for injected packet.
253 //
254 if(TAP_RX_NBL_FLAG_TEST(NetBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED))
255 {
256 PNET_BUFFER netBuffer;
257 PMDL mdl;
258 PUCHAR injectBuffer;
259
260 netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
261 mdl = NET_BUFFER_FIRST_MDL(netBuffer);
262
263 injectBuffer = (PUCHAR )MmGetSystemAddressForMdlSafe(mdl,NormalPagePriority);
264
265 if(injectBuffer)
266 {
267 NdisFreeMemory(injectBuffer,0,0);
268 }
269
270 NdisFreeMdl(mdl);
271 }
272
273 //
274 // Complete the IRP
275 //
276 irp = (PIRP )NetBufferList->MiniportReserved[0];
277
278 if(irp)
279 {
280 irp->IoStatus.Status = IoCompletionStatus;
281 IoCompleteRequest(irp, IO_NO_INCREMENT);
282 }
283
284 // Decrement in-flight receive NBL count.
285 nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount);
286 ASSERT(nblCount >= 0 );
287 if (0 == nblCount)
288 {
289 NdisSetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent);
290 }
291
292 // Free the NBL
293 NdisFreeNetBufferList(NetBufferList);
294 }
295
296 VOID
AdapterReturnNetBufferLists(__in NDIS_HANDLE MiniportAdapterContext,__in PNET_BUFFER_LIST NetBufferLists,__in ULONG ReturnFlags)297 AdapterReturnNetBufferLists(
298 __in NDIS_HANDLE MiniportAdapterContext,
299 __in PNET_BUFFER_LIST NetBufferLists,
300 __in ULONG ReturnFlags
301 )
302 {
303 PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext;
304 PNET_BUFFER_LIST currentNbl, nextNbl;
305
306 UNREFERENCED_PARAMETER(ReturnFlags);
307
308 //
309 // Process each NBL individually
310 //
311 currentNbl = NetBufferLists;
312 while (currentNbl)
313 {
314 PNET_BUFFER_LIST nextNbl;
315
316 nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl);
317 NET_BUFFER_LIST_NEXT_NBL(currentNbl) = NULL;
318
319 // Complete write IRP and free NBL and associated resources.
320 tapCompleteIrpAndFreeReceiveNetBufferList(
321 adapter,
322 currentNbl,
323 STATUS_SUCCESS
324 );
325
326 // Move to next NBL
327 currentNbl = nextNbl;
328 }
329 }
330
331 // IRP_MJ_WRITE callback.
332 NTSTATUS
TapDeviceWrite(PDEVICE_OBJECT DeviceObject,PIRP Irp)333 TapDeviceWrite(
334 PDEVICE_OBJECT DeviceObject,
335 PIRP Irp
336 )
337 {
338 NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success
339 PIO_STACK_LOCATION irpSp;// Pointer to current stack location
340 PTAP_ADAPTER_CONTEXT adapter = NULL;
341 ULONG dataLength;
342
343 PAGED_CODE();
344
345 irpSp = IoGetCurrentIrpStackLocation( Irp );
346
347 //
348 // Fetch adapter context for this device.
349 // --------------------------------------
350 // Adapter pointer was stashed in FsContext when handle was opened.
351 //
352 adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext;
353
354 ASSERT(adapter);
355
356 //
357 // Sanity checks on state variables
358 //
359 if (!tapAdapterReadAndWriteReady(adapter))
360 {
361 //DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n",
362 // MINIPORT_INSTANCE_ID (adapter)));
363 //NOTE_ERROR();
364
365 Irp->IoStatus.Status = ntStatus = STATUS_CANCELLED;
366 Irp->IoStatus.Information = 0;
367 IoCompleteRequest (Irp, IO_NO_INCREMENT);
368
369 return ntStatus;
370 }
371
372 // Save IRP-accessible copy of buffer length
373 Irp->IoStatus.Information = irpSp->Parameters.Write.Length;
374
375 if (Irp->MdlAddress == NULL)
376 {
377 DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n",
378 MINIPORT_INSTANCE_ID (adapter)));
379
380 NOTE_ERROR();
381 Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER;
382 Irp->IoStatus.Information = 0;
383 IoCompleteRequest (Irp, IO_NO_INCREMENT);
384
385 return ntStatus;
386 }
387
388 //
389 // Try to get a virtual address for the MDL.
390 //
391 NdisQueryMdl(
392 Irp->MdlAddress,
393 &Irp->AssociatedIrp.SystemBuffer,
394 &dataLength,
395 NormalPagePriority
396 );
397
398 if (Irp->AssociatedIrp.SystemBuffer == NULL)
399 {
400 DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n",
401 MINIPORT_INSTANCE_ID (adapter)));
402
403 NOTE_ERROR();
404 Irp->IoStatus.Status = ntStatus = STATUS_INSUFFICIENT_RESOURCES;
405 Irp->IoStatus.Information = 0;
406 IoCompleteRequest (Irp, IO_NO_INCREMENT);
407
408 return ntStatus;
409 }
410
411 ASSERT(dataLength == irpSp->Parameters.Write.Length);
412
413 Irp->IoStatus.Information = irpSp->Parameters.Write.Length;
414
415 //
416 // Handle miniport Pause
417 // ---------------------
418 // NDIS 6 miniports implement a temporary "Pause" state normally followed
419 // by the Restart. While in the Pause state it is forbidden for the miniport
420 // to indicate receive NBLs.
421 //
422 // That is: The device interface may be "up", but the NDIS miniport send/receive
423 // interface may be temporarily "down".
424 //
425 // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas receive path
426 // the code below will perform a "lying send" for write IRPs passed to the
427 // driver while the miniport is in the Paused state.
428 //
429 // The correct implementation is to go ahead and build the NBLs corresponding
430 // to the user-mode write - but queue them. When Restart is entered the
431 // queued NBLs would be dequeued and indicated to the host.
432 //
433 if(tapAdapterSendAndReceiveReady(adapter) == NDIS_STATUS_SUCCESS)
434 {
435 if (/*!adapter->m_tun &&*/ ((irpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE))
436 {
437 PNET_BUFFER_LIST netBufferList;
438
439 DUMP_PACKET ("IRP_MJ_WRITE ETH",
440 (unsigned char *) Irp->AssociatedIrp.SystemBuffer,
441 irpSp->Parameters.Write.Length);
442
443 //=====================================================
444 // If IPv4 packet, check whether or not packet
445 // was truncated.
446 //=====================================================
447 #if PACKET_TRUNCATION_CHECK
448 IPv4PacketSizeVerify (
449 (unsigned char *) Irp->AssociatedIrp.SystemBuffer,
450 irpSp->Parameters.Write.Length,
451 FALSE,
452 "RX",
453 &adapter->m_RxTrunc
454 );
455 #endif
456 (Irp->MdlAddress)->Next = NULL; // No next MDL
457
458 // Allocate the NBL and NB. Link MDL chain to NB.
459 netBufferList = NdisAllocateNetBufferAndNetBufferList(
460 adapter->ReceiveNblPool,
461 0, // ContextSize
462 0, // ContextBackFill
463 Irp->MdlAddress, // MDL chain
464 0,
465 dataLength
466 );
467
468 if(netBufferList != NULL)
469 {
470 LONG nblCount;
471
472 NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL
473
474 // Stash IRP pointer in NBL MiniportReserved[0] field.
475 netBufferList->MiniportReserved[0] = Irp;
476 netBufferList->MiniportReserved[1] = NULL;
477
478 // This IRP is pended.
479 IoMarkIrpPending(Irp);
480
481 // This IRP cannot be cancelled while in-flight.
482 IoSetCancelRoutine(Irp,NULL);
483
484 TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList);
485
486 // Increment in-flight receive NBL count.
487 nblCount = NdisInterlockedIncrement(&adapter->ReceiveNblInFlightCount);
488 ASSERT(nblCount > 0 );
489
490 //
491 // Indicate the packet
492 // -------------------
493 // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length
494 // contains the complete packet including Ethernet header and payload.
495 //
496 NdisMIndicateReceiveNetBufferLists(
497 adapter->MiniportAdapterHandle,
498 netBufferList,
499 NDIS_DEFAULT_PORT_NUMBER,
500 1, // NumberOfNetBufferLists
501 0 // ReceiveFlags
502 );
503
504 ntStatus = STATUS_PENDING;
505 }
506 else
507 {
508 DEBUGP (("[%s] NdisMIndicateReceiveNetBufferLists failed in IRP_MJ_WRITE\n",
509 MINIPORT_INSTANCE_ID (adapter)));
510 NOTE_ERROR ();
511
512 // Fail the IRP
513 Irp->IoStatus.Information = 0;
514 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
515 }
516 }
517 /*
518 else if (adapter->m_tun && ((irpSp->Parameters.Write.Length) >= IP_HEADER_SIZE))
519 {
520 PETH_HEADER p_UserToTap = &adapter->m_UserToTap;
521 PMDL mdl; // Head of MDL chain.
522
523 // For IPv6, need to use Ethernet header with IPv6 proto
524 if ( IPH_GET_VER( ((IPHDR*) Irp->AssociatedIrp.SystemBuffer)->version_len) == 6 )
525 {
526 p_UserToTap = &adapter->m_UserToTap_IPv6;
527 }
528
529 DUMP_PACKET2 ("IRP_MJ_WRITE P2P",
530 p_UserToTap,
531 (unsigned char *) Irp->AssociatedIrp.SystemBuffer,
532 irpSp->Parameters.Write.Length);
533
534 //=====================================================
535 // If IPv4 packet, check whether or not packet
536 // was truncated.
537 //=====================================================
538 #if PACKET_TRUNCATION_CHECK
539 IPv4PacketSizeVerify (
540 (unsigned char *) Irp->AssociatedIrp.SystemBuffer,
541 irpSp->Parameters.Write.Length,
542 TRUE,
543 "RX",
544 &adapter->m_RxTrunc
545 );
546 #endif
547
548 //
549 // Allocate MDL for Ethernet header
550 // --------------------------------
551 // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length
552 // contains the only the Ethernet payload. Prepend the user-mode provided
553 // payload with the Ethernet header pointed to by p_UserToTap.
554 //
555 mdl = NdisAllocateMdl(
556 adapter->MiniportAdapterHandle,
557 p_UserToTap,
558 sizeof(ETH_HEADER)
559 );
560
561 if(mdl != NULL)
562 {
563 PNET_BUFFER_LIST netBufferList;
564
565 // Chain user's Ethernet payload behind Ethernet header.
566 mdl->Next = Irp->MdlAddress;
567 (Irp->MdlAddress)->Next = NULL; // No next MDL
568
569 // Allocate the NBL and NB. Link MDL chain to NB.
570 netBufferList = NdisAllocateNetBufferAndNetBufferList(
571 adapter->ReceiveNblPool,
572 0, // ContextSize
573 0, // ContextBackFill
574 mdl, // MDL chain
575 0,
576 sizeof(ETH_HEADER) + dataLength
577 );
578
579 if(netBufferList != NULL)
580 {
581 LONG nblCount;
582
583 NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL
584
585 // This IRP is pended.
586 IoMarkIrpPending(Irp);
587
588 // This IRP cannot be cancelled while in-flight.
589 IoSetCancelRoutine(Irp,NULL);
590
591 // Stash IRP pointer in NBL MiniportReserved[0] field.
592 netBufferList->MiniportReserved[0] = Irp;
593 netBufferList->MiniportReserved[1] = NULL;
594
595 // Set flag indicating that this is P2P packet
596 TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList);
597 TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_P2P);
598
599 // Increment in-flight receive NBL count.
600 nblCount = NdisInterlockedIncrement(&adapter->ReceiveNblInFlightCount);
601 ASSERT(nblCount > 0 );
602
603 //
604 // Indicate the packet
605 //
606 NdisMIndicateReceiveNetBufferLists(
607 adapter->MiniportAdapterHandle,
608 netBufferList,
609 NDIS_DEFAULT_PORT_NUMBER,
610 1, // NumberOfNetBufferLists
611 0 // ReceiveFlags
612 );
613
614 ntStatus = STATUS_PENDING;
615 }
616 else
617 {
618 mdl->Next = NULL;
619 NdisFreeMdl(mdl);
620
621 DEBUGP (("[%s] NdisMIndicateReceiveNetBufferLists failed in IRP_MJ_WRITE\n",
622 MINIPORT_INSTANCE_ID (adapter)));
623 NOTE_ERROR ();
624
625 // Fail the IRP
626 Irp->IoStatus.Information = 0;
627 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
628 }
629 }
630 else
631 {
632 DEBUGP (("[%s] NdisAllocateMdl failed in IRP_MJ_WRITE\n",
633 MINIPORT_INSTANCE_ID (adapter)));
634 NOTE_ERROR ();
635
636 // Fail the IRP
637 Irp->IoStatus.Information = 0;
638 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
639 }
640 }
641 */
642 else
643 {
644 DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n",
645 MINIPORT_INSTANCE_ID (adapter),
646 irpSp->Parameters.Write.Length));
647 NOTE_ERROR ();
648
649 Irp->IoStatus.Information = 0; // ETHERNET_HEADER_SIZE;
650 Irp->IoStatus.Status = ntStatus = STATUS_BUFFER_TOO_SMALL;
651 }
652 }
653 else
654 {
655 DEBUGP (("[%s] Lying send in IRP_MJ_WRITE while adapter paused\n",
656 MINIPORT_INSTANCE_ID (adapter)));
657
658 ntStatus = STATUS_SUCCESS;
659 }
660
661 if (ntStatus != STATUS_PENDING)
662 {
663 Irp->IoStatus.Status = ntStatus;
664 IoCompleteRequest(Irp, IO_NO_INCREMENT);
665 }
666
667 return ntStatus;
668 }
669
670