Назвать его недокументированным нельзя - в составе Microsoft Visual C++ для него есть заголовочные файлы. Правда, практически без комментариев. Документация (как официальная, так, что любопытно, и поставляемая независимыми разработчиками и издательствами) старательно об этом интерфейсе умалчивает. Хотя он, без сомнения, является базовым при разработке низкоуровневых сетевых приложений. Думаю, читатели уже догадались, что речь идет о NAL (Network Abstraction Layer), программный интерфейс к которому предоставляет модуль NAL.DLL.

Скудость сведений об этом API поражает. Потратив около недели на их поиск в Internet, я обнаружил лишь несколько упоминаний о NAL, да и то в виде вопросов, осторожно заданных в паре конференций: «А что это такое и как этим пользоваться?» А в MSDN, TechNet и других источниках (я просматривал все начиная с июля 1996 г.) NAL тоже только упоминается.

Пришлось искать обходные пути. В состав SMS (System Management Server) входит мощное средство сканирования сети - Network Monitor. Так вот, эта прекрасно спроектированная программа позволяет писать расширения, которые представляют собой DLL с определенным набором экспортируемых функций. Вторая версия Network Monitor, которая поставляется в составе Microsoft BackOffice 4.5, предполагает уже использование COM-интерфейса для модулей расширения, технологии написания которых посвящено немало места во всех выпусках MSDN. Но как создать собственный аналог этой программы, остается тайной корпорации Microsoft.

Можно, конечно, проделать весь путь разработки низкоуровневого сетевого приложения, начав с написания сетевого драйвера уровня TDI (см. ниже). В DDK очень подробно рассказано, как это делается. Но зачем тратить недели и месяцы на повторение того, что входит в стандартную поставку операционной системы? Задавшись этим простым вопросом, я все-таки решил узнать, как программировать NAL. Оказалось, что совсем несложно.

Надо отдать должное программистам Microsoft - они разработали удивительно логичный, эффективный и интуитивно понятный программный интерфейс.

Для проведения экспериментов нужно иметь под рукой Windows 95/98/NT и MSVC 6.0.

OSI и архитектура сети Microsoft

Здесь я сделаю небольшое отступление в область теории сетей и ее реализации компанией Microsoft.

В 1978 г. International Standards Organization (ISO) приняла модель Open System Interconnect (OSI) в качестве первого шага стандартизации многочисленных протоколов, необходимых для построения сетей.

ISO OSI:

  • предоставила стандарты обмена данными, поддерживаемые различными производителями программного и аппаратного обеспечения;
  • включает семь уровней с определением функций каждого из них и рекомендации по организации взаимодействия между компонентами разных уровней;
  • определяет протоколы и интерфейсы, соответствующие каждому уровню модели.

Реализация каждого уровня OSI представляет собой независимый модуль. При разработке аппаратных и программных модулей должны быть реализованы следующие принципы.

  • Переход на другой уровень возможен только при необходимости изменения уровня абстракции.
  • Каждый уровень предоставляет стандартизованные для него функции.
  • Функции каждого уровня выбираются из соображений поддержки стандартных протоколов.
  • При разграничении уровней минимизируется информация об их интерфейсах.

Чтобы понять механизм работы сетевого монитора, необходимо очень ясно представлять себе, как в Microsoft Windows NT выполнен стек OSI.

Реализация модели OSI в MS Windows NT основана на понятии boundary - функциональном интерфейсе между уровнями в архитектуре сетевой подсистемы Windows NT. Эта концепция позволяет сделать систему открытой для разработчиков сетевых устройств, сервисов и драйверов.

Определены два уровня абстракции:

  • Network Driver Interface Specification (NDIS);
  • Transport Driver Interface (TDI).

Transport Driver Interface. TDI представляет собой обобщенный интерфейс между ОС и протоколами транспортного уровня (AppleTalk, NetBT, Tcp/Ip и т. д.). Это позволяет обеспечить независимость ОС и приложений от используемых транспортов. В отличие от NDIS, TDI - лишь стандарт на обмен информацией между двумя уровнями абстракции, но не программный модуль.

Network Driver Interface Specification 3.0. В 1989 г. специалистами компаний 3Com и Microsoft была разработана спецификация, определяющая интерфейс между уровнем Network в модели OSI и более высоким уровнем, реализующим драйверы протоколов. NDIS - это стандарт, позволивший одновременно функционировать многим сетевым адаптерам и многим драйверам протоколов.

Он определяет интерфейс к драйверам устройств, а именно NDIS-wrapper, реализацией которого и является NAL.

Шаг первый - установка NAL

Для того чтобы работали все примеры программ, которые будут разбираться, необходимо установить Network Monitor Agent. Для этого нужно в Control Panel найти значок Network, в нем закладку Services и нажать на кнопку Add. Затем следует выбрать из списка Network Monitor Agent, и после перезагрузки системы эту часть исследования можно смело считать завершенной.

Шаг второй - вызов функций NAL

Теперь мы готовы начать разработку нашего монитора. Как я уже упоминал, в MSVC входят заголовочные файлы, с описаниями структур и функций, необходимых для работы с NAL. Если подсоединить к проекту эти файлы и библиотеки (NAL.LIB и т. д.), окажется, что проект собрать не удается. Компоновщик не слинкует выбранные библиотеки! Выход очень простой. Файл NAL.H необходимо переписать, изменив вызовы функций на динамические, явно описав процесс их загрузки из библиотеки. Для этого я написал три класса (см. Листинг 1).

Шаг третий - работа с NAL

Для работы с NAL я написал класс Cnal (см. Листинг 2).

Методы этого класса называются так же, как и соответствующие функции библиотеки NAL.DLL.

Полный текст рассматриваемых здесь модулей можно получить с моего сайта в Internet по адресу: http://members.xoom.com/alex_ep/source/nalsrc.zip, а здесь я покажу реализацию лишь одного метода из класса CNal::EnumNetworks.

Опишем тип:

typedef DWORD (WINAPI
 *LPENUMNETWORKS)( VOID );

Тогда реализация метода будет выглядеть следующим образом:

DWORD CNal::EnumNetworks( void )
{
	LPENUMNETWORKS		lpEnumNetworks;
	DWORD		dwRes	= 0;
lpEnumNetworks = (LPENUMNETWORKS)GetProcAddress(
 m_hInstance,»EnumNetworks»);
if( lpEnumNetworks != NULL )
		dwRes = ((lpEnumNetworks)());
	return dwRes;
}

Аналогично реализуются и остальные методы класса, которые являются, по существу, вызовами соответствующих функций API.

Шаг четвертый - алгоритм работы NAL

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

Работа с интерфейсом состоит из трех основных этапов - инициализации, накапливания информации и статистики и получения содержимого фреймов.

Функции инициализации библиотеки (соответствующей, например, функции CoInitialize ) здесь нет.

Первая функция, необходимая при работе с NAL, - Enum Networks. Она возвращает количество сетевых адаптеров, установленных на компьютере. Каждому адаптеру соответствует некоторый сегмент сети, в который этот адаптер «смотрит». Хотя в приведенном примере можно работать лишь с одним адаптером, API позволяет одновременно получать информацию о любом количестве сетей. Рассмотрим запуск процесса трассировки более подробно. В коде примера это делается в следующем методе (см. Листинг 3).

Функция CreatePassword экспортируется из сервисной библиотеки BHSUPP.DLL. Если в настройках Network Analizer Agent, к которым можно получить доступ из Control Panel Windows NT, пароль не указан, то в качестве аргумента этой функции передается пустая строка. В случае, если пароль указан, NAL предложит ввести его в диалоговое окно.

Функции OpenNetwork, прототип которой приведен в описании класса CNal, передается несколько параметров.

DWORD NetworkID - порядковый номер, начиная с нуля, сети, информация о которой будет собираться.

HPASSWORD hPassword - параметр можно получить с помощью вызова функции CreatePassword.

NETWORKPROC lpNetworkProc - callback-функция, имеющая следующий прототип:

typedef DWORD (WINAPI
 *NETWORKPROC)(HNETWORK, DWORD, DWORD,
 LPVOID, LPVOID, LPVOID);
DWORD CALLBACK NetworkProc(HNETWORK
 hHandle, DWORD dwMessage, DWORD
 dwStatus, LPVOID lpUserContext, LPVOID
 lpParam1, LPVOID lpParam2).

LPVOID lpUserContext - параметр, передаваемый callback-функции.

LPSTATISTICSPARAM lpStatisticsParam - указатель на структуру, в которой будет аккумулироваться вся информация о сканируемой сети.

Если вызов функции прошел успешно, то будет возвращен идентификатор сети. В противном случае возвращается NULL, а код ошибки может быть получен вызовом функции BhGetLastError.

В структуре STATISTICSPARAM может храниться лишь информация об общей статистике сети. Для сохранения подробной информации о трафике необходимо зарезервировать место в памяти. Это осуществляется вызовом функции AllocNetworkBuffer, описанной выше. Ей передаются следующие параметры:

DWORD NetworkID - порядковый номер, начиная с нуля, сети, информация о которой будет собираться;

DWORD BufferSize - размер буфера, который необходимо выделить. Он должен быть кратным значению BUFFERSIZE, определенной в модуле NALTYPES.H. Я рекомендую использовать значения фактора не меньшие, чем 30.

Функция возвращает дескриптор памяти в случае успешного завершения и NULL - в противном случае. Код ошибки можно получить с помощью функции BhGetLastError().

Теперь все готово к запуску процесса трассировки. Это делается с помощью функции StartCapturing, которой в качестве параметров передаются идентификаторы сети и буфера, который был получен вызовом функции AllocNetworkBuffer.

NAL не предоставляет интерфейс для получения информации о текущем состоянии трафика. Поэтому в примере используется прерывание по таймеру. В его обработчике один раз в секунду приостанавливается трассировка и выводятся текущие значения статистики. Посмотрим, как это делается.

void CRTNetworkAnalizerDlg::OnTimer(UINT
 nIDEvent) 
{
	rt_StopTimer();
	m_nal.PauseCapturing( m_hNetwork );
	DisplayStatistics();
m_nal.ContinueCapturing();
	rt_StartTimer();
	CDialog::OnTimer(nIDEvent);
}

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

В заключение остается добавить, что аналогичные библиотеки несложно построить для любого языка программирования, поддерживающего вызовы функций из DLL, например Delphi или MS Visual Basic.

Важное замечание

Описанная в этой статье технология под Windows 2000 не работает. По крайней мере, с SP1.

ОБ АВТОРЕ

Александр ЭПШТЕЙН - разработчик коммерческого и свободно распространяемого ПО для Windows и UNIX. Начальник отдела разработки ПО компании «КиберПлат.КОМ». С ним можно связаться по адресу: alex_ep@hotmail.com.