TDI filter - RECEIVE
От: onyx2 Украина  
Дата: 21.05.05 10:09
Оценка:
Как реализовать отлложенную обработку IRP пакетов при приеме данных?

Я перехватываю callback ClientEventReceive, в нем проверяю данные, а затем вызываю оригинальный обработчик
ClientEventReceive, адрес которого сохранил при перехвате.

//вырезка из кода
//===============================================================
NTSTATUS MyClientEventReceive(PVOID TdiEventContext,
    CONNECTION_CONTEXT  ConnectionContext,
    ULONG  ReceiveFlags, ULONG BytesIndicated,
    ULONG  BytesAvailable, ULONG  *BytesTaken,
    PVOID  Tsdu, PIRP* IoRequestPacket)
{

    .
    .
    .

    OriginalClientEventReceive(TdiEventContext, ConnectionContext,ReceiveFlags, BytesIndicated,
        BytesAvailable, BytesTaken, Tsdu, IoRequestPacket);

    .
    .
    .
}


Мне нужно приостановить на некоторое время прием данных.
Ожидать мы не можем т.к. данный callback вызывается на DISPATH_LEVEL.

Мне советовали сделать так: сохранить где-нибудь Tsdu, в BytesTaken записать 0 и вернуть STATUS_DATA_NOT_ACCEPTED.
Когда же мне понадобится возобновить обработку приема — надо сформировать IRP пакет TDI_RECEIVE и вызвать IoCallDriver.

Да процесс передачи данных тормозится надежно, но вот возобновить передачу данных после такого приема никак не получается.

Как организовать отложенную обработку IRP при приеме?

Заранее благодарен за любые ответы, советы, ссылки...
www.cubik.biz
Re: TDI filter - RECEIVE
От: TarasCo  
Дата: 23.05.05 06:13
Оценка:
ТОпик вообще повсещен другому вопросу, но интересующая Вас техника описывается

http://rsdn.ru/Forum/Message.aspx?mid=1153682
Автор: TarasCo
Дата: 03.05.05
Да пребудет с тобою сила
Re[2]: TDI filter - RECEIVE
От: onyx2 Украина  
Дата: 01.06.05 10:43
Оценка:
Sorry, что так долго не отвечал — тестировал код приведенный в скинутой мне ссылочке (спасибо за ссылочку).
В общем с ClientEventReceiv'ом ничего путного у меня не получилось — перепробовал все что можно.

Но зато получилось с обработчиком ClientEventConnect (что возможно даже лучше):

NTSTATUS ClientEventConnect(PVOID EventContext,LONG AddressLength,PVOID Address,
                LONG UserDataLength,PVOID UserData,LONG OptionsLength,PVOID Options, 
                PVOID   *ConnectionContext,PIRP*AcceptIrp)
{
    //...
    Status= OriginalConnectHandler(EventContext,AddressLength,Address,UserDataLength,UserData,
                                   OptionsLength,Options,ConnectionContext,AcceptIrp);

   IoCopyCurrentIrpStackLocationToNext(*AcceptIrp);
   IoSetCompletionRoutine(*AcceptIrp, CompletionConnect, 0, TRUE, TRUE, TRUE);
   IoSetNextIrpStackLocation(*AcceptIrp);

   //...
   return Status;
}

//==========================================================================================
//completion routine
NTSTATUS CompletionConnect( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
{
    if (Irp->PendingReturned)
        IoMarkIrpPending(Irp);  

    return STATUS_MORE_PROCESSING_REQUIRED;
}


После такого приема соединение с удаленным клиентом надежно тормозится (находится в состоянии ожидания) — то что мне и надо.

И теперь когда мне нужно разрешить соединение, я делаю
   Irp->IoStatus.Status= STATUS_SUCCESS;
   IoCompleteRequest(Irp, IO_NO_INCREMENT); //здесь Irp == *AcceptIrp в ClientEventConnect


И далее происходит нормальная передача данных.


Вопрос: как запретить соединение в данном случае?

Я пробовал
— отменить Irp
— вызывать обработчик ClientEventDisconnect
— и то и другое.

Ничего не происходит. Точнее, удаленный клиент тупо тормозит (Я тестирую на простом клиент-сервере TCP),
а TCP-сервер начинает беситься, т.е. ф-ция read, которой он читает из сокета становится почему-то неждущей и он
в бесконечном цикле выводит сообщения. В общем неважно. Результат один — отменить соединение не получается.

Как запретить (разорвать) соединение с удаленным клиентом, когда в ClientEventConnect мы сделали ему (соединению) как бы pending?
www.cubik.biz
Re[3]: TDI filter - RECEIVE
От: TarasCo  
Дата: 01.06.05 13:41
Оценка:
Здравствуйте, onyx2, Вы писали:

O>Sorry, что так долго не отвечал — тестировал код приведенный в скинутой мне ссылочке (спасибо за ссылочку).

O>В общем с ClientEventReceiv'ом ничего путного у меня не получилось — перепробовал все что можно.

O>Но зато получилось с обработчиком ClientEventConnect (что возможно даже лучше):


O>
O>NTSTATUS ClientEventConnect(PVOID EventContext,LONG AddressLength,PVOID Address,
O>                LONG UserDataLength,PVOID UserData,LONG OptionsLength,PVOID Options, 
O>                PVOID   *ConnectionContext,PIRP*AcceptIrp)
O>{
O>    //...
O>    Status= OriginalConnectHandler(EventContext,AddressLength,Address,UserDataLength,UserData,
O>                                   OptionsLength,Options,ConnectionContext,AcceptIrp);

O>   IoCopyCurrentIrpStackLocationToNext(*AcceptIrp);
O>   IoSetCompletionRoutine(*AcceptIrp, CompletionConnect, 0, TRUE, TRUE, TRUE);
O>   IoSetNextIrpStackLocation(*AcceptIrp);

O>   //...
O>   return Status;
O>}

O>//==========================================================================================
O>//completion routine
O>NTSTATUS CompletionConnect( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
O>{
O>    if (Irp->PendingReturned)
O>        IoMarkIrpPending(Irp);  

O>    return STATUS_MORE_PROCESSING_REQUIRED;
O>}
O>


O>После такого приема соединение с удаленным клиентом надежно тормозится (находится в состоянии ожидания) — то что мне и надо.


O>И теперь когда мне нужно разрешить соединение, я делаю

O>
   Irp->>IoStatus.Status= STATUS_SUCCESS;
O>   IoCompleteRequest(Irp, IO_NO_INCREMENT); //здесь Irp == *AcceptIrp в ClientEventConnect
O>


O>И далее происходит нормальная передача данных.



O>Вопрос: как запретить соединение в данном случае?


O>Я пробовал

O> — отменить Irp
O> — вызывать обработчик ClientEventDisconnect
O> — и то и другое.

O>Ничего не происходит. Точнее, удаленный клиент тупо тормозит (Я тестирую на простом клиент-сервере TCP),

O>а TCP-сервер начинает беситься, т.е. ф-ция read, которой он читает из сокета становится почему-то неждущей и он
O>в бесконечном цикле выводит сообщения. В общем неважно. Результат один — отменить соединение не получается.

O>Как запретить (разорвать) соединение с удаленным клиентом, когда в ClientEventConnect мы сделали ему (соединению) как бы pending?


А очевидное решение в голову не приходило?

   Irp->>IoStatus.Status= STATUS_INSUFFICIENT_RESOURCES; //как вариант STATUS_UNSUCCESSFUL
   IoCompleteRequest(Irp, IO_NO_INCREMENT);



Отменять IRP не имеете права — не вы его создали
вызывать обработчик ClientEventDisconnect — нет особого смысла....

Зато нужно бы вызвать TDI_DISCONNECT — так как с точки зрения tcpip соединение принято, нужно ему сказать о закрытии

Кроме того, хотелось бы заметиь о порочности подобного подхода. Для удаленного хоста — соединение принято, он начнет слать запросы и не получая ответов, сам прикроет соединение.
Да пребудет с тобою сила
Re[4]: TDI filter - RECEIVE
От: onyx2 Украина  
Дата: 01.06.05 14:04
Оценка:
Спасибо за критику. Именно от TarasCo я этого и ждал.

TC>А очевидное решение в голову не приходило?


TC>
   Irp->>>IoStatus.Status= STATUS_INSUFFICIENT_RESOURCES; //как вариант STATUS_UNSUCCESSFUL
TC>   IoCompleteRequest(Irp, IO_NO_INCREMENT); 
TC>


Вот это не работает.


TC>Отменять IRP не имеете права — не вы его создали

TC>вызывать обработчик ClientEventDisconnect — нет особого смысла....

Правильное замечание, я это понял чуть раньше. Я устанавливал CancelRoutine, а кто-то (tcpip.sys) обнулял поле Irp->CancelRoutine.
В общем — все верно.

TC>Зато нужно бы вызвать TDI_DISCONNECT — так как с точки зрения tcpip соединение принято, нужно ему сказать о закрытии


Тоже верно.
Я сформировал вручную TDI_DISCONNECT IRP. И такой вариант работает (соединение надежно обрывается):

PIRP Irp;
PDEVICE_OBJECT pDev= IoGetRelatedDeviceObject(pConnectionInfo->Handle);  //pConnectionInfo - моя структура
if ( pDev ) {
   Irp = TdiBuildInternalDeviceControlIrp(TDI_DISCONNECT, pDev, pConnectionInfo->Handle, NULL, NULL);
   if (Irp) {       
       TdiBuildDisconnect(Irp, pDev, pConnectionInfo->Handle, NULL, NULL, NULL, TDI_DISCONNECT_RELEASE, NULL, NULL);
       IoCallDriver(pDev, Irp);                        
   }
}


TC>Кроме того, хотелось бы заметиь о порочности подобного подхода. Для удаленного хоста — соединение принято, он начнет слать запросы и не получая ответов, сам прикроет соединение.


Все нормально — главное данные к серверу попадать не будут.
www.cubik.biz
Re[5]: TDI filter - RECEIVE
От: TarasCo  
Дата: 01.06.05 14:48
Оценка:
Здравствуйте, onyx2, Вы писали:

O>Спасибо за критику. Именно от TarasCo я этого и ждал.


TC>>А очевидное решение в голову не приходило?


TC>>
   Irp->>>>IoStatus.Status= STATUS_INSUFFICIENT_RESOURCES; //как вариант STATUS_UNSUCCESSFUL
TC>>   IoCompleteRequest(Irp, IO_NO_INCREMENT); 
TC>>


O>Вот это не работает.


Это должно работать для клиента ( afd.sys например ). Он послал IRP и должен занть о его не легкой судьбе . netstat -a естественно продожит показывть наличие установленного соединения, поскольку драйверу то мы ничего не сказали, поэтому:

O>
O>PIRP Irp;
O>PDEVICE_OBJECT pDev= IoGetRelatedDeviceObject(pConnectionInfo->Handle);  //pConnectionInfo - моя структура
O>if ( pDev ) {
O>   Irp = TdiBuildInternalDeviceControlIrp(TDI_DISCONNECT, pDev, pConnectionInfo->Handle, NULL, NULL);
O>   if (Irp) {       
O>       TdiBuildDisconnect(Irp, pDev, pConnectionInfo->Handle, NULL, NULL, NULL, TDI_DISCONNECT_RELEASE, NULL, NULL);
O>       IoCallDriver(pDev, Irp);                        
O>   }
O>}            
O>


TC>>Кроме того, хотелось бы заметиь о порочности подобного подхода. Для удаленного хоста — соединение принято, он начнет слать запросы и не получая ответов, сам прикроет соединение.


O>Все нормально — главное данные к серверу попадать не будут.


Если надеятся на устойчивость tcpip.sys — то нормально. А так — прямой путь к DoS, что для серверов не желательно . Представте — атака с 1000 хостов на веб сервер (для примера). Устанавливаем соединение и сразу 200 килогшрамм мусора туда. Это все на сервере жрет несвопабельную память ( tcpip честно пытается держать принятые данные), вызвает лишнее копирование памяти ( tcpip честно пытается возрщать буфера драйверу сетевой карты ).
Да пребудет с тобою сила
Re[6]: TDI filter - RECEIVE
От: onyx2 Украина  
Дата: 06.06.05 09:40
Оценка:
TC>Если надеятся на устойчивость tcpip.sys — то нормально. А так — прямой путь к DoS, что для серверов не желательно . Представте — атака с 1000 хостов на веб сервер (для примера). Устанавливаем соединение и сразу 200 килогшрамм мусора туда. Это все на сервере жрет несвопабельную память ( tcpip честно пытается держать принятые данные), вызвает лишнее копирование памяти ( tcpip честно пытается возрщать буфера драйверу сетевой карты ).

Посоветуйте тогда, что можно предпринять, чтобы таких ситуаций не возникало.
www.cubik.biz
Re[7]: TDI filter - RECEIVE
От: TarasCo  
Дата: 06.06.05 11:11
Оценка: 7 (1)
Здравствуйте, onyx2, Вы писали:

TC>>Если надеятся на устойчивость tcpip.sys — то нормально. А так — прямой путь к DoS, что для серверов не желательно . Представте — атака с 1000 хостов на веб сервер (для примера). Устанавливаем соединение и сразу 200 килогшрамм мусора туда. Это все на сервере жрет несвопабельную память ( tcpip честно пытается держать принятые данные), вызвает лишнее копирование памяти ( tcpip честно пытается возрщать буфера драйверу сетевой карты ).


O>Посоветуйте тогда, что можно предпринять, чтобы таких ситуаций не возникало.


1)Переформулировать задачу. Допустим, отложить IRP, но не надолго — т.е не блокировать его на неопределенный срок, а обработать во вспомогательном потоке например. В таком случае, ничего страшного не произойдет — для сети несколько десятков мс — нестрашная задержка.
2)Если нужно таки блокировать ( для подтверждения оператором например ) — надежным способом будет переход на NDIS уровень. Принятый пакет нужно проанализировать. Если это SYN TCP пакет — скопровать его в собственный буфер ( лучше всего, выделить заранее набор буферов в pagable памяти и заранее же их залочить ) и поставить в очередь ( желательно, ограничить ее по длине и по времени пребывания, высшим классом будет еще и защита от флуда ). Сетевой карте говорим, что пакет успешно принят, tcpip.sys — ничего не говорим. При подтверждении приема соединения — собираем свой пакет и отдаем его для tcpip.sys. При подтверждении от него, ничего не говорим минипорту сетевой платы.
Да пребудет с тобою сила
Re[8]: TDI filter - RECEIVE
От: Mike_MS  
Дата: 06.06.05 17:12
Оценка:
Здравствуйте, TarasCo, Вы писали:

TC>1)Переформулировать задачу. Допустим, отложить IRP, но не надолго — т.е не блокировать его на неопределенный срок, а обработать во вспомогательном потоке например. В таком случае, ничего страшного не произойдет — для сети несколько десятков мс — нестрашная задержка.


А вот это не верно — тоесть не верно в случае с Майкрософтом — теоретически все правильно, а практически AFD имеет один баг. В этом случае клиенты WinInet будут получать некорректный поток (тоесть там данные будут местами перепутаны).
Re[9]: TDI filter - RECEIVE
От: TarasCo  
Дата: 07.06.05 07:51
Оценка:
Здравствуйте, Mike_MS, Вы писали:

M_M>Здравствуйте, TarasCo, Вы писали:


TC>>1)Переформулировать задачу. Допустим, отложить IRP, но не надолго — т.е не блокировать его на неопределенный срок, а обработать во вспомогательном потоке например. В таком случае, ничего страшного не произойдет — для сети несколько десятков мс — нестрашная задержка.


M_M>А вот это не верно — тоесть не верно в случае с Майкрософтом — теоретически все правильно, а практически AFD имеет один баг. В этом случае клиенты WinInet будут получать некорректный поток (тоесть там данные будут местами перепутаны).



Сразу скажу, не проверял, просто соображения на этот счет:

Возможно, это не баг, а фича. tcpip.sys не дагадывается о нашей подлости, и считает, что клиент принял соединение. Естественно, при получении данных он вызовет нотификатор ClientEventReceive. А подтверждение о приеме соединения придет от нашего фильтра позже. Возможно, в этот момент у afd.sys снесет крышу . Как метод борьбы с этим могу предложить попробывать:
1) устанавливать ClientEventReceive = NULL до момента пропуска IRP об установлении соединения. Правда, в этом случае tcpip.sys может будет просто скидывать данные. Я не встречал нигде описания, должен ли драйвер в таком случае кешировать данные. По идее — должен. Ведь драйвер может "подумать", что клиент вызовет TDI_RECEIVE. Тем более, логика работы TCP предусматривает наличие буфера для сборки потока данных.
2) устанавливать ClientEventReceive = NULL и вызывать самому TDI_RECEIVE, самому же организовывать очередь. Короче написать свой аналог afd.sys с кешем и.т.д. Но это сложно и трудоемко
Да пребудет с тобою сила
Re[10]: TDI filter - RECEIVE
От: Mike_MS  
Дата: 07.06.05 14:01
Оценка:
M_M>>А вот это не верно — тоесть не верно в случае с Майкрософтом — теоретически все правильно, а практически AFD имеет один баг. В этом случае клиенты WinInet будут получать некорректный поток (тоесть там данные будут местами перепутаны).


TC>Сразу скажу, не проверял, просто соображения на этот счет:


TC>Возможно, это не баг, а фича. tcpip.sys не дагадывается о нашей подлости, и считает, что клиент принял соединение. Естественно, при получении данных он вызовет нотификатор ClientEventReceive. А подтверждение о приеме соединения придет от нашего фильтра позже. Возможно, в этот момент у afd.sys снесет крышу . Как метод борьбы с этим могу предложить попробывать:

TC>1) устанавливать ClientEventReceive = NULL до момента пропуска IRP об установлении соединения. Правда, в этом случае tcpip.sys может будет просто скидывать данные. Я не встречал нигде описания, должен ли драйвер в таком случае кешировать данные. По идее — должен. Ведь драйвер может "подумать", что клиент вызовет TDI_RECEIVE. Тем более, логика работы TCP предусматривает наличие буфера для сборки потока данных.
TC>2) устанавливать ClientEventReceive = NULL и вызывать самому TDI_RECEIVE, самому же организовывать очередь. Короче написать свой аналог afd.sys с кешем и.т.д. Но это сложно и трудоемко

Да нет, там причина совсем другая. Тут у нас 3 недели была ескалация с Майкрософт по поводу этого вопроса. AFD для оптимизации делает некоторые вещи, которые потом вылазят боком для WinInet. Буферизация — один из методов как этого избежать — но есть другой подводный камень — data flow control в этом случае — на соединении с большой скоростью можем получить "снежный ком".
Re: TDI filter - RECEIVE 2
От: onyx2 Украина  
Дата: 06.10.06 13:10
Оценка:
Приветствую.

Все тот же callback ClientEventReceive.
Нужен совет, как поступить.
Сейчас опишу ситуацию.

Реализация callback'а примерно следующая:
//=============================================================================
// TDI_EVENT_RECEIVE
// 
NTSTATUS HookEventReceive(
    IN PVOID    TdiEventContext,
    IN CONNECTION_CONTEXT    ConnectionContext,
    IN ULONG    ReceiveFlags,
    IN ULONG    BytesIndicated,
    IN ULONG    BytesAvailable,
    OUT ULONG    *BytesTaken,
    IN PVOID    Tsdu,
    OUT PIRP    *IoRequestPacket    
    )
{

  //...

      //call original handler
    ntStatus = EventReceive(EventReceiveContext,
        ConnectionContext,
        ReceiveFlags,
        BytesIndicated,
        BytesAvailable,
        BytesTaken,
        Tsdu,
        IoRequestPacket);

    if( ntStatus == STATUS_MORE_PROCESSING_REQUIRED    && IoRequestPacket && *IoRequestPacket ) {
        PIO_STACK_LOCATION        IrpSp = NULL;
        PSF_DEVICE_EXTENSION    pDeviceExtension = pConnInfo->DeviceExtension;
        //
        // Handle TDI_RECEIVE request passdown
        // -----------------------------------
        // The caller has provided a TDI_RECEIVE request at IoRequestPacket
        // to obtain the remaining Tdsu data.
        //
        //    We want to filter processing of the request using our own completion handler. 
        //    The key steps are:
        //
        //   1.) Copy the current IRP stack location to next.
        //   2.) Set our completion routine.
        //   3.) Emulate the effect of IoCallDriver in advancing the
        //       IRP stack location.
        //
        
        // Get the current I/O stack location.
        IrpSp = IoGetCurrentIrpStackLocation( (*IoRequestPacket) );
        IoCopyCurrentIrpStackLocationToNext( (*IoRequestPacket) );

        // set completion routine            
        IoSetCompletionRoutine(
            (*IoRequestPacket),
            HookEventReceiveCompletion,
            pDeviceExtension,    //ВАЖНО: вот здесь я сохраняю в качестве контекста указатель на pDeviceExtension
            TRUE,
            TRUE,
            TRUE
            );

        IoSetNextIrpStackLocation(*IoRequestPacket);            
    }


    //error or restrict
    if (STATUS_DATA_NOT_ACCEPTED == ntStatus) {
        if (BytesTaken) { *BytesTaken = 0; }
    }

  return ntStatus;
}


//=============================================================================
// HookEventReceive IoCompletion
//
//note:
//     
NTSTATUS HookEventReceiveCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, void *Context )
{        
    PSF_DEVICE_EXTENSION        pDeviceExtension = NULL;
    
    if ( !DeviceObject || !Irp || !Context ) { return STATUS_SUCCESS; }
    
    pDeviceExtension    = (PSF_DEVICE_EXTENSION)(Context);
        
    // propagate the IRP pending flag
    if (Irp->PendingReturned) {
        IoMarkIrpPending(Irp);
    }
    
    //pDeviceExtension->pFilterDeviceObject - это мое фильтрующее устройство приатаченное к \Device\Tcp
    if (pDeviceExtension->pFilterDeviceObject != DeviceObject) {
        //вот сюда очень часто попадаю!!!
        DBG_PRINT(DBG_MSG_ERROR, "invalid device object pointer!");
        return STATUS_SUCCESS;
    }    
    
    //...    
    
    //always return STATUS_SUCCESS - для того чтобы другие драйвера, подписавшие IoCompletion для данного Irp, тоже получили управление
    return STATUS_SUCCESS;    
}


Почему мне приходят Irp запросы с не моим DeviceObject?
И вопрос: как поступать в такой ситуации?

Т.е. меня интересует: это нормальная ситуация (когда приходят Irp запросы с не моим DeviceObject)? Или ошибочная (и нужно немедленно прекращать обработку Irp)?

P.S. Драйвер тестируется под verifier'ом.
www.cubik.biz
Re[2]: TDI filter - RECEIVE 2
От: onyx2 Украина  
Дата: 11.10.06 06:05
Оценка:
Наверно и себе и людям мозги заморочил
Сам с Irp стеком мудрю, а потом еще и спрашиваю: "почему DeviceObject не такой как надо?".

В общем вопрос снимается с обсуждения.
www.cubik.biz
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.