summaryrefslogtreecommitdiff
path: root/windows/TapDriver6/txpath.c
diff options
context:
space:
mode:
authorGrant Limberg <glimberg@gmail.com>2015-04-25 18:59:52 -0700
committerGrant Limberg <glimberg@gmail.com>2015-04-25 18:59:52 -0700
commitec45aeb42aa644b71654b0467c4aebc80e896420 (patch)
treee7d544030dfc9e279007a8455a12206e895446eb /windows/TapDriver6/txpath.c
parenta86a0ab2b13ae19d99f4667666a25b337e0b93f1 (diff)
parent7af1f3a79ab8928a3b49bac458ee87368c46edc5 (diff)
downloadinfinitytier-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.c1175
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;
+}
+