c - NDIS filter driver' FilterReceiveNetBufferLists handler isn't called -


i developing ndis filter driver, , fount filterreceivenetbufferlists never called (the network blocked) under condition (like open wireshark or click "interface list" button of it). when start capturing, filterreceivenetbufferlists normal (network restored), strange.

i found when mannually return ndis_status_failure ndisfoidrequest function in oid originating place of winpcap driver (biocqueryoid & biocsetoid switch branch of npf_iocontrol), driver won't block network (also winpcap can't work).

is there wrong ndisfoidrequest call?

the deviceio routine in packet.c originates oid requests:

case biocqueryoid: case biocsetoid:      trace_message(packet_debug_loud, "biocsetoid - biocqueryoid");      //     // gain ownership of ndis handle     //     if (npf_startusingbinding(open) == false)     {         //         // mac unbindind or unbound         //         set_failure_invalid_request();         break;     }       // extract request list of free ones     requestlistentry = exinterlockedremoveheadlist(&open->requestlist, &open->requestspinlock);     if (requestlistentry == null)     {         //         // release ownership of ndis handle         //         npf_stopusingbinding(open);          set_failure_nomem();         break;     }      prequest = containing_record(requestlistentry, internal_request, listelement);      //     //  see if ndis request     //     oiddata = irp->associatedirp.systembuffer;      if ((irpsp->parameters.deviceiocontrol.inputbufferlength == irpsp->parameters.deviceiocontrol.outputbufferlength) &&         (irpsp->parameters.deviceiocontrol.inputbufferlength >= sizeof(packet_oid_data)) &&         (irpsp->parameters.deviceiocontrol.inputbufferlength >= sizeof(packet_oid_data) - 1 + oiddata->length))     {         trace_message2(packet_debug_loud, "biocsetoid|biocqueryoid request: oid=%08lx, length=%08lx", oiddata->oid, oiddata->length);          //         //  buffer valid         //         ndiszeromemory(&prequest->request, sizeof(ndis_oid_request));         prequest->request.header.type = ndis_object_type_oid_request;         prequest->request.header.revision = ndis_oid_request_revision_1;         prequest->request.header.size = ndis_sizeof_oid_request_revision_1;          if (functioncode == biocsetoid)         {             prequest->request.requesttype = ndisrequestsetinformation;             prequest->request.data.set_information.oid = oiddata->oid;              prequest->request.data.set_information.informationbuffer = oiddata->data;             prequest->request.data.set_information.informationbufferlength = oiddata->length;         }         else         {             prequest->request.requesttype = ndisrequestqueryinformation;             prequest->request.data.query_information.oid = oiddata->oid;              prequest->request.data.query_information.informationbuffer = oiddata->data;             prequest->request.data.query_information.informationbufferlength = oiddata->length;         }          ndisresetevent(&prequest->internalrequestcompletedevent);          if (*((pvoid *) prequest->request.sourcereserved) != null)         {             *((pvoid *) prequest->request.sourcereserved) = null;         }         //         //  submit request         //         prequest->request.requestid = (pvoid) npf6x_request_id;         assert(open->adapterhandle != null);         status = ndisfoidrequest(open->adapterhandle, &prequest->request);         //status = ndis_status_failure;     }     else     {         //         // release ownership of ndis handle         //         npf_stopusingbinding(open);          //         //  buffer small         //         set_failure_buffer_small();         break;     }      if (status == ndis_status_pending)     {         ndiswaitevent(&prequest->internalrequestcompletedevent, 1000);         status = prequest->requeststatus;     }      //     // release ownership of ndis handle     //     npf_stopusingbinding(open);      //     // complete request     //     if (functioncode == biocsetoid)     {         oiddata->length = prequest->request.data.set_information.bytesread;         trace_message1(packet_debug_loud, "biocsetoid completed, bytesread = %u", oiddata->length);     }     else     {         if (functioncode == biocqueryoid)         {             oiddata->length = prequest->request.data.query_information.byteswritten;              if (status == ndis_status_success)             {                 //                 // check stupid bug of nortel driver ipsecw2k.sys v. 4.10.0.0 doesn't set byteswritten correctly                 // driver 1 shipped nortel client contivity vpn client v04_65.18, , md5 buggy (unsigned) driver                 // 3c2ff8886976214959db7d7ffaefe724 *ipsecw2k.sys (there multiple copies of binary same exact version info!)                 //                  // (certified) driver shipped nortel client contivity vpn client v04_65.320 doesn't seem affected bug.                 //                 if (prequest->request.data.query_information.byteswritten > prequest->request.data.query_information.informationbufferlength)                 {                     trace_message2(packet_debug_loud, "bogus return ndisrequest (query): bytes written (%u) > infobufferlength (%u)!!", prequest->request.data.query_information.byteswritten, prequest->request.data.query_information.informationbufferlength);                      status = ndis_status_invalid_data;                 }             }              trace_message1(packet_debug_loud, "biocqueryoid completed, byteswritten = %u", oiddata->length);         }     }       exinterlockedinserttaillist(&open->requestlist, &prequest->listelement, &open->requestspinlock);      if (status == ndis_status_success)     {         set_result_success(sizeof(packet_oid_data) - 1 + oiddata->length);     }     else     {         set_failure_invalid_request();     }      break; 

three filter oid routines:

_use_decl_annotations_ ndis_status npf_oidrequest(     ndis_handle         filtermodulecontext,     pndis_oid_request   request     ) {     popen_instance          open = (popen_instance) filtermodulecontext;     ndis_status             status;     pndis_oid_request       clonedrequest=null;     boolean                 bsubmitted = false;     pfilter_request_context context;     boolean                 bfalse = false;      trace_enter();          {         status = ndisallocatecloneoidrequest(open->adapterhandle,                                             request,                                             npf6x_alloc_tag,                                             &clonedrequest);         if (status != ndis_status_success)         {             trace_message(packet_debug_loud, "fileroidrequest: cannot clone request\n");             break;         }          context = (pfilter_request_context)(&clonedrequest->sourcereserved[0]);         *context = request;          bsubmitted = true;          //         // use same request id         //         clonedrequest->requestid = request->requestid;          open->pendingoidrequest = clonedrequest;          status = ndisfoidrequest(open->adapterhandle, clonedrequest);          if (status != ndis_status_pending)         {             npf_oidrequestcomplete(open, clonedrequest, status);             status = ndis_status_pending;         }       }while (bfalse);      if (bsubmitted == false)     {         switch(request->requesttype)         {             case ndisrequestmethod:                 request->data.method_information.bytesread = 0;                 request->data.method_information.bytesneeded = 0;                 request->data.method_information.byteswritten = 0;                 break;              case ndisrequestsetinformation:                 request->data.set_information.bytesread = 0;                 request->data.set_information.bytesneeded = 0;                 break;              case ndisrequestqueryinformation:             case ndisrequestquerystatistics:             default:                 request->data.query_information.byteswritten = 0;                 request->data.query_information.bytesneeded = 0;                 break;         }      }      trace_exit();     return status;  }  //-------------------------------------------------------------------  _use_decl_annotations_ void npf_canceloidrequest(     ndis_handle             filtermodulecontext,     pvoid                   requestid     ) {     popen_instance                      open = (popen_instance) filtermodulecontext;     pndis_oid_request                   request = null;     pfilter_request_context             context;     pndis_oid_request                   originalrequest = null;     boolean                             bfalse = false;      filter_acquire_lock(&open->oidlock, bfalse);      request = open->pendingoidrequest;      if (request != null)     {         context = (pfilter_request_context)(&request->sourcereserved[0]);          originalrequest = (*context);     }      if ((originalrequest != null) && (originalrequest->requestid == requestid))     {         filter_release_lock(&open->oidlock, bfalse);          ndisfcanceloidrequest(open->adapterhandle, requestid);     }     else     {         filter_release_lock(&open->oidlock, bfalse);     } }  //-------------------------------------------------------------------  _use_decl_annotations_ void npf_oidrequestcomplete(     ndis_handle         filtermodulecontext,     pndis_oid_request   request,     ndis_status         status     ) {     popen_instance                      open = (popen_instance) filtermodulecontext;     pndis_oid_request                   originalrequest;     pfilter_request_context             context;     boolean                             bfalse = false;      trace_enter();      context = (pfilter_request_context)(&request->sourcereserved[0]);     originalrequest = (*context);      //     // internal request     //     if (originalrequest == null)     {         trace_message1(packet_debug_loud, "status= %p", status);         npf_internalrequestcomplete(open, request, status);         trace_exit();         return;     }       filter_acquire_lock(&open->oidlock, bfalse);      assert(open->pendingoidrequest == request);     open->pendingoidrequest = null;      filter_release_lock(&open->oidlock, bfalse);       //     // copy information returned request original request     //     switch(request->requesttype)     {         case ndisrequestmethod:             originalrequest->data.method_information.outputbufferlength =  request->data.method_information.outputbufferlength;             originalrequest->data.method_information.bytesread = request->data.method_information.bytesread;             originalrequest->data.method_information.bytesneeded = request->data.method_information.bytesneeded;             originalrequest->data.method_information.byteswritten = request->data.method_information.byteswritten;             break;          case ndisrequestsetinformation:             originalrequest->data.set_information.bytesread = request->data.set_information.bytesread;             originalrequest->data.set_information.bytesneeded = request->data.set_information.bytesneeded;             break;          case ndisrequestqueryinformation:         case ndisrequestquerystatistics:         default:             originalrequest->data.query_information.byteswritten = request->data.query_information.byteswritten;             originalrequest->data.query_information.bytesneeded = request->data.query_information.bytesneeded;             break;     }       (*context) = null;      ndisfreecloneoidrequest(open->adapterhandle, request);      ndisfoidrequestcomplete(open->adapterhandle, originalrequest, status);      trace_exit(); } 


below mail received jeffrey, think best answer question:)


the packet filter works differently lwfs versus protocols. let me give background. you’ll know of this, i’m sure, it’s helpful review basics, can sure we’re both on same page. ndis datapath organized tree:

enter image description here

packet filtering happens @ 2 places in stack:

(a) once in miniport hardware, and

(b) @ top of stack, below protocols.

ndis track each protocols’ packet filter separately, efficiency. if 1 protocol asks see packets (promiscuous mode), not protocols have sort through traffic. really, there (p+1) different packet filters in system, p number of protocols:

enter image description here

now if there these different packet filters, how oid_gen_current_packet_filter work? ndis ndis tracks each protocols’ packet filter, merges filter @ top of miniport stack. suppose protocol0 requests packet filter of a+b, , protocol1 requests packet filter of c, , protocol2 requests packet filter of b+d:

enter image description here

then @ top of stack, ndis merges packet filters a+b+c+d. gets sent down filter stack, , miniport.

because of merging process, no matter protocol2 sets packet filter, protocol2 cannot affect other protocols. protocols don’t have worry “sharing” packet filter. however, same not true lwf. if lwf1 decides set new packet filter, not merged:

enter image description here

in above picture, lwf1 decided change packet filter c+e. overwrote protocols’ packet filter of a+b+c+d, meaning flags a, b, , d never make hardware. if protocols relying on flags a, b, or d, protocols’ functionality broken.

this design – lwfs have great power, , can stack. designed have power veto packet filters of other protocols. in case, don’t want mess other protocols; want filter have minimal effects on rest of system.

so want keep track of packet filter is, , never remove flags current packet filter. means should query packet filter when filter attaches, , update cached value whenever see oid_gen_current_packet_filter come down above.

if usermode app needs more flags current packet filter has, can issue oid , add additional flags. means hardware’s packet filter have more flags. no protocol’s packet filter change, protocols still see same stuff.

enter image description here

in above example, filter lwf1 playing nice. though lwf1 cares flag e, lwf1 has still passed down flags a, b, c, , d too, since lwf1 knows protocols above want flags set.

the code manage isn’t bad, once idea of needs done manage packet filter:

  1. always track latest packet filter protocols above.

  2. never let nic see packet filter has fewer flags protocols’ packet filter.

  3. add in own flags needed.

ok, gives idea of packet filter , how manage it. next question how map “promiscuous mode” , “non-promiscuous mode” actual flags? let’s define these 2 modes carefully:

non-promiscuous mode: capture tool sees receive traffic operating system have received. if hardware filters out traffic, don’t want see traffic. user wants diagnose local operating system in normal state.

promiscuous mode: give capture tool many receive packets possible – ideally every bit transferred on wire. doesn’t matter whether packet destined local host or not. user wants diagnose network, , wants see happening on network.

i think when @ way, consequences packet filter flags straightforward. non-promiscuous mode, not change packet filter. let hardware packet filter whatever operating system wants be. promiscuous mode, add in ndis_packet_type_promiscuous flag, , nic hardware give possibly can.

so if it’s simple lwf, why did old protocol-based npf driver need many more flags? old protocol-based driver had couple problems:

  1. it can’t “non-promiscuous mode” correct

  2. it can’t capture send-packets of other protocols

the first problem npf-protocol can’t implement our definition of “non-promiscuous mode” correctly. if npf-the-protocol wants see receive traffic os sees it, packet filter should use? if sets packet filter of zero, npf won’t see traffic. npf can set packet filter of directed|broadcast|multicast. that’s assumption of tcpip , other protocols setting. if tcpip decided set promiscuous flag (certain socket flags cause happen), npf seeing fewer packets tcpip see, wrong. if npf sets promiscuous flag, see more traffic tcpip see, wrong. it’s tough capturing protocol decide flags set sees same packets rest of os sees. lwfs don’t have problem, since lwfs see combined oid after protocols’ filters merged.

the second problem npf-protocol needed loopback mode capture sent-packets. lwfs don’t need loopback -- in fact, actively harmful. let’s use same diagram see why. here’s npf capturing receive path in promiscuous mode:

enter image description here

now let’s see happens when unicast packet received:

enter image description here

since packet matches hardware’s filter, packet comes stack. when packet gets protocol layer, ndis gives packet both protocols, tcpip , npf, since both protocols’ packet filters match packet. works enough.

but send path tricky:

enter image description here

tcpip sent packet, npf never got chance see it! solve problem, ndis added notion of “loopback” packet filter flag. flag little bit special, since doesn’t go hardware. instead, loopback packet filter tells ndis bounce send-traffic receive path, diagnostics tools npf can see packets. looks this:

enter image description here

now loopback path used diagnostics tools, haven’t spent time optimizing it. and, since means send packets travel across stack twice (once normal send path, , again in receive path), has @ least double cpu cost. why said ndis lwf able capture @ higher throughput protocol, since lwfs don’t need loopback path.

why not? why don’t lwfs need loopback? if go , @ last few diagrams, you’ll see of our lwfs saw traffic – both send , receive – without loopback. lwf meets requirements of seeing traffic, without needing bother loopback. that’s why lwf should never set loopback flags.

ok, email got longer wanted, hope clears of questions around packet filter, loopback path, , how lwfs different protocols. please let me know if wasn’t clear, or if diagrams didn’t come through.


Comments

Popular posts from this blog

c# - Send Image in Json : 400 Bad request -

jquery - Fancybox - apply a function to several elements -

An easy way to program an Android keyboard layout app -