static Boolean ssh_driver_copy_and_send_wan(SshNdisIMAdapter adapter, PNDIS_WAN_PACKET pkt, SshInterceptorProtocol protocol) { SshNdisIMInterceptor interceptor; SshCpuContext cpu_ctx; SshInterceptorPacket ip; SshNdisPacket packet; LONG new_value; SSH_ASSERT(adapter != NULL); SSH_ASSERT(adapter->interceptor != NULL); SSH_ASSERT(pkt != NULL); interceptor = (SshNdisIMInterceptor)adapter->interceptor; SSH_ASSERT(MAXIMUM_PROCESSORS == 1); cpu_ctx = &interceptor->cpu_ctx[0]; /* Allocate a new packet. */ ip = ssh_interceptor_packet_alloc((SshInterceptor)interceptor, SSH_PACKET_FROMADAPTER, protocol, adapter->ifnum, SSH_INTERCEPTOR_INVALID_IFNUM, pkt->CurrentLength); if (ip == NULL) { SSH_DEBUG(SSH_D_FAIL, ("Adapter %@: Failed to allocate packet", ssh_adapter_id_st_render, adapter)); return FALSE; } /* Copy the data into the packet. */ if (!ssh_interceptor_packet_copyin(ip, 0, pkt->CurrentBuffer, pkt->CurrentLength)) { SSH_DEBUG(SSH_D_FAIL, ("Adapter %@: Failed to copy WAN packet", ssh_adapter_id_st_render, adapter)); return FALSE; } packet = CONTAINING_RECORD(ip, SshNdisPacketStruct, ip); /* Set don't loopback flag. */ NdisSetPacketFlags(packet->np, NDIS_FLAGS_DONT_LOOPBACK); NDIS_SET_PACKET_STATUS(packet->np, NDIS_STATUS_SUCCESS); SSH_ASSERT(packet->parent_complete_cb == NULL_FNPTR); packet->complete_cb = ssh_driver_send_complete_cb; packet->complete_cb_handle = adapter; packet->complete_cb_param = NULL; packet->f.flags.from_local_stack = 1; packet->ip.ifnum_in = adapter->ifnum; packet->ip.flags |= SSH_PACKET_FROMPROTOCOL; packet->adapter_in = (SshAdapter)adapter; new_value = InterlockedIncrement(&adapter->ref_count); SSH_ASSERT(new_value > 0); SSH_DUMP_PACKET(SSH_D_MY5, ("Cloned packet:"), packet); ssh_interceptor_send_to_engine(interceptor, adapter, packet); ssh_interceptor_process_enqueued_packets(interceptor, cpu_ctx); return TRUE; }
VOID MPSendPackets( IN NDIS_HANDLE MiniportAdapterContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets ) /*++ Routine Description: Send Packet Array handler. Either this or our SendPacket handler is called based on which one is enabled in our Miniport Characteristics. Arguments: MiniportAdapterContext Pointer to our adapter PacketArray Set of packets to send NumberOfPackets Self-explanatory Return Value: None --*/ { PADAPT pAdapt = (PADAPT)MiniportAdapterContext; NDIS_STATUS Status; UINT i; PVOID MediaSpecificInfo = NULL; UINT MediaSpecificInfoSize = 0; FILTER_STATUS fStatus; PUCHAR pBuffer; int nBufLen; for (i = 0; i < NumberOfPackets; i++) { PNDIS_PACKET Packet, MyPacket; Packet = PacketArray[i]; //KdPrint(("微端口MPSendPackets方法,开始报文分析\n")); getMemoryCopyFromPacket(Packet, &pBuffer, &nBufLen); KdPrint(("\n==> 微端口MPSendPackets方法,开始分析报文\n")); fStatus = analyzeBuffer(pAdapt, pBuffer, &nBufLen, MODE_SEND); //fStatus = AnalysisPacket(Packet, FALSE); //fStatus = FILTER_STATUS_PASS; if (fStatus == FILTER_STATUS_DROP) { //丢弃 KdPrint(("<== 微端口MPSendPackets方法,进入FILTER_STATUS_DROP模式\n")); freeMemory(pBuffer); // 在这个函数中,任何一个被放弃的包,都必须调用NdisMSendComplete。 NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt), Packet, NDIS_STATUS_FAILURE); continue; } /* else if (fStatus == FILTER_STATUS_MODIFY_REDIRECT) { //没有发送时接收转发这种情况 } */ else if (fStatus == FILTER_STATUS_MODIFY_SEND) { //修改+发送 KdPrint(("<== 微端口MPSendPackets方法,进入FILTER_STATUS_MODIFY_SEND模式\n")); MyPacket = createPacketFromMemory_SendPool(pAdapt, pBuffer, nBufLen); //freePacketBufferOnly(Packet); // The driver should fail the send if the virtual miniport is in low // power state // if (pAdapt->MPDeviceState > NdisDeviceStateD0) { NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt), Packet, NDIS_STATUS_FAILURE); continue; } //这里原来有NDIS51的一个简化处理,放在此文件最下面了 do { PSEND_RSVD SendRsvd; NdisAcquireSpinLock(&pAdapt->Lock); // // If the below miniport is going to low power state, stop sending down any packet. // if (pAdapt->PTDeviceState > NdisDeviceStateD0) { NdisReleaseSpinLock(&pAdapt->Lock); Status = NDIS_STATUS_FAILURE; break; } pAdapt->OutstandingSends++; NdisReleaseSpinLock(&pAdapt->Lock); //NdisAllocatePacket(&Status, // &MyPacket, // pAdapt->SendPacketPoolHandle); SendRsvd = (PSEND_RSVD)(MyPacket->ProtocolReserved); SendRsvd->OriginalPkt = Packet; NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet); NdisSetPacketFlags(MyPacket, NDIS_FLAGS_RESERVED4); //我们做个标记 //现在我们自己构造buffer,所以不需要用原packet的buffer了 //NDIS_PACKET_FIRST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(Packet); //NDIS_PACKET_LAST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(Packet); // // Copy the OOB data from the original packet to the new // packet. // NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(MyPacket), NDIS_OOB_DATA_FROM_PACKET(Packet), sizeof(NDIS_PACKET_OOB_DATA)); // // Copy relevant parts of the per packet info into the new packet // NdisIMCopySendPerPacketInfo(MyPacket, Packet); // // Copy the Media specific information // NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet, &MediaSpecificInfo, &MediaSpecificInfoSize); if (MediaSpecificInfo || MediaSpecificInfoSize) { NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(MyPacket, MediaSpecificInfo, MediaSpecificInfoSize); } NdisSend(&Status, pAdapt->BindingHandle, MyPacket); if (Status != NDIS_STATUS_PENDING) { NdisIMCopySendCompletePerPacketInfo (Packet, MyPacket); //NdisFreePacket(MyPacket); freePacketBufferAndMemory(MyPacket); ADAPT_DECR_PENDING_SENDS(pAdapt); } } while (FALSE); if (Status != NDIS_STATUS_PENDING) { NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt), Packet, Status); } } else //FILTER_STATUS_PASS { //按PASS处理 KdPrint(("<== 微端口MPSendPackets方法,进入FILTER_STATUS_PASS模式\n")); freeMemory(pBuffer); // // The driver should fail the send if the virtual miniport is in low // power state // if (pAdapt->MPDeviceState > NdisDeviceStateD0) { NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt), Packet, NDIS_STATUS_FAILURE); continue; } //这里原来有NDIS51的一个简化处理,放在此文件最下面了 do { NdisAcquireSpinLock(&pAdapt->Lock); // // If the below miniport is going to low power state, stop sending down any packet. // if (pAdapt->PTDeviceState > NdisDeviceStateD0) { NdisReleaseSpinLock(&pAdapt->Lock); Status = NDIS_STATUS_FAILURE; break; } pAdapt->OutstandingSends++; NdisReleaseSpinLock(&pAdapt->Lock); NdisAllocatePacket(&Status, &MyPacket, pAdapt->SendPacketPoolHandle); if (Status == NDIS_STATUS_SUCCESS) { PSEND_RSVD SendRsvd; SendRsvd = (PSEND_RSVD)(MyPacket->ProtocolReserved); SendRsvd->OriginalPkt = Packet; NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet); NdisClearPacketFlags(MyPacket, NDIS_FLAGS_RESERVED4); //防止和上面的包混淆 NDIS_PACKET_FIRST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(Packet); NDIS_PACKET_LAST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(Packet); // // Copy the OOB data from the original packet to the new // packet. // NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(MyPacket), NDIS_OOB_DATA_FROM_PACKET(Packet), sizeof(NDIS_PACKET_OOB_DATA)); // // Copy relevant parts of the per packet info into the new packet // NdisIMCopySendPerPacketInfo(MyPacket, Packet); // // Copy the Media specific information // NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet, &MediaSpecificInfo, &MediaSpecificInfoSize); if (MediaSpecificInfo || MediaSpecificInfoSize) { NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(MyPacket, MediaSpecificInfo, MediaSpecificInfoSize); } NdisSend(&Status, pAdapt->BindingHandle, MyPacket); if (Status != NDIS_STATUS_PENDING) { NdisIMCopySendCompletePerPacketInfo (Packet, MyPacket); NdisFreePacket(MyPacket); ADAPT_DECR_PENDING_SENDS(pAdapt); } } else { // // The driver cannot allocate a packet. // ADAPT_DECR_PENDING_SENDS(pAdapt); } } while (FALSE); if (Status != NDIS_STATUS_PENDING) { NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt), Packet, Status); } } } }
/*------------------------------------------------------------------------- ssh_driver_send_packets() Sends multiple NDIS packet into the network via virtual NIC. The packets are copied and then saved into a queue. The interceptor thread then processes the packets later. Arguments: miniport_context - virtual NIC object packet_array - array of NDIS packets packet_cnt - number of packets in the array Returns: Notes: To ensure that the sequential ordering of packets is not changed in error conditions we interrupt processing packets in the array whenever we notice that we cannot handle the packet. All the remaining packets in the array are then completed with FAILURE status. If we are running low of NDIS_PACKET resources then return with failure so that upper layer notices our lack of resources and the traffic then decreases. Default IRQL: <= DISPATCH_LEVEL -------------------------------------------------------------------------*/ static Boolean ssh_driver_copy_and_send(SshNdisIMAdapter adapter, PNDIS_PACKET src) { SshNdisIMInterceptor interceptor; SshCpuContext cpu_ctx; SshNdisPacket packet; ULONG new_value; UINT flags; SSH_IRQL old_irql; Boolean status = FALSE; SSH_ASSERT(adapter != NULL); SSH_ASSERT(src != NULL); SSH_RAISE_IRQL(DISPATCH_LEVEL, &old_irql); new_value = InterlockedIncrement(&adapter->ref_count); SSH_ASSERT(new_value > 0); if (!ssh_adapter_can_accept_send(adapter)) { SSH_DEBUG(SSH_D_FAIL, ("Adapter %@: not in running state!", ssh_adapter_id_st_render, adapter)); InterlockedDecrement(&adapter->ref_count); goto end; } interceptor = (SshNdisIMInterceptor)adapter->interceptor; #ifdef _WIN32_WCE ssh_kernel_critical_section_start(&interceptor->packet_pool_cs); #endif /* _WIN32_WCE */ cpu_ctx = &interceptor->cpu_ctx[ssh_kernel_get_cpu()]; /* Try to clone the original packet's descriptors */ packet = ssh_packet_clone((SshInterceptor)interceptor, &cpu_ctx->packet_pool, SSH_PROTOCOL_ETHERNET, src, FALSE); #ifdef _WIN32_WCE ssh_kernel_critical_section_end(&interceptor->packet_pool_cs); #endif /* _WIN32_WCE */ if (packet == NULL) { SSH_DEBUG(SSH_D_FAIL, ("Adapter %@: Failed to clone packet", ssh_adapter_id_st_render, adapter)); InterlockedDecrement(&adapter->ref_count); goto end; } NDIS_SET_PACKET_STATUS(src, NDIS_STATUS_PENDING); packet->complete_cb = ssh_driver_send_complete_cb; packet->complete_cb_handle = adapter; packet->complete_cb_param = NULL; packet->parent_complete_cb = ssh_driver_parent_send_complete_cb; packet->parent_complete_handle = adapter; packet->parent_complete_np = src; packet->parent_complete_param = (void *)NDIS_STATUS_SUCCESS; /* Set don't loopback flag and per packet info */ flags = NdisGetPacketFlags(packet->np); flags |= NDIS_FLAGS_DONT_LOOPBACK; NdisSetPacketFlags(packet->np, flags); NdisIMCopySendPerPacketInfo(packet->np, src); /* Set the information that engine uses */ SSH_DUMP_PACKET(SSH_D_MY5, "Cloned packet:", packet); packet->f.flags.from_local_stack = 1; packet->ip.ifnum_in = adapter->ifnum; packet->ip.flags |= SSH_PACKET_FROMPROTOCOL; packet->adapter_in = (SshAdapter)adapter; SSH_DEBUG(SSH_D_NICETOKNOW, ("Adapter %@: %u operations pending", ssh_adapter_id_st_render, adapter, new_value)); ssh_interceptor_send_to_engine(interceptor, adapter, packet); ssh_interceptor_process_enqueued_packets(interceptor, cpu_ctx); status = TRUE; end: if (old_irql < SSH_DISPATCH_LEVEL) SSH_LOWER_IRQL(old_irql); return status; }
/*-------------------------------------------------------------------------- ssh_interceptor_send() Sends packet either down to the network or up to the protocol. --------------------------------------------------------------------------*/ void ssh_interceptor_send(SshInterceptor interceptor, SshInterceptorPacket ip, size_t media_header_len) { SshNdisPacket packet; ULONG first_buf_len = SSH_ETHERH_HDRLEN; SshNdisIMAdapter adapter; SshCpuContext cpu_ctx; Boolean use_one_buffer = FALSE; ULONG new_value; /* Sanity checks for arguments */ SSH_ASSERT(interceptor != NULL); SSH_ASSERT(ip != NULL); SSH_ASSERT((ip->flags & SSH_PACKET_FROMADAPTER) != (ip->flags & SSH_PACKET_FROMPROTOCOL)); #ifndef _WIN32_WCE SSH_ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); #endif /* _WIN32_WCE */ cpu_ctx = &interceptor->cpu_ctx[ssh_kernel_get_cpu()]; packet = CONTAINING_RECORD(ip, SshNdisPacketStruct, ip); #ifdef DEBUG_LIGHT packet->f.flags.in_engine = 0; #endif /* DEBUG_LIGHT */ adapter = (SshNdisIMAdapter)packet->adapter_in; /* Check if adapter where the packet should be sent is different where the packet originated from */ if (adapter && (adapter->ifnum == ip->ifnum_out)) { new_value = InterlockedIncrement(&adapter->ref_count); packet->adapter_out = (SshAdapter)adapter; } else { SshAdapter gen_adapter = NULL; if (ip->ifnum_out < SSH_INTERCEPTOR_MAX_ADAPTERS) { ssh_kernel_rw_mutex_lock_read(&interceptor->adapter_lock); gen_adapter = interceptor->adapter_table[ip->ifnum_out]; if (gen_adapter) { new_value = InterlockedIncrement(&gen_adapter->ref_count); packet->adapter_out = gen_adapter; } ssh_kernel_rw_mutex_unlock_read(&interceptor->adapter_lock); } if (gen_adapter == NULL) goto free_packet; adapter = (SshNdisIMAdapter)gen_adapter; } SSH_ASSERT(new_value > 0); /* Check that active adapter found and it supports the protocol */ if (!ssh_adapter_is_enabled((SshNdisIMAdapter)adapter)) { SSH_DEBUG(SSH_D_FAIL, ("active network connection not found")); goto free_packet; } #ifndef _WIN32_WCE /* Check if packet is plain IPv4 (IPv6) and then add ethernet framing */ if (ip->protocol == SSH_PROTOCOL_IP4 || ip->protocol == SSH_PROTOCOL_IP6) { if (!ssh_wan_packet_encapsulate((SshAdapter)adapter, ip)) { SSH_DEBUG(SSH_D_FAIL, ("packet framing failed")); goto free_packet; } /* Some dial-up drivers seem to expect to receive whole packet in one NDIS buffer. */ if (ip->flags & SSH_PACKET_FROMADAPTER) use_one_buffer = TRUE; } #endif /* _WIN32_WCE */ /* Add the VLAN tagging, if any */ if (packet->vlan_tag_count > 0) { if (adapter != (SshNdisIMAdapter)packet->adapter_in) { /* Engine forwards this packet to different interface. Check whether this is VLAN/QoS enabled interface and reconstruct tagging accordingly. */ switch (adapter->options & (NDIS_MAC_OPTION_8021Q_VLAN | NDIS_MAC_OPTION_8021P_PRIORITY)) { case 0: /* Adapter doesn't support IEEE 802.1q/p; drop VLAN tag(s). */ packet->vlan_tag_count = 0; break; case NDIS_MAC_OPTION_8021P_PRIORITY: /* Adapter supports only priority (QoS) tagging. */ packet->vlan_tags[0].vlan_id = 0; packet->vlan_tag_count = 1; break; default: /* Adapter supports also VLAN. Change the VLAN ID of the first tag to the one configued to this NIC driver. */ packet->vlan_tags[0].vlan_id = adapter->vlan_id; break; } } if (packet->vlan_tag_count) { unsigned char *vlan_tags; SshUInt16 i; vlan_tags = ssh_interceptor_packet_insert(ip, SSH_ETHERH_OFS_TYPE, packet->vlan_tag_count * 4); if (vlan_tags == NULL) { SSH_DEBUG(SSH_D_FAIL, ("Failed to add VLAN tags")); return; } for (i = 0; i < packet->vlan_tag_count; i++) { unsigned char *tag = vlan_tags + (i * 4); SSH_PUT_16BIT(tag, SSH_ETHERTYPE_VLAN); SSH_PUT_16BIT((tag + 2), (packet->vlan_tags[i].vlan_id << 4 | packet->vlan_tags[i].qos)); } } } NDIS_SET_PACKET_HEADER_SIZE(packet->np, SSH_ETHERH_HDRLEN); NDIS_SET_PACKET_STATUS(packet->np, NDIS_STATUS_SUCCESS); #ifdef _WIN32_WCE if (adapter->media == NdisMediumWan) { if (ip->flags & SSH_PACKET_FROMPROTOCOL) { if (!ssh_wan_send_to_adapter(adapter, packet, ip->protocol)) SSH_DEBUG(SSH_D_FAIL, ("Cannot send packet to WAN adapter")); } else if (ip->flags & SSH_PACKET_FROMADAPTER) { if (!ssh_wan_send_to_protocol(adapter, packet, ip->protocol)) SSH_DEBUG(SSH_D_FAIL, ("Cannot send packet to WAN protocol")); } else { SSH_DEBUG(SSH_D_ERROR, ("Dropping WAN packet without direction")); } ssh_interceptor_packet_free(&packet->ip); return; } #endif /* _WIN32_WCE */ if (ip->flags & SSH_PACKET_FROMPROTOCOL) { NDIS_STATUS status; /* Send packet to network */ NdisSetPacketFlags(packet->np, NDIS_FLAGS_DONT_LOOPBACK); if (cpu_ctx->in_packet_cb || cpu_ctx->in_route_cb || cpu_ctx->in_timeout_cb) { SSH_DEBUG(SSH_D_NICETOKNOW, ("Risk for recursive call; enqueueing packet 0x%p", packet)); #ifdef DEBUG_LIGHT packet->f.flags.in_send_queue = 1; #endif /* DEBUG_LIGHT */ if (cpu_ctx->in_packet_cb) { ssh_net_packet_enqueue(&cpu_ctx->send_queue[adapter->ifnum], (SshNetDataPacket)packet); cpu_ctx->packets_in_send_queue = 1; } else if (cpu_ctx->in_route_cb) { ssh_net_packet_enqueue(&cpu_ctx->route_send_queue[adapter->ifnum], (SshNetDataPacket)packet); cpu_ctx->packets_in_route_send_queue = 1; } else if (cpu_ctx->in_timeout_cb) { ssh_net_packet_enqueue( &cpu_ctx->timeout_send_queue[adapter->ifnum], (SshNetDataPacket)packet); cpu_ctx->packets_in_timeout_send_queue = 1; } } else { #ifdef DEBUG_LIGHT SSH_DEBUG(SSH_D_NICETOKNOW, ("Sending packet 0x%p to underlying driver", packet)); packet->f.flags.in_miniport = 1; #endif /* DEBUG_LIGHT */ NdisSend(&status, adapter->binding_handle, packet->np); if (status != NDIS_STATUS_PENDING) { SSH_DEBUG(SSH_D_NICETOKNOW, ("Send operation completed synchronously; " "packet=0x%p, status=%@", ssh_ndis_status_render, status)); ssh_interceptor_packet_free(&packet->ip); } } } else if (ip->flags & SSH_PACKET_FROMADAPTER) { /* Packet is ready now so check packet consistency */ if (use_one_buffer) first_buf_len = packet->packet_len; else first_buf_len += adapter->lookahead_size; first_buf_len = MIN(first_buf_len, packet->packet_len); if (!ssh_packet_get_contiguous_data((SshNetDataPacket)packet, 0, first_buf_len, FALSE)) { SSH_DEBUG(SSH_D_FAIL, ("Invalid packet")); goto free_packet; } if (cpu_ctx->in_packet_cb || cpu_ctx->in_route_cb || cpu_ctx->in_timeout_cb) { SSH_DEBUG(SSH_D_NICETOKNOW, ("Risk for recursive call; enqueueing packet 0x%p", packet)); #ifdef DEBUG_LIGHT packet->f.flags.in_recv_queue = 1; #endif /* DEBUG_LIGHT */ if (cpu_ctx->in_packet_cb) { ssh_net_packet_enqueue(&cpu_ctx->recv_queue[adapter->ifnum], (SshNetDataPacket)packet); cpu_ctx->packets_in_recv_queue = 1; } else if (cpu_ctx->in_route_cb) { ssh_net_packet_enqueue(&cpu_ctx->route_recv_queue[adapter->ifnum], (SshNetDataPacket)packet); cpu_ctx->packets_in_route_recv_queue = 1; } else if (cpu_ctx->in_timeout_cb) { ssh_net_packet_enqueue( &cpu_ctx->timeout_recv_queue[adapter->ifnum], (SshNetDataPacket)packet); cpu_ctx->packets_in_timeout_recv_queue = 1; } } else { SSH_DEBUG(SSH_D_NICETOKNOW, ("Indicating packet 0x%p to upper layers", packet)); #ifdef DEBUG_LIGHT packet->f.flags.in_protocol = 1; #endif /* DEBUG_LIGHT */ NdisMIndicateReceivePacket(adapter->handle, &packet->np, 1); } } else { SSH_NOTREACHED; } return; free_packet: /* Otherwise just drop the packet */ SSH_DEBUG(SSH_D_FAIL, ("ssh_interceptor_send(): dropping packet")); ssh_interceptor_packet_free(&packet->ip); }