diff options
| author | Grant Limberg <glimberg@gmail.com> | 2015-04-25 18:59:52 -0700 |
|---|---|---|
| committer | Grant Limberg <glimberg@gmail.com> | 2015-04-25 18:59:52 -0700 |
| commit | ec45aeb42aa644b71654b0467c4aebc80e896420 (patch) | |
| tree | e7d544030dfc9e279007a8455a12206e895446eb /windows/TapDriver6/txpath.c | |
| parent | a86a0ab2b13ae19d99f4667666a25b337e0b93f1 (diff) | |
| parent | 7af1f3a79ab8928a3b49bac458ee87368c46edc5 (diff) | |
| download | infinitytier-ec45aeb42aa644b71654b0467c4aebc80e896420.tar.gz infinitytier-ec45aeb42aa644b71654b0467c4aebc80e896420.zip | |
Merge branch 'adamierymenko-dev' into android-jni
Diffstat (limited to 'windows/TapDriver6/txpath.c')
| -rw-r--r-- | windows/TapDriver6/txpath.c | 1175 |
1 files changed, 1175 insertions, 0 deletions
diff --git a/windows/TapDriver6/txpath.c b/windows/TapDriver6/txpath.c new file mode 100644 index 00000000..7993ca40 --- /dev/null +++ b/windows/TapDriver6/txpath.c @@ -0,0 +1,1175 @@ +/* + * TAP-Windows -- A kernel driver to provide virtual tap + * device functionality on Windows. + * + * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson. + * + * This source code is Copyright (C) 2002-2014 OpenVPN Technologies, Inc., + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// +// Include files. +// + +#include "tap.h" + +//====================================================================== +// TAP Send Path Support +//====================================================================== + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, TapDeviceRead) +#endif // ALLOC_PRAGMA + +// checksum code for ICMPv6 packet, taken from dhcp.c / udp_checksum +// see RFC 4443, 2.3, and RFC 2460, 8.1 +USHORT +icmpv6_checksum( + __in const UCHAR *buf, + __in const int len_icmpv6, + __in const UCHAR *saddr6, + __in const UCHAR *daddr6 + ) +{ + USHORT word16; + ULONG sum = 0; + int i; + + // make 16 bit words out of every two adjacent 8 bit words and + // calculate the sum of all 16 bit words + for (i = 0; i < len_icmpv6; i += 2) + { + word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_icmpv6) ? (buf[i+1] & 0xFF) : 0); + sum += word16; + } + + // add the IPv6 pseudo header which contains the IP source and destination addresses + for (i = 0; i < 16; i += 2) + { + word16 =((saddr6[i] << 8) & 0xFF00) + (saddr6[i+1] & 0xFF); + sum += word16; + } + + for (i = 0; i < 16; i += 2) + { + word16 =((daddr6[i] << 8) & 0xFF00) + (daddr6[i+1] & 0xFF); + sum += word16; + } + + // the next-header number and the length of the ICMPv6 packet + sum += (USHORT) IPPROTO_ICMPV6 + (USHORT) len_icmpv6; + + // keep only the last 16 bits of the 32 bit calculated sum and add the carries + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Take the one's complement of sum + return ((USHORT) ~sum); +} + +/* + +// check IPv6 packet for "is this an IPv6 Neighbor Solicitation that +// the tap driver needs to answer?" +// see RFC 4861 4.3 for the different cases +static IPV6ADDR IPV6_NS_TARGET_MCAST = + { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x08 }; +static IPV6ADDR IPV6_NS_TARGET_UNICAST = + { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }; + +BOOLEAN +HandleIPv6NeighborDiscovery( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in UCHAR * m_Data + ) +{ + const ETH_HEADER * e = (ETH_HEADER *) m_Data; + const IPV6HDR *ipv6 = (IPV6HDR *) (m_Data + sizeof (ETH_HEADER)); + const ICMPV6_NS * icmpv6_ns = (ICMPV6_NS *) (m_Data + sizeof (ETH_HEADER) + sizeof (IPV6HDR)); + ICMPV6_NA_PKT *na; + USHORT icmpv6_len, icmpv6_csum; + + // we don't really care about the destination MAC address here + // - it's either a multicast MAC, or the userland destination MAC + // but since the TAP driver is point-to-point, all packets are "for us" + + // IPv6 target address must be ff02::1::ff00:8 (multicast for + // initial NS) or fe80::1 (unicast for recurrent NUD) + if ( memcmp( ipv6->daddr, IPV6_NS_TARGET_MCAST, + sizeof(IPV6ADDR) ) != 0 && + memcmp( ipv6->daddr, IPV6_NS_TARGET_UNICAST, + sizeof(IPV6ADDR) ) != 0 ) + { + return FALSE; // wrong target address + } + + // IPv6 Next-Header must be ICMPv6 + if ( ipv6->nexthdr != IPPROTO_ICMPV6 ) + { + return FALSE; // wrong next-header + } + + // ICMPv6 type+code must be 135/0 for NS + if ( icmpv6_ns->type != ICMPV6_TYPE_NS || + icmpv6_ns->code != ICMPV6_CODE_0 ) + { + return FALSE; // wrong ICMPv6 type + } + + // ICMPv6 target address must be fe80::8 (magic) + if ( memcmp( icmpv6_ns->target_addr, IPV6_NS_TARGET_UNICAST, + sizeof(IPV6ADDR) ) != 0 ) + { + return FALSE; // not for us + } + + // packet identified, build magic response packet + + na = (ICMPV6_NA_PKT *) MemAlloc (sizeof (ICMPV6_NA_PKT), TRUE); + if ( !na ) return FALSE; + + //------------------------------------------------ + // Initialize Neighbour Advertisement reply packet + //------------------------------------------------ + + // ethernet header + na->eth.proto = htons(NDIS_ETH_TYPE_IPV6); + ETH_COPY_NETWORK_ADDRESS(na->eth.dest, Adapter->PermanentAddress); + ETH_COPY_NETWORK_ADDRESS(na->eth.src, Adapter->m_TapToUser.dest); + + // IPv6 header + na->ipv6.version_prio = ipv6->version_prio; + NdisMoveMemory( na->ipv6.flow_lbl, ipv6->flow_lbl, + sizeof(na->ipv6.flow_lbl) ); + icmpv6_len = sizeof(ICMPV6_NA_PKT) - sizeof(ETH_HEADER) - sizeof(IPV6HDR); + na->ipv6.payload_len = htons(icmpv6_len); + na->ipv6.nexthdr = IPPROTO_ICMPV6; + na->ipv6.hop_limit = 255; + NdisMoveMemory( na->ipv6.saddr, IPV6_NS_TARGET_UNICAST, + sizeof(IPV6ADDR) ); + NdisMoveMemory( na->ipv6.daddr, ipv6->saddr, + sizeof(IPV6ADDR) ); + + // ICMPv6 + na->icmpv6.type = ICMPV6_TYPE_NA; + na->icmpv6.code = ICMPV6_CODE_0; + na->icmpv6.checksum = 0; + na->icmpv6.rso_bits = 0x60; // Solicited + Override + NdisZeroMemory( na->icmpv6.reserved, sizeof(na->icmpv6.reserved) ); + NdisMoveMemory( na->icmpv6.target_addr, IPV6_NS_TARGET_UNICAST, + sizeof(IPV6ADDR) ); + + // ICMPv6 option "Target Link Layer Address" + na->icmpv6.opt_type = ICMPV6_OPTION_TLLA; + na->icmpv6.opt_length = ICMPV6_LENGTH_TLLA; + ETH_COPY_NETWORK_ADDRESS( na->icmpv6.target_macaddr, Adapter->m_TapToUser.dest ); + + // calculate and set checksum + icmpv6_csum = icmpv6_checksum ( + (UCHAR*) &(na->icmpv6), + icmpv6_len, + na->ipv6.saddr, + na->ipv6.daddr + ); + + na->icmpv6.checksum = htons( icmpv6_csum ); + + DUMP_PACKET ("HandleIPv6NeighborDiscovery", + (unsigned char *) na, + sizeof (ICMPV6_NA_PKT)); + + IndicateReceivePacket (Adapter, (UCHAR *) na, sizeof (ICMPV6_NA_PKT)); + + MemFree (na, sizeof (ICMPV6_NA_PKT)); + + return TRUE; // all fine +} + +//=================================================== +// Generate an ARP reply message for specific kinds +// ARP queries. +//=================================================== +BOOLEAN +ProcessARP( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in const PARP_PACKET src, + __in const IPADDR adapter_ip, + __in const IPADDR ip_network, + __in const IPADDR ip_netmask, + __in const MACADDR mac + ) +{ + //----------------------------------------------- + // Is this the kind of packet we are looking for? + //----------------------------------------------- + if (src->m_Proto == htons (NDIS_ETH_TYPE_ARP) + && MAC_EQUAL (src->m_MAC_Source, Adapter->PermanentAddress) + && MAC_EQUAL (src->m_ARP_MAC_Source, Adapter->PermanentAddress) + && ETH_IS_BROADCAST(src->m_MAC_Destination) + && src->m_ARP_Operation == htons (ARP_REQUEST) + && src->m_MAC_AddressType == htons (MAC_ADDR_TYPE) + && src->m_MAC_AddressSize == sizeof (MACADDR) + && src->m_PROTO_AddressType == htons (NDIS_ETH_TYPE_IPV4) + && src->m_PROTO_AddressSize == sizeof (IPADDR) + && src->m_ARP_IP_Source == adapter_ip + && (src->m_ARP_IP_Destination & ip_netmask) == ip_network + && src->m_ARP_IP_Destination != adapter_ip) + { + ARP_PACKET *arp = (ARP_PACKET *) MemAlloc (sizeof (ARP_PACKET), TRUE); + if (arp) + { + //---------------------------------------------- + // Initialize ARP reply fields + //---------------------------------------------- + arp->m_Proto = htons (NDIS_ETH_TYPE_ARP); + arp->m_MAC_AddressType = htons (MAC_ADDR_TYPE); + arp->m_PROTO_AddressType = htons (NDIS_ETH_TYPE_IPV4); + arp->m_MAC_AddressSize = sizeof (MACADDR); + arp->m_PROTO_AddressSize = sizeof (IPADDR); + arp->m_ARP_Operation = htons (ARP_REPLY); + + //---------------------------------------------- + // ARP addresses + //---------------------------------------------- + ETH_COPY_NETWORK_ADDRESS (arp->m_MAC_Source, mac); + ETH_COPY_NETWORK_ADDRESS (arp->m_MAC_Destination, Adapter->PermanentAddress); + ETH_COPY_NETWORK_ADDRESS (arp->m_ARP_MAC_Source, mac); + ETH_COPY_NETWORK_ADDRESS (arp->m_ARP_MAC_Destination, Adapter->PermanentAddress); + arp->m_ARP_IP_Source = src->m_ARP_IP_Destination; + arp->m_ARP_IP_Destination = adapter_ip; + + DUMP_PACKET ("ProcessARP", + (unsigned char *) arp, + sizeof (ARP_PACKET)); + + IndicateReceivePacket (Adapter, (UCHAR *) arp, sizeof (ARP_PACKET)); + + MemFree (arp, sizeof (ARP_PACKET)); + } + + return TRUE; + } + else + return FALSE; +} +*/ + +//============================================================= +// CompleteIRP is normally called with an adapter -> userspace +// network packet and an IRP (Pending I/O request) from userspace. +// +// The IRP will normally represent a queued overlapped read +// operation from userspace that is in a wait state. +// +// Use the ethernet packet to satisfy the IRP. +//============================================================= + +VOID +tapCompletePendingReadIrp( + __in PIRP Irp, + __in PTAP_PACKET TapPacket + ) +{ + int offset; + int len; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + ASSERT(Irp); + ASSERT(TapPacket); + + //------------------------------------------- + // While TapPacket always contains a + // full ethernet packet, including the + // ethernet header, in point-to-point mode, + // we only want to return the IPv4 + // component. + //------------------------------------------- + + if (TapPacket->m_SizeFlags & TP_TUN) + { + offset = ETHERNET_HEADER_SIZE; + len = (int) (TapPacket->m_SizeFlags & TP_SIZE_MASK) - ETHERNET_HEADER_SIZE; + } + else + { + offset = 0; + len = (TapPacket->m_SizeFlags & TP_SIZE_MASK); + } + + if (len < 0 || (int) Irp->IoStatus.Information < len) + { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status = STATUS_BUFFER_OVERFLOW; + NOTE_ERROR (); + } + else + { + Irp->IoStatus.Information = len; + Irp->IoStatus.Status = status = STATUS_SUCCESS; + + // Copy packet data + NdisMoveMemory( + Irp->AssociatedIrp.SystemBuffer, + TapPacket->m_Data + offset, + len + ); + } + + // Free the TAP packet + NdisFreeMemory(TapPacket,0,0); + + // Complete the IRP + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); +} + +VOID +tapProcessSendPacketQueue( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + KIRQL irql; + + // Process the send packet queue + KeAcquireSpinLock(&Adapter->SendPacketQueue.QueueLock,&irql); + + while(Adapter->SendPacketQueue.Count > 0 ) + { + PIRP irp; + PTAP_PACKET tapPacket; + + // Fetch a read IRP + irp = IoCsqRemoveNextIrp( + &Adapter->PendingReadIrpQueue.CsqQueue, + NULL + ); + + if( irp == NULL ) + { + // No IRP to satisfy + break; + } + + // Fetch a queued TAP send packet + tapPacket = tapPacketRemoveHeadLocked( + &Adapter->SendPacketQueue + ); + + ASSERT(tapPacket); + + // BUGBUG!!! Investigate whether release/reacquire can cause + // out-of-order IRP completion. Also, whether user-mode can + // tolerate out-of-order packets. + + // Release packet queue lock while completing the IRP + //KeReleaseSpinLock(&Adapter->SendPacketQueue.QueueLock,irql); + + // Complete the read IRP from queued TAP send packet. + tapCompletePendingReadIrp(irp,tapPacket); + + // Reqcquire packet queue lock after completing the IRP + //KeAcquireSpinLock(&Adapter->SendPacketQueue.QueueLock,&irql); + } + + KeReleaseSpinLock(&Adapter->SendPacketQueue.QueueLock,irql); +} + +// Flush the pending send TAP packet queue. +VOID +tapFlushSendPacketQueue( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + KIRQL irql; + + // Process the send packet queue + KeAcquireSpinLock(&Adapter->SendPacketQueue.QueueLock,&irql); + + DEBUGP (("[TAP] tapFlushSendPacketQueue: Flushing %d TAP packets\n", + Adapter->SendPacketQueue.Count)); + + while(Adapter->SendPacketQueue.Count > 0 ) + { + PTAP_PACKET tapPacket; + + // Fetch a queued TAP send packet + tapPacket = tapPacketRemoveHeadLocked( + &Adapter->SendPacketQueue + ); + + ASSERT(tapPacket); + + // Free the TAP packet + NdisFreeMemory(tapPacket,0,0); + } + + KeReleaseSpinLock(&Adapter->SendPacketQueue.QueueLock,irql); +} + +VOID +tapAdapterTransmit( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNET_BUFFER NetBuffer, + __in BOOLEAN DispatchLevel + ) +/*++ + +Routine Description: + + This routine is called to transmit an individual net buffer using a + style similar to the previous NDIS 5 AdapterTransmit function. + + In this implementation adapter state and NB length checks have already + been done before this function has been called. + + The net buffer will be completed by the calling routine after this + routine exits. So, under this design it is necessary to make a deep + copy of frame data in the net buffer. + + This routine creates a flat buffer copy of NB frame data. This is an + unnecessary performance bottleneck. However, the bottleneck is probably + not significant or measurable except for adapters running at 1Gbps or + greater speeds. Since this adapter is currently running at 100Mbps this + defect can be ignored. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter Pointer to our adapter context + NetBuffer Pointer to the net buffer to transmit + DispatchLevel TRUE if called at IRQL == DISPATCH_LEVEL + +Return Value: + + None. + + In the Microsoft NDIS 6 architecture there is no per-packet status. + +--*/ +{ + NDIS_STATUS status; + ULONG packetLength; + PTAP_PACKET tapPacket; + PVOID packetData; + + packetLength = NET_BUFFER_DATA_LENGTH(NetBuffer); + + // Allocate TAP packet memory + tapPacket = (PTAP_PACKET )NdisAllocateMemoryWithTagPriority( + Adapter->MiniportAdapterHandle, + TAP_PACKET_SIZE (packetLength), + TAP_PACKET_TAG, + NormalPoolPriority + ); + + if(tapPacket == NULL) + { + DEBUGP (("[TAP] tapAdapterTransmit: TAP packet allocation failed\n")); + return; + } + + tapPacket->m_SizeFlags = (packetLength & TP_SIZE_MASK); + + // + // Reassemble packet contents + // -------------------------- + // NdisGetDataBuffer does most of the work. There are two cases: + // + // 1.) If the NB data was not contiguous it will copy the entire + // NB's data to m_data and return pointer to m_data. + // 2.) If the NB data was contiguous it returns a pointer to the + // first byte of the contiguous data instead of a pointer to m_Data. + // In this case the data will not have been copied to m_Data. Copy + // to m_Data will need to be done in an extra step. + // + // Case 1.) is the most likely in normal operation. + // + packetData = NdisGetDataBuffer(NetBuffer,packetLength,tapPacket->m_Data,1,0); + + if(packetData == NULL) + { + DEBUGP (("[TAP] tapAdapterTransmit: Could not get packet data\n")); + + NdisFreeMemory(tapPacket,0,0); + + return; + } + + if(packetData != tapPacket->m_Data) + { + // Packet data was contiguous and not yet copied to m_Data. + NdisMoveMemory(tapPacket->m_Data,packetData,packetLength); + } + + DUMP_PACKET ("AdapterTransmit", tapPacket->m_Data, packetLength); + + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify( + tapPacket->m_Data, + packetLength, + FALSE, + "TX", + &Adapter->m_TxTrunc + ); +#endif + + //===================================================== + // Are we running in DHCP server masquerade mode? + // + // If so, catch both DHCP requests and ARP queries + // to resolve the address of our virtual DHCP server. + //===================================================== +#if 0 + if (Adapter->m_dhcp_enabled) + { + const ETH_HEADER *eth = (ETH_HEADER *) tapPacket->m_Data; + const IPHDR *ip = (IPHDR *) (tapPacket->m_Data + sizeof (ETH_HEADER)); + const UDPHDR *udp = (UDPHDR *) (tapPacket->m_Data + sizeof (ETH_HEADER) + sizeof (IPHDR)); + + // ARP packet? + if (packetLength == sizeof (ARP_PACKET) + && eth->proto == htons (NDIS_ETH_TYPE_ARP) + && Adapter->m_dhcp_server_arp + ) + { + if (ProcessARP( + Adapter, + (PARP_PACKET) tapPacket->m_Data, + Adapter->m_dhcp_addr, + Adapter->m_dhcp_server_ip, + ~0, + Adapter->m_dhcp_server_mac) + ) + { + goto no_queue; + } + } + + // DHCP packet? + else if (packetLength >= sizeof (ETH_HEADER) + sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + && eth->proto == htons (NDIS_ETH_TYPE_IPV4) + && ip->version_len == 0x45 // IPv4, 20 byte header + && ip->protocol == IPPROTO_UDP + && udp->dest == htons (BOOTPS_PORT) + ) + { + const DHCP *dhcp = (DHCP *) (tapPacket->m_Data + + sizeof (ETH_HEADER) + + sizeof (IPHDR) + + sizeof (UDPHDR)); + + const int optlen = packetLength + - sizeof (ETH_HEADER) + - sizeof (IPHDR) + - sizeof (UDPHDR) + - sizeof (DHCP); + + if (optlen > 0) // we must have at least one DHCP option + { + if (ProcessDHCP (Adapter, eth, ip, udp, dhcp, optlen)) + { + goto no_queue; + } + } + else + { + goto no_queue; + } + } + } +#endif + + //=============================================== + // In Point-To-Point mode, check to see whether + // packet is ARP (handled) or IPv4 (sent to app). + // IPv6 packets are inspected for neighbour discovery + // (to be handled locally), and the rest is forwarded + // all other protocols are dropped + //=============================================== +#if 0 + if (Adapter->m_tun) + { + ETH_HEADER *e; + + e = (ETH_HEADER *) tapPacket->m_Data; + + switch (ntohs (e->proto)) + { + case NDIS_ETH_TYPE_ARP: + + // Make sure that packet is the right size for ARP. + if (packetLength != sizeof (ARP_PACKET)) + { + goto no_queue; + } + + ProcessARP ( + Adapter, + (PARP_PACKET) tapPacket->m_Data, + Adapter->m_localIP, + Adapter->m_remoteNetwork, + Adapter->m_remoteNetmask, + Adapter->m_TapToUser.dest + ); + + default: + goto no_queue; + + case NDIS_ETH_TYPE_IPV4: + + // Make sure that packet is large enough to be IPv4. + if (packetLength < (ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)) + { + goto no_queue; + } + + // Only accept directed packets, not broadcasts. + if (memcmp (e, &Adapter->m_TapToUser, ETHERNET_HEADER_SIZE)) + { + goto no_queue; + } + + // Packet looks like IPv4, queue it. :-) + tapPacket->m_SizeFlags |= TP_TUN; + break; + + case NDIS_ETH_TYPE_IPV6: + // Make sure that packet is large enough to be IPv6. + if (packetLength < (ETHERNET_HEADER_SIZE + IPV6_HEADER_SIZE)) + { + goto no_queue; + } + + // Broadcasts and multicasts are handled specially + // (to be implemented) + + // Neighbor discovery packets to fe80::8 are special + // OpenVPN sets this next-hop to signal "handled by tapdrv" + if ( HandleIPv6NeighborDiscovery(Adapter,tapPacket->m_Data) ) + { + goto no_queue; + } + + // Packet looks like IPv6, queue it. :-) + tapPacket->m_SizeFlags |= TP_TUN; + } + } +#endif + + //=============================================== + // Push packet onto queue to wait for read from + // userspace. + //=============================================== + if(tapAdapterReadAndWriteReady(Adapter)) + { + tapPacketQueueInsertTail(&Adapter->SendPacketQueue,tapPacket); + } + else + { + // + // Tragedy. All this work and the packet is of no use... + // + NdisFreeMemory(tapPacket,0,0); + } + + // Return after queuing or freeing TAP packet. + return; + + // Free TAP packet without queuing. +no_queue: + if(tapPacket != NULL ) + { + NdisFreeMemory(tapPacket,0,0); + } + +exit_success: + return; +} + +VOID +tapSendNetBufferListsComplete( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNET_BUFFER_LIST NetBufferLists, + __in NDIS_STATUS SendCompletionStatus, + __in BOOLEAN DispatchLevel + ) +{ + PNET_BUFFER_LIST currentNbl; + PNET_BUFFER_LIST nextNbl = NULL; + ULONG sendCompleteFlags = 0; + + for ( + currentNbl = NetBufferLists; + currentNbl != NULL; + currentNbl = nextNbl + ) + { + ULONG frameType; + ULONG netBufferCount; + ULONG byteCount; + + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + + // Set NBL completion status. + NET_BUFFER_LIST_STATUS(currentNbl) = SendCompletionStatus; + + // Fetch first NBs frame type. All linked NBs will have same type. + frameType = tapGetNetBufferFrameType(NET_BUFFER_LIST_FIRST_NB(currentNbl)); + + // Fetch statistics for all NBs linked to the NB. + netBufferCount = tapGetNetBufferCountsFromNetBufferList( + currentNbl, + &byteCount + ); + + // Update statistics by frame type + if(SendCompletionStatus == NDIS_STATUS_SUCCESS) + { + switch(frameType) + { + case NDIS_PACKET_TYPE_DIRECTED: + Adapter->FramesTxDirected += netBufferCount; + Adapter->BytesTxDirected += byteCount; + break; + + case NDIS_PACKET_TYPE_BROADCAST: + Adapter->FramesTxBroadcast += netBufferCount; + Adapter->BytesTxBroadcast += byteCount; + break; + + case NDIS_PACKET_TYPE_MULTICAST: + Adapter->FramesTxMulticast += netBufferCount; + Adapter->BytesTxMulticast += byteCount; + break; + + default: + ASSERT(FALSE); + break; + } + } + else + { + // Transmit error. + Adapter->TransmitFailuresOther += netBufferCount; + } + + currentNbl = nextNbl; + } + + if(DispatchLevel) + { + sendCompleteFlags |= NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL; + } + + // Complete the NBLs + NdisMSendNetBufferListsComplete( + Adapter->MiniportAdapterHandle, + NetBufferLists, + sendCompleteFlags + ); +} + +BOOLEAN +tapNetBufferListNetBufferLengthsValid( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNET_BUFFER_LIST NetBufferLists + ) +/*++ + +Routine Description: + + Scan all NBLs and their linked NBs for valid lengths. + + Fairly absurd to find and packets with bogus lengths, but wise + to check anyway. If ANY packet has a bogus length, then abort the + entire send. + + The only time that one might see this check fail might be during + HCK driver testing. The HKC test might send oversize packets to + determine if the miniport can gracefully deal with them. + + This check is fairly fast. Unlike NDIS 5 packets, fetching NDIS 6 + packets lengths do not require any computation. + +Arguments: + + Adapter Pointer to our adapter context + NetBufferLists Head of a list of NBLs to examine + +Return Value: + + Returns TRUE if all NBs have reasonable lengths. + Otherwise, returns FALSE. + +--*/ +{ + PNET_BUFFER_LIST currentNbl; + + currentNbl = NetBufferLists; + + while (currentNbl) + { + PNET_BUFFER_LIST nextNbl; + PNET_BUFFER currentNb; + + // Locate next NBL + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + + // Locate first NB (aka "packet") + currentNb = NET_BUFFER_LIST_FIRST_NB(currentNbl); + + // + // Process all NBs linked to this NBL + // + while(currentNb) + { + PNET_BUFFER nextNb; + ULONG packetLength; + + // Locate next NB + nextNb = NET_BUFFER_NEXT_NB(currentNb); + + packetLength = NET_BUFFER_DATA_LENGTH(currentNb); + + // Minimum packet size is size of Ethernet plus IPv4 headers. + ASSERT(packetLength >= (ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)); + + if(packetLength < (ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)) + { + return FALSE; + } + + // Maximum size should be Ethernet header size plus MTU plus modest pad for + // VLAN tag. + ASSERT( packetLength <= (ETHERNET_HEADER_SIZE + VLAN_TAG_SIZE + Adapter->MtuSize)); + + if(packetLength > (ETHERNET_HEADER_SIZE + VLAN_TAG_SIZE + Adapter->MtuSize)) + { + return FALSE; + } + + // Move to next NB + currentNb = nextNb; + } + + // Move to next NBL + currentNbl = nextNbl; + } + + return TRUE; +} + +VOID +AdapterSendNetBufferLists( + __in NDIS_HANDLE MiniportAdapterContext, + __in PNET_BUFFER_LIST NetBufferLists, + __in NDIS_PORT_NUMBER PortNumber, + __in ULONG SendFlags + ) +/*++ + +Routine Description: + + Send Packet Array handler. Called by NDIS whenever a protocol + bound to our miniport sends one or more packets. + + The input packet descriptor pointers have been ordered according + to the order in which the packets should be sent over the network + by the protocol driver that set up the packet array. The NDIS + library preserves the protocol-determined ordering when it submits + each packet array to MiniportSendPackets + + As a deserialized driver, we are responsible for holding incoming send + packets in our internal queue until they can be transmitted over the + network and for preserving the protocol-determined ordering of packet + descriptors incoming to its MiniportSendPackets function. + A deserialized miniport driver must complete each incoming send packet + with NdisMSendComplete, and it cannot call NdisMSendResourcesAvailable. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + MiniportAdapterContext Pointer to our adapter + NetBufferLists Head of a list of NBLs to send + PortNumber A miniport adapter port. Default is 0. + SendFlags Additional flags for the send operation + +Return Value: + + None. Write status directly into each NBL with the NET_BUFFER_LIST_STATUS + macro. + +--*/ +{ + NDIS_STATUS status; + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + BOOLEAN DispatchLevel = (SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL); + PNET_BUFFER_LIST currentNbl; + BOOLEAN validNbLengths; + + UNREFERENCED_PARAMETER(NetBufferLists); + UNREFERENCED_PARAMETER(PortNumber); + UNREFERENCED_PARAMETER(SendFlags); + + ASSERT(PortNumber == 0); // Only the default port is supported + + // + // Can't process sends if TAP device is not open. + // ---------------------------------------------- + // Just perform a "lying send" and return packets as if they + // were successfully sent. + // + if(adapter->TapFileObject == NULL) + { + // + // Complete all NBLs and return if adapter not ready. + // + tapSendNetBufferListsComplete( + adapter, + NetBufferLists, + NDIS_STATUS_SUCCESS, + DispatchLevel + ); + + return; + } + + // + // Check Adapter send/receive ready state. + // + status = tapAdapterSendAndReceiveReady(adapter); + + if(status != NDIS_STATUS_SUCCESS) + { + // + // Complete all NBLs and return if adapter not ready. + // + tapSendNetBufferListsComplete( + adapter, + NetBufferLists, + status, + DispatchLevel + ); + + return; + } + + // + // Scan all NBLs and linked packets for valid lengths. + // --------------------------------------------------- + // If _ANY_ NB length is invalid, then fail the entire send operation. + // + // BUGBUG!!! Perhaps this should be less agressive. Fail only individual + // NBLs... + // + // If length check is valid, then TAP_PACKETS can be safely allocated + // and processed for all NBs being sent. + // + validNbLengths = tapNetBufferListNetBufferLengthsValid( + adapter, + NetBufferLists + ); + + if(!validNbLengths) + { + // + // Complete all NBLs and return if and NB length is invalid. + // + tapSendNetBufferListsComplete( + adapter, + NetBufferLists, + NDIS_STATUS_INVALID_LENGTH, + DispatchLevel + ); + + return; + } + + // + // Process each NBL individually + // + currentNbl = NetBufferLists; + + while (currentNbl) + { + PNET_BUFFER_LIST nextNbl; + PNET_BUFFER currentNb; + + // Locate next NBL + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + + // Locate first NB (aka "packet") + currentNb = NET_BUFFER_LIST_FIRST_NB(currentNbl); + + // Transmit all NBs linked to this NBL + while(currentNb) + { + PNET_BUFFER nextNb; + + // Locate next NB + nextNb = NET_BUFFER_NEXT_NB(currentNb); + + // Transmit the NB + tapAdapterTransmit(adapter,currentNb,DispatchLevel); + + // Move to next NB + currentNb = nextNb; + } + + // Move to next NBL + currentNbl = nextNbl; + } + + // Complete all NBLs + tapSendNetBufferListsComplete( + adapter, + NetBufferLists, + NDIS_STATUS_SUCCESS, + DispatchLevel + ); + + // Attempt to complete pending read IRPs from pending TAP + // send packet queue. + tapProcessSendPacketQueue(adapter); +} + +VOID +AdapterCancelSend( + __in NDIS_HANDLE MiniportAdapterContext, + __in PVOID CancelId + ) +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + + // + // This miniport completes its sends quickly, so it isn't strictly + // neccessary to implement MiniportCancelSend. + // + // If we did implement it, we'd have to walk the Adapter->SendWaitList + // and look for any NB that points to a NBL where the CancelId matches + // NDIS_GET_NET_BUFFER_LIST_CANCEL_ID(Nbl). For any NB that so matches, + // we'd remove the NB from the SendWaitList and set the NBL's status to + // NDIS_STATUS_SEND_ABORTED, then complete the NBL. + // +} + +// IRP_MJ_READ callback. +NTSTATUS +TapDeviceRead( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success + PIO_STACK_LOCATION irpSp;// Pointer to current stack location + PTAP_ADAPTER_CONTEXT adapter = NULL; + + PAGED_CODE(); + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Fetch adapter context for this device. + // -------------------------------------- + // Adapter pointer was stashed in FsContext when handle was opened. + // + adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext; + + ASSERT(adapter); + + // + // Sanity checks on state variables + // + if (!tapAdapterReadAndWriteReady(adapter)) + { + //DEBUGP (("[%s] Interface is down in IRP_MJ_READ\n", + // MINIPORT_INSTANCE_ID (adapter))); + //NOTE_ERROR(); + + Irp->IoStatus.Status = ntStatus = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + // Save IRP-accessible copy of buffer length + Irp->IoStatus.Information = irpSp->Parameters.Read.Length; + + if (Irp->MdlAddress == NULL) + { + DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_READ\n", + MINIPORT_INSTANCE_ID (adapter))); + + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + if ((Irp->AssociatedIrp.SystemBuffer + = MmGetSystemAddressForMdlSafe( + Irp->MdlAddress, + NormalPagePriority + ) ) == NULL + ) + { + DEBUGP (("[%s] Could not map address in IRP_MJ_READ\n", + MINIPORT_INSTANCE_ID (adapter))); + + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + // BUGBUG!!! Use RemoveLock??? + + // + // Queue the IRP and return STATUS_PENDING. + // ---------------------------------------- + // Note: IoCsqInsertIrp marks the IRP pending. + // + + // BUGBUG!!! NDIS 5 implementation has IRP_QUEUE_SIZE of 16 and + // does not queue IRP if this capacity is exceeded. + // + // Is this needed??? + // + IoCsqInsertIrp(&adapter->PendingReadIrpQueue.CsqQueue, Irp, NULL); + + // Attempt to complete pending read IRPs from pending TAP + // send packet queue. + tapProcessSendPacketQueue(adapter); + + ntStatus = STATUS_PENDING; + + return ntStatus; +} + |
