123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176 |
- /*
- * 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;
- }
|