Протокол PPP (Point to Point Protocol — протокол «точка—точка») является в настоящее время основным для обмена данными между клиентом и сервером Internet при доступе по коммутируемым линиям. Хотя поддержка PPP встроена во все основные ОС, он представляет несомненный интерес для программиста.

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

Как следует из его названия, PPP предназначен для передачи данных в соединениях типа «точка—точка». Это канальный протокол, допускающий использование с различными сетевыми протоколами. Конечно, чаще всего в роли сетевого протокола выступает TCP/IP, но возможны и другие варианты, например IPX или NetBios.

Протокол PPP считается (и является) весьма сложным. Это связано с тем, что по существу он представляет собой не один протокол, а стек протоколов, имеющий несколько уровней.

Первый, нижний, уровень, общий для всех типов передаваемых данных, отвечает за управление соединением. Он определяет такие характеристики канала, как сжатие и кодирование данных; впрочем, чтобы программисту «жизнь медом не казалась», на этом уровне задаются и некоторые параметры, имеющие к нему лишь косвенное отношение.

Второй уровень отвечает за управление собственно передаваемыми данными, и список параметров конфигурации для него зависит от характера этих данных, т. е. от сетевого протокола, канал для которого обеспечивает PPP. Например, при установлении Internet-соединения стороны сообщают друг другу свои IP-адреса, а когда должны передаваться данные протокола IPX или NetBios, IP-адреса, естественно, не нужны.

Рис. 1. Общая структура PPP-соединения

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

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

И наконец, программисту, реализующему работу с PPP, необходимо иметь в виду уровень управления физическим устройством, который не входит в стандарт, но в ряде случаев играет существенную роль. Таким образом, общая структура PPP-соединения имеет четыре уровня, как показано на рис. 1.

Передача и прием данных

Обмен данными по протоколу PPP осуществляется путем передачи пакетов. Длина их ограничена сверху, т. е. не может превышать некоторой заданной величины. Поскольку фактическая длина пакета данных заранее неизвестна, его начало и конец маркируются специальными флагами — байтами с шестнадцатеричным кодом 0x7E.

Непосредственно за начальным флагом стандарт требует помещать еще два байта с фиксированным значением: поле адреса (0xFF) и поле управления (0x03). Однако для повышения пропускной способности соединения эти поля могут опускаться, так что при анализе пакета здесь необходима осторожность.

Далее следует идентификатор протокола длиной в один или два байта, а за ним — собственно передаваемые данные. Замыкают пакет 16-битовая контрольная сумма CRC16, называемая также FCS (Frame Check Sequence — контрольная сумма кадра), и завершающий флаг (рис. 2).

Значение FCS рассчитывается для всех байтов пакета за исключением начального и завершающего флагов и двух байтов, содержащих саму контрольную сумму. Обратите внимание на то, что при передаче по протоколу PPP для FCS (как и для других числовых данных) используется так называемый сетевой порядок байтов, обратный принятому в IBM PC.

При передаче встретившиеся внутри пакета символы 0x7E, а также 0х7D и (по умолчанию) все символы с кодами меньше 0x20 заменяются ESC-последовательностями, состоящими из символа 0x7D и кодируемого символа с инвертированным шестым битом (операция исключающего «или» с числом 0х20). Контрольная сумма пакета рассчитывается до кодирования.

Вот и все, что необходимо знать для создания минимального набора функций, обеспечивающих простейший обмен данными по протоколу PPP. Соответствующая программа приведена в листинге; поскольку целесообразно было использовать объектно-ориентированный подход, она написана на языке Си++. Программа рассчитана на работу под управлением ОС MS Windows, однако ее перенос в другие системы не представляет особой трудности.

Итак, сам по себе обмен данными по протоколу PPP довольно прост. Так где же начинаются пресловутые сложности? В самом начале — при установлении соединения.

Установление PPP-соединения с точки зрения программиста

Особенностью PPP по сравнению с более старым протоколом SLIP является то, что все основные параметры канала конфигурируются на этапе установления соединения.

Конфигурирование заключается в обмене специальными пакетами, содержащими значения параметров. Каждый пакет имеет код, указывающий на его функцию (тип), и идентификатор (рис. 3). Полный перечень типов пакетов приведен в табл. 1. Собственно конфигурационными из них являются первые четыре: CONFIGURE_REQUEST, CONFIGURE_ACK, CONFIGURE_NAK и CONFIGURE_REJECT.

Рис. 3. Формат поля данных пакета конфигурации

Хотя согласуемые параметры на каждом уровне, естественно, свои, формат их представления в конфигурационных пакетах всегда один и тот же: описание каждого параметра (или группы взаимосвязанных параметров) состоит из двух однобайтовых полей — кода и длины, за которыми следует поле значения (значений) переменной длины (рис. 4). Число, заносимое в поле длины, соответствует суммарному размеру полей описания параметра, поэтому поле значений может занимать от 0 до 253 (255-2) байт. Его содержимое целиком зависит от параметра.

Рис. 4. Формат описания параметра в пакете конфигурации

Процесс согласования параметров на первом уровне начинается с того, что стороны посылают друг другу по пакету CONFIGURE_REQUEST, имеющему идентификатор 1 и содержащему некоторый набор значений. В ответ на пришедший пакет CONFIGURE_REQUEST должен быть послан пакет с тем же идентификатором, содержащий либо подтверждение полученных значений (CONFIGURE_ACK), либо запрос на изменение каких-то из них (CONFIGURE_NAK), либо категорический отказ от обсуждения определенных параметров (CONFIGURE_REJECT).

Получив пакет CONFIGURE_NAK или CONFIGURE_REJECT, сторона генерирует следующий пакет CONFIGURE_REQUEST, идентификатор которого на 1 больше, чем у предыдущего отправленного пакета CONFIGURE_REQUEST, и так продолжается до тех пор, пока обе стороны не отправят (и не получат) по пакету CONFIGURE_ACK с идентификатором, соответствующим последнему полученному (отправленному) пакету CONFIGURE_REQUEST. Если ответ на посланный пакет задерживается (время ожидания, рекомендуемое стандартом PPP, составляет 3 с), сторона повторяет его с идентификатором, увеличенным на 1.

Затем аналогичная процедура (но уже с другими параметрами) выполняется для следующих уровней конфигурирования. Общее число уровней — три: уровень управления соединением (протокол LCP — Link Control Protocol), уровень авторизации и уровень конфигурирования протокола передачи данных: в случае соединения с Internet это уровень конфигурирования IP-соединения (протокол IPCP).

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

Общие принципы использования конфигурационных пакетов, не зависящие от уровня, таковы.

  • Пакет CONFIGURE_REQUEST должен содержать полный набор параметров, включая и те, которые получают значения, определенные стандартом протокола как значения по умолчанию.
  • В ответ на один пакет CONFIGURE_REQUEST может быть послан только один ответный пакет.
  • Каждый посылаемый стороной пакет CONFIGURE_REQUEST получает свой идентификатор. Пакету ответа присваивается идентификатор соответствующего запроса.
  • Пакет CONFIGURE_ACK посылается, если для стороны приемлемы все перечисленные в запросе параметры. Его данные представляют собой точную копию данных пакета запроса.
  • В пакет с отрицательным ответом (CONFIGURE_NAK, CONFIGURE_REJECT) включаются только те параметры, которые неприемлемы для стороны.
  • Пакет CONFIGURE_NAK содержит приемлемые для стороны параметры, заменяющие предложенные в запросе.
  • Параметры считаются согласованными тогда и только тогда, когда каждая из сторон получит в ответ на свой запрос пакет CONFIGURE_ACK.

Таким образом, получив запрос на установку определенных параметров, сторона не может подтвердить одни параметры, порекомендовать изменить значения других и отклонить третьи, а должна сделать что-то одно. Что же именно? Поскольку получение пакета CONFIGURE_NAK приведет к генерации нового пакета CONFIGURE_REQUEST с измененными параметрами, целесообразно будет сперва исключить те из них, которые в принципе неприемлемы. Иными словами, при разборе присланных в запросе параметров наивысший приоритет следует отдать ответу CONFIGURE_REJECT, следующий — ответу CONFIGURE_NAK и, наконец, последний — ответу CONFIGURE_ACK.

Процедура конфигурирования соединения схематично показана на рис. 5 (для простоты предполагается, что параметры, переданные в последнем конфигурационном запросе, полностью удовлетворяют вторую сторону и она отвечает пакетом CONFIGURE_ACK).

Конфигурирование управления соединением

В качестве примера рассмотрим конфигурирование самого нижнего уровня, осуществляемое в соответствии с протоколом LCP. Двухбайтовый идентификатор протокола, передаваемый в составе PPP-пакета, для LCP равен 0xC021.

У LCP есть несколько особенностей, отличающих его от других протоколов, используемых с PPP. Во-первых, LCP-пакеты всегда передаются без сжатия. Во-вторых, они должны обрабатываться в любой фазе соединения, в том числе и тогда, когда уровень LCP успешно сконфигурирован и начался собственно обмен данными. В-третьих, получение LCP-пакета TERMINATE_REQUEST служит основанием для разрыва соединения. И наконец, в-четвертых, в LCP-пакетах может передаваться информация об ошибках в других протоколах.

Параметры конфигурации протокола LCP перечислены в табл. 2. Рассмотрим их более подробно.

Максимальная длина принимаемого блока. Этот параметр определяет максимальную длину блока данных, который сторона может принять. По умолчанию ее значение равно 1500 байт, однако некоторые провайдеры используют реализацию PPP для ОС FreeBSD, где стандартное значение — 1520 байт. Увеличение принятой по умолчанию максимальной длины блока обычно не допускается. Она может быть только уменьшена, но в случае Internet-соединения нет причин как бы то ни было ее менять.

Поскольку задается длина не всего PPP-пакета, а лишь области данных, для вычисления необходимого размера буфера следует прибавить к значению по умолчанию число 8 (суммарная длина полей адреса, управления, контрольной суммы, начального и завершающего флагов). Получившаяся величина с некоторым округлением в большую сторону и дала значение 1530 в заголовке класса в листинге.

Кодирование символов. Поскольку многие модемы используют символы с кодами меньше 0x20 в качестве управляющих, стандарт PPP, как уже упоминалось, рекомендует кодировать их в виде ESC-последовательностей. Но поскольку кодирование снижает пропускную способность линии (передается два байта вместо одного), стороны могут договориться не кодировать те символы, которые будут восприняты правильно. Для этого используется 32-битовая маска, содержащаяся в поле значений. Младший ее бит соответствует символу с кодом 0, старший — символу с кодом 31 (0x1F); установка бита в 1 указывает, что он будет кодироваться, установка в 0 — что нет. Значение параметра по умолчанию — 0xFFFFFFFF, но большинство провайдеров в качестве значения маски посылает 0.

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

Протокол авторизации доступа. Этот параметр позволяет договориться об используемом протоколе авторизации доступа. Авторизация в PPP довольно сложна, и мы не будем ее здесь рассматривать. Заметим, что при соединении с Internet-провайдером соответствующую процедуру, как правило, можно обойти, введя имя и пароль пользователя в окне терминала до начала PPP-сеанса. Тогда пакет CONFIGURE_REQUEST протокола LCP, который вы получите от провайдера, не будет содержать данного параметра.

«Магическое число». Основное назначение данного параметра — проверка наличия реального физического соединения между двумя разными машинами. «Магическое число», помещаемое в поле значений, представляет собой четырехбайтовую последовательность, полученную с помощью генератора случайных чисел. Стороны обмениваются «магическими числами», и если они совпадают, то это, скорее всего, означает, что физическое соединение отсутствует, а компьютер обменивается пакетами сам с собой. Такая ситуация по-английски называется loop-back; стандарт PPP ее никак не регламентирует.

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

Протокол управления качеством линии. Значением этого параметра, вообще говоря, является двухбайтовый идентификатор протокола управления качеством линии. Однако при конфигурировании медленных соединений (к которым относятся и модемные) он не используется, поскольку управление качеством линии в таких соединениях не применяется.

Сжатие поля протокола. При разрешенном сжатии поле протокола PPP-пакетов будет иметь длину в один байт вместо двух. Сжатие запрещено стандартом для протокола LCP и, как правило, не применяется для конфигурационных протоколов, таких, как протокол авторизации или протокол конфигурирования IP-соединения. Для IP-пакетов значение однобайтового поля протокола равно 0x21. Параметр асимметричен; его поле значения имеет нулевую длину: он просто присутствует в конфигурационном пакете, если сжатие должно быть разрешено, и отсутствует в противном случае.

Сжатие полей адреса и управления. По формату и ограничениям на использование этот параметр аналогичен предыдущему. Когда он согласован, стороны не передают в составе PPP-пакетов поля адреса и управления. Настоятельно рекомендуется использовать сжатие полей адреса и управления совместно со сжатием поля протокола.

Согласовав все параметры LCP, можно переходить к следующим фазам установления соединения — авторизации и конфигурированию IP-канала.

Литература

  1. Джамса К., Коуп К. Программирование для Интернет в среде Windows: Пер. с англ. — СПб.: Питер, 1996.
  2. Фейт С. TCP/IP: Пер. с англ. — М.: Лори, 2000.
  3. RFC 1661, 1662 (эти и другие RFC-документы можно найти по адресу http://www.cis.ohio-state.edu/htbin/rfc/).

Об авторе

Леонид Садофьев — сотрудник фирмы SoftDev Spb. (С.-Петербург) и автор сайта IntReal, www.intreal.spb.ru; e-mail: webmaster@intreal.spb.ru, LSadofiev@softdev.spb.ru