| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
7.2 Сетевые драйверы Семенов Ю.А. (ГНЦ ИТЭФ) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Читатели, знакомые с телекоммуникационными протоколами, могут заинтересоваться тем, как писать прикладные программы для работы с пакетами. Прикладная программа взаимодействует с драйвером сетевого интерфейса. Ethernet-интерфейс, как и всякий другой, содержит несколько статусных управляющих регистров (CSR) и один или несколько регистров данных. Запись (чтение) в эти регистры выполняется в IBM/PC с помощью команд IN (OUT). Каждому регистру ставится в соответствие определенный номер порта. Блок номеров портов задается с помощью переключателей на интерфейсе или в процессе постановки пакетного драйвера. В последнем случае в AUTOEXEC-файле должна присутствовать строка, например (для DOS): NE2000 0x60 0x5 0x300, (если ваша ЭВМ снабжена интерфейсом типа NE2000). Здесь предполагается, что интерфейс будет использовать номер прерывания 0x60, аппаратное прерывание 0x5 и блок портов, начиная с шестнадцатеричного адреса 0x300 (io_addr). Следует иметь в виду, что разные интерфейсы могут иметь разное число CSR-регистров и отличные от приведенных ниже функции (NE2100). Интерфейс характеризуется тремя целыми числами: класс (8 бит), тип (16 бит) и номер. Класс говорит о том, для какой из сетевых сред предназначен данный прибор (PPP, DIX Ethernet, IEEE 802.3, IEEE 802.5, Pronet-10, Appletalk и т.д.). Тип описывает конкретную реализацию интерфейса (NE2000, NI5210, 3C501 и т.д.). Тип 0xffff соответствует всем интерфейсам данного класса. В случае, когда ЭВМ оснащена более чем одним интерфейсом идентичного типа, для их идентификации используется номер. На рис. 7.2.1 показана структура управляющего (CSR) регистра сетевого интерфейса. В таблице 7.2.1 приведен перечень основных классов с примерами определенных типов интерфейсов. Рис. 7.2.1.А Структура CSR-регистра интерфейса (CSR0)
CSR1 (доступ разрешен при CSR0[stop] = 1) Рис. 7.2.1.Б. CSR1 CSR2 (доступ разрешен при CSR0[stop] = 1) Рис. 7.2.1В. CSR2
csr3 (доступ разрешен при CSR0[stop] = 1) Рис. 7.2.1.Г. CSR3 Таблица 7.2.1.
Любой пакетный драйвер имеет блок исходных данных (MS-DOS), напр.:
Структура переменных init_mode (смещение = 0) имеет вид Рис. 7.2.2. Структура переменных init_mode
Кольцевой входной буфер имеет следующую структуру:
Структура переменных rd_stat имеет вид Рис. 7.2.3. Структура переменных rd_stat
Выходной буфер имеет сходную структуру. Я не буду описывать здесь то, как следует писать системные драйверы (Исчерпывающую информацию по написанию таких драйверов читатель может найти в книге "Написание драйверов для MS-DOS" Р.Лея и "Уэйт Груп", Москва "Мир", 1995), тем более что существует достаточное их количество в депозитариях общего доступа (Например, анонимное FTP по адресам ftp.funet.fi, ftp.switch.ch или oak.oakland.edu, депозитарий SimTel ). Приведенное выше описание регистров интерфейса не является единственно возможным (см. также руководство по сетевому контроллеру 8390 и файл NE2.ASM из ссылки ftp.funet.fi. Структура драйверов варьируется для разных операционных систем. Для системных программистов полезно иметь возможность настраивать драйвер или непосредственно интерфейс на определенный режим, например, на прием всех пакетов, проходящих по кабельному сегменту. Последнее может представлять интерес в диагностических целях, так как вслед за пакетным драйвером загружается Etherdrv, Winsock или winpkt и т.д., блокирующие режим приема всех пакетов (mode=6). Ниже приведен пример описания основных параметров драйвера:
; размер буфера для мультикаст-адресов
Работа с пакетным драйвером в MS-DOS Существует множество пакетных драйверов. Можно обнаружить несколько модификаций для одного и того же типа интерфейса. Эти драйверы могут быть ориентированы на работу в разных программных средах (Novell, UNIX, MS-DOS и т.д.) и иметь разные возможности. Для MS-DOS сложился неофициальный стандарт, который позволяет использовать драйвер для самых разных приложений. Драйвер может использовать минимум возможностей интерфейса (базовый уровень), реализовать более широкий набор функций (мультикастинг, сбор статистики и т.д.) или поддерживать практически все, на что способен данный прибор. В последнем случае он занимает больше места в памяти. Описания операций с пакетными драйверами, приведенные ниже, выполнены в нотации ассемблера IBM/PC. При написании программы следует помнить, что порядок байтов в Ethernet противоположен тому, который используется в вашей IBM/PC. Пакетные драйверы используют программные прерывания в интервале 0x60 - 0x80. Следует сразу заметить, что не все прерывания из этого списка свободны и при конфигурировании системы следует проявлять осмотрительность. Для того чтобы избежать конфликтов с другими внешними устройствами, предусматривается возможность реконфигурации прерываний. Предполагается, что программа обработки прерываний начинается с команды безусловной передачи управления (JMP), за которой следует текстовая строка "PKT DRVR". Именно эта строка служит указателем при поиске адреса пакетного прерывания. Практически все драйверы могут работать с различными протоколами (TCP/IP, OSI и др.). Решить задачу мультиплексирования на связном уровне помогает процедура access_type, которая обеспечивает доступ для пакетов определенного типа. Все функции реализуются с помощью обращения к драйверу с набором определенных параметров. При этом значение регистра AH определяет тип запроса. Каждому типу используемого сетевого протокола, с которым работает интерфейс, ставится в соответствие целочисленный указатель (handle), получаемый с помощью процедуры access_type. Выполнимость драйвером тех или иных операций может быть выяснена с помощью запроса driver_info. При работе с драйвером следует проявлять осторожность и спасать нужные вам регистры. Следует также помнить, что порядок байтов в PC и в некоторых сетях, включая Ethernet, не совпадает. Описание основных запросов, посылаемых пакетному драйверу: 1. Получение информации о типе и функциональных возможностях драйвера driver_info AH == 1,
Целочисленный указатель (handle) должен быть занесен в регистр BX (для старых драйверов). В случае ошибки устанавливается флаг carry, а код ошибки заносится в регистр DH. Сообщение BAD_HANDLE (неверный указатель) возможно только для старых драйверов. При благополучном исполнении флаг carry равен нулю, а в регистры будет занесены следующие параметры:
Ниже приведен пример программы, реализующей некоторые из описанных запросов.
.DATA
.CODE
HAVE_SIGNATURE:
2. Организация доступа для пакетов данного
типа Запрос access_type инициализирует доступ для пакетов определенного типа (type). Аргумент typelen – длина спецификации типа в байтах, для PC/TCP равна 5 (наименьшее значение - 2, для IP и ARP). Аргумент receiver является указателем на подпрограмму, которая вызывается при приеме пакета. Получая пакет, драйвер дважды обращается к этой программе. Первый раз (при AX==0) это делается с целью получения адреса буфера, куда должен быть положен пакет. Прикладная программа в этом случае должна выдать указатель буфера в регистры ES:DI. Если прикладной процесс не имеет свободного буфера,то возвращается значение 0:0. Пакет выбрасывается и повторное обращение к программе receiver отменяется. Форма реализации запроса аналогична приведенному для driver_info:
В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможные ошибки:
При успешном выполнении запроса флаг carry=0, а в регистр AX занесен указатель (handle). Обращение к приемнику (receiver): (*receiver)(handle, flag, len [, buffer])
if AX == 1,
Если параметр typelen равен нулю, прикладной процесс готов получать все пакеты. Очень важно, чтобы при первом обращении к receiver (AX==0) CX (длина пакета) была указана правильно, что позволит выделить нужное место в памяти. CX должна включать в себя длину MAC-заголовка и размер самого сообщения без контрольной суммы (CRC). Повторный вызов (AX==1) программы receiver указывает на то,что пакет записан в буфер и прикладная программа может с ним работать. Адрес буфера будет указан в регистрах DS:SI. 3. Завершение доступа пакетов данного типа release_type int release_type(handle) AH == 3; _release_type proc near
В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможная ошибка: BAD_HANDLE (не верный указатель). При успешном выполнении запроса флаг carry=0. Эта операция прерывает доступ пакетов, соответствующих указателю, полученному с помощью запроса access_type. Старый указатель после выполнения этого запроса не действителен. 4. Процедура посылки пакета send_packet(buffer, length) AH == 4 (код запроса) В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки 12 CANT_SEND. send_packet отправляет пакет с числом байт, равным CX. Пакет должен в исходный момент лежать, начиная с адреса DS:SI. Прикладная программа должна сформировать все необходимые заголовки. Информация, нужная для осуществления демультиплексирования пакетов (MAC или LLC), также должна быть записана в пакет, так как при этом запросе не сообщается значение указателя (handle). 5. Завершение работы драйвера terminate(handle) AH == 5 (код запроса) В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможные ошибки: 1 BAD_HANDLE; Завершает работу драйвера, соответствующего указателю, который приведен в качестве параметра запроса. Если возможно, драйвер будет выгружен и занимаемая им память освобождена. 6. Получение физического адреса интерфейса get_address(handle,buf,len) AH == 6 (код запроса) В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможные ошибки: 1 BAD_HANDLE; Копирует текущее значение сетевого (физического) адреса интерфейса в буфер. Если получено сообщение NO_SPACE, это означает, что выделенного места (len=CX) для копирования адреса не хватило. 7. Возвращение интерфейса в исходное состояние reset_interface(handle) AH == 7 (код запроса) В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможные ошибки: 1 BAD_HANDLE; Возвращает интерфейс в исходное состояние, прерывая все процессы. Местное значение физического сетевого адреса, если оно было изменено, восстанавливается из ROM, прием переключается в режим 3, а список мультикастинг-адресов обнуляется. При работе с несколькими указателями (handle) возможны серьезные неприятности, по этому выполнение запроса блокируется и присылается сообщение CANT_RESET. 8. Запрос установки режима приема пакетов set_rcv_mode(handle,mode) AH == 20 (код запроса) int handle; В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможные ошибки: 1 BAD_HANDLE; Устанавливает режим приема пакетов. Режим 3 используется по умолчанию. Возможны (но не для всех интерфейсов) следующие режимы:
9. Считывание действующего режима приема пакетов get_rcv_mode(handle) AH == 21 (код запроса) int handle; BX (входной параметр - указатель) В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки 1 BAD_HANDLE. При успешном выполнении запроса флаг carry=0, а в регистр AX заносится код режима приема пакетов. 10. Занесение списка мультикастинг-адресов в интерфейс set_multicast_list(addrlst,len) AH == 22 (код запроса) В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможные ошибки: 6 NO_MULTICAST; Список адресов представляет собой счетную последовательность, начинающуюся с байта числа адресов в списке. На список адресов указывает комбинация регистров ES:DI. Сообщение NO_SPACE присылается, если указатель адреса отсутствует, или число адресов превосходит аппаратные возможности интерфейса. Прежде чем заносить список, полезно сначала ознакомиться с имеющимся уже списком, выполнив запрос get_multicast_list. При получении сообщения NO_SPACE рекомендуется попытаться установить режим приема 3 с помощью запроса set_rcv_mode. 11. Получение рабочего списка мультикастинг-адресов get_multicast_list AH == 23 (код запроса) В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможные ошибки: 6 NO_MULTICAST; При успешном выпонении запроса флаг carry=0, в регистр CX заносится длина списка адресов, а регистры ES:DI указывают на начало счетной оследовательности, где запрошенный список лежит. Прикладная программа не должна модифицировать этот список. 12. Получение статистических данных об ошибках и трафике через данный интерфейсget_statistics(handle) AH == 24 (код запроса) В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки 1 BAD_HANDLE. При успешном выполнении запроса флаг carry=0, а в массиве, начиная с адреса DS:SI, лежит запрошенная информация. struct statistics {
Статистические данные имеют вид целых 32-разрядных чисел в формате IBM/PC. 13. Смена физического адреса интерфейса set_address(addr, len) AH == 25 В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки. Возможные ошибки: 13 CANT_SET; При благоприятном выполнении запроса флаг carry=0, а значение регистра CX сохраняется. Запрос используется в случае, когда необходим специфический физический адрес интерфейса (например, в случае DECNET). При наличии более одного указателя (handle) драйвер откажется исполнить данный запрос и пришлет сообщение CANT_SET. Этим не исчерпывается перечень возможных запросов, существует некоторое количество операций, относящихся к экстра-набору функций (код функциональности 5 или 6, смотри описание запроса driver_info). | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Previous:
7.1 Winsock (для UNIX, Windows-95 и -NT)
UP:
7 Программирование для сетей (новые идеи, принципы и возможности)
|