Институт Системного Программирования РАН

Работа FTP-ALG

Г.В. Ключников


Последняя модификация: 14/01/2003

Аннотация

Данная статья описывает принципы работы протокола FTP и шлюза прикладного уровня FTP-ALG (Application Level Gateway) в контексте использования NAT-PT (Network Address Translation and Protocol Translation [RFC 2766]).

FTP-ALG

Для обеспечения прозрачности на прикладном уровне по протоколу FTP требуется FTP-ALG.

1. Работа FTP

Команды протокола FTP содержат информацию об IP адресе и номере порта в командах на установление соединения данных. [RFC 959] "File Transfer Protocol" определят две такие команды: PORT и PASV. [RFC 2428] "FTP Extensions for IPv6 and NATs" фактически заменяет их на команды EPRT и EPSV. Протокол FTP использует два TCP соединения: 1) командное соединение (control connection) для передачи FTP команд, 2) соединение для передачи данных (data connection). Командное соединение всегда инициируется клиентом на TCP порт 21 сервера. Соединение данных устанавливается всякий раз, когда необходимо передать данные и закрывается по окончанию передачи данных.

Для установления соединения данных определены три различных способа:

1) Обычный режим

Клиент передает серверу адрес и порт (в команде PORT или EPRT), которые будут использоваться сервером для установления соединения с клиентом для передачи/приема данных. Соединение инициализируется сервером. Если данные передает сервер (get), то соединение закрывает сервер, если данные передает клиент (put), то это соединение закрывает клиент по концу файла. Порт клиента выбирается произвольный (>1024), а у сервера - всегда порт 20.

Формат команды PORT [RFC 959, section 3.3, 4.1.2], [RFC 1123, section 4.1.2.5]:

PORT h1,h2,h3,h4,p1,p2
где h1,h2,h3,h4	- ASCII представление десятичных цифр IPv4 адреса
    p1,p2 - ASCII представление двух десятичных цифр, из которых вычисляется
            номер порта по формуле (p1*256 + p2)
Например:
Клиент: PORT 195,208,32,215,192,89
Сервер: 200 PORT command successful.
Сервер должен установить соединение данных с хостом 195.208.32.215 
на TCP порт 49241 (192*256 + 89).
Формат команды EPRT [RFC 2428, section 2]
EPRT<space><d><net-prt><d><net-addr><d><tcp-port><d>

<d> - символ-разделитель, рекомендуется символ"|" (ASCII 124)
<net-prt> - должен быть номер адресной фамилии AF (address family number), 
определенный IANA [RFC 1700]; этот номер определяет используемый протокол.

AF Number 	Protocol
--------------	---------------------------------
1		Internet Protocol, Version 4
2		Internet Protocol, Version 6

<net-prt> - протокольно-зависимое ASCII представление сетевого адреса, 
для IPv4 и IPv6 (AF номера 1 и 2) адреса должны быть следующих форматов:

AF Number	Address Format			Example
---------	---------------------------	-----------------------------
1		dotted decimal			132.235.1.2
2		IPv6 string representations	1080::8:800:200C:417A

<tcp-port> - ASCII представление TCP порта
Например:
Клиент: EPRT |1|132.235.1.2|6275|
Сервер: 200 EPRT command successful.
Сервер должен использовать IPv4 протокол для открытия соединения данных 
с хостом 132.235.1.2 на TCP порт 6275.

Клиент: EPRT |2|1080::8:800:200C:417A|5282|
Сервер: 200 EPRT command successful.
Сервер должен использовать IPv6 протокол для открытия соединения данных 
с хостом 1080::8:800:200C:417A на TCP порт 5282.
При получении правильной (valid) PORT/EPRT команды сервер должен возвратить ответ с кодом 200 (Command OK). Стандартные коды ошибки 500 и 501 указывают на синтаксические ошибки. Коды ответа определяет [RFC 959, section 4.2].Кроме того, вводится новый код ошибки 522 [RFC 2428, section 2] для команд EPRT и EPSV, который указывает, что запрошенный протокол не поддерживается сервером.

2) Пассивный режим

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

Формат команды PASV [RFC 959, section 3.3, 4.1.2], [RFC 1123, section 4.1.2.6]:

клиент: PASV
сервер: 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)

где 	h1,h2,h3,h4	- ASCII представление десятичных цифр IPv4 адреса
p1,p2 -	- ASCII представление двух десятичных цифр, из которых вычисляется 
номер порта по формуле (p1*256 + p2)

Например:

Клиент: PASV
Сервер: 227 Entering Passive Mode (195,208,32,72,212,69)
Формат команды EPSV [RFC 2428, section 3]:

По команде EPSV от клиента сервер отвечает только номером порта. Код ответа на правильный запрос должен быть 229. Стандартные коды ответа 500 и 501 используются для индикации синтаксической ошибки.

клиент: EPSV
сервер: 229 <text indicating server is entering extended passive mode> \
(<d><d><d><tcp-port><d>)

<d> - символ-разделитель, рекомендуется символ"|" (ASCII 124)
<tcp-port> - ASCII представление номера TCP порта

Например:
Клиент: EPSV
Сервер: 229 Entering Extended Passive Mode (|||6446|)
Когда команда EPSV используется без аргументов, сервер будет выбирать протокол для соединения данных, основываясь на протоколе, который использует управляющее соединение. Если сервер возвращает протокол, который не поддерживается клиентом, клиент должен послать ABOR команду для закрытия слушающего сокета на сервере.

Клиент может послать EPSV команду, в которой явно указывается тип протокола, следующего формата:

EPS<space><net-prt>
Если указанный протокол поддерживается сервером, то он должен использовать этот протокол. Если нет, сервер должен вернуть сообщение об ошибке с кодом 522.

Команда EPSV может использоваться с аргументом "ALL" для информирования NATs (Network Address Translators), что EPRT команда (как и все остальные команды данных, т.е. PORT и PASV) больше использоваться не будут. Пример этой команды:

EPSV<space>ALL
После приема команды EPSV ALL, сервер должен отбрасывать все команды на установление соединения данных кроме EPSV (т.е. EPRT, PORT, PASV и т.д.).

[RFC 2428, section 4] описывает использование команды EPSV. В частности определяет, что если управляющее соединение и все соединения данных, устанавливаются между теми же двумя машинами, то в этом случае ДОЛЖНА использоваться команда EPSV. И по поводу EPSV ALL: "When a client only expects to do two-way FTP transfers, it SHOULD issue this command (EPSV ALL) as soon as possible".

3) Использование порта клиента по умолчанию

Клиент может не посылать команду PORT (или EPRT) для указания параметров соединения данных. В этом случае сервер использует для соединения данных тот же порт клиента, что уже используется для управляющего соединения. Единственная неприятность здесь в том, что после закрытия каждого соединения сокет на сервере будет находиться в состоянии TIME_WAIT в течении 2MSL, и TCP не сможет разрешить следующее соединение в течении этого времени, так как в этом случае все параметры сокета идентичны для следующего соединения данных, которое затребует клиент, и поэтому опция сокета SO_REUSEADDR не работает. Если клиент повторяет запрос на данные, сервер сможет ответить только по истечению таймаута в 2MSL.

2. Работа FTP-ALG

Все команды FTP передаются по управляющему соединению. Так как для корректной работы FTP протокола необходимо транслировать только управляющие команды PORT, PASV, EPRT, EPSV, FTP-ALG должен обрабатывать только управляющее соединение FTP. Соединения данных остаются без изменения (т.е. FTP-ALG их не затрагивает). Управляющее соединение инициируется клиентом на TCP порт 21 сервера. Поэтому все приходящие TCP пакеты, у которых порт отправителя или получателя равен 21, направляются в модуль FTP-ALG. Модуль FTP-ALG работает с собственным списком записей типа FTP (структура записи описана в разделе 4).

Различаются два типа сеансов FTP: 1) порождаемых IPv4 стороной, 2) порождаемых IPv6 стороной.

2.1. Соединения, порождаемые IPv4 клиентом

Клиент IPv4 может порождать команды PORT и PASV, и, если он реализует [RFC 2428], команды EPRT и EPSV. Команды PORT, PASV транслируются в команды EPRT и EPSV соответственно, а так же ответ EPSV транслируется в ответ PASV перед отправкой IPv4 клиенту.

Трансляция команды PORT

Команда PORT h1,h2,h3,h4,p1,p2 
транслируется в 
EPRT<space><d><net-prt><d><net-addr><d><tcp-port><d>
Поле <net-prt> устанавливается равным 2 (AF_INET6), IPv4 адрес h1,h2,h3,h4 транслируется в IPv6 <net-addr> в ASCII представлении, IPv4 TCP порт p1,p2 в десятичное значение IPv6 порта <tcp-port> в ASCII представлении.

Примечание. Кроме того, возможно, будет необходимо транслировать и ответ EPRT в ответ PORT, так символьная стока ответа может включать описание команды которая выполнилась или не выполнилась успешно.

Например,

Клиент: PORT 195,208,32,215,192,89
После трансляции: EPRT |2|1080::8:800:200C:417A|5282|
Ответ: 200 EPRT command successful.
После трансляции: 200 PORT command successful.
Трансляция команды PASV
Команда PASV транслируется в 
EPSV<space><net-prt>
Поле <net-prt> устанавливается равным 2 (AF_INET6).

Ответ EPSV:
229 <text indicating server is entering extended passive mode> \
(<d><d><d><tcp-port><d>)
транслируется в ответ PASV:
227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
Так как в ответе EPSV не присутствует IPv6 адрес, то он получается из поля адреса отправителя IPv6 пакета и транслируется в адрес IPv4 адрес h1,h2,h3,h4. Этот адрес может быть получен из соответствующего поля записи FTP. Поле IPv6 порта <tcp-port> из ответа EPSV транслируется в IPv4 порт p1,p2. Если используется NAPT, необходима предварительная трансляция порта в соответствующее ему IPv4 значение перед переводом в p1,p2.

Если клиент использует команды EPRT и EPSV, то FTP-ALG будет транслировать только параметры этих команд.

Трансляция команды EPRT

Поле <net-prt> транслируется из 1 (AF_INET) в 2 (AF_INET6), поле IPv4 адреса <net-addr> - в соответствующий ему IPv6 адрес <net-addr> в ASCII представлении, поле порта <tcp-port> не транслируется.

Трансляция команды EPSV

В команде EPSV транслируется поле <net-prt> из 1 (AF_INET) в 2 (AF_INET6) В ответе EPSV поле <tcp-port> транслируется только, если используется порт трансляция NAPT. Примечание. При трансляции команд PASV и EPSV со стороны IPv4 клиентов необходимо запомнить, какая именно команда была транслирована, чтобы корректно произвести трансляцию ответа. Если производиться трансляция ответа от команд PORT и EPRT, то для них также необходимо знать какая была команда запроса. 2.2. Соединения, порождаемые IPv6 клиентом

Возможны два подхода:

1) транслировать v6 команды EPRT, EPSV в v4 команды EPRT, EPSV, и v4 ответы EPSV в v6 ответы EPSV;
2) транслировать v6 команды EPRT и EPSV в команды PORT и PASV, и ответы PASV в ответы EPSV.

В первом случае транслируются параметры команд <net-prt>, <net-addr>, <tcp-port> из v6 значений в соответствующие им v4 значения. При этом v4 сервер должен поддерживать команды EPRT и EPSV [RFC 2428], иначе v4 FTP сервер будет возвращать ошибку типа: "500 'EPSV': command not understood" и v6 FTP клиент не сможет работать.

Во втором случае будет невозможно транслировать команду "EPSV<space>ALL", по которой клиент извещает сервер, что в дальнейшей работе он будет использовать только команду EPSV, при этом сервер должен отбрасывать все другие команды данных PORT, PASV и EPRT. Эту ситуацию можно обойти, просто отвечая v6 клиенту на команду "EPSV<space>ALL" сообщением об успешном выполнении команды от имени сервера, но при этом реально никакой команды серверу не посылать. После этого клиент не должен посылать другие команды кроме EPSV, которые мы уже можем транслировать. Кроме того, можно зафиксировать такое состояние и от имени сервера отбрасывать все другие команды.

В принципе можно реализовать оба способа и регулировать включение того или другого в параметрах конфигурации NAT-PT

Первый подход

Трансляция команды EPRT

Поле <net-prt> транслируется из 2 (AF_INET6) в 1 (AF_INET), поле IPv6 адреса <net-addr> - в соответствующий ему IPv4 адрес <net-addr> в ASCII представлении, поле v6 порта <tcp-port> - в соответствующее ему поле v4 порта. Ответ не транслируется.

Трансляция команды EPSV

В команде EPSV транслируется поле <net-prt> из 2 (AF_INET6) в 1 (AF_INET), если оно присутствует. В ответе EPSV поле <tcp-port> транслируется только, если используется порт трансляция NAPT.

Второй подход

Трансляция команды EPRT

Команда EPRT<space><d><net-prt><d><net-addr><d><tcp-port><d> транслируется в
PORT h1,h2,h3,h4,p1,p2
IPv6 <net-addr> в ASCII представлении транслируется в IPv4 адрес h1,h2,h3,h4, десятичное значение IPv6 порта <tcp-port> в ASCII представлении транслируется в IPv4 значение порта p1,p2.

Примечание. Кроме того, необходимо транслировать и ответ PORT в ответ EPRT, так символьная стока ответа может включать название команды которая выполнилась или не выполнилась успешно.

Трансляция команды EPSV

Команда EPSV<space><net-prt> транслируется в PASV.
Ответ PASV: 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
транслируется в ответ EPSV:
229 <text indicating server is entering extended passive mode> \
(<d><d><d><tcp-port><d>)

Поле IPv4 порта p1,p2 из ответа PASV транслируется в поле IPv6 порта <tcp-port>. Изменения значения порта не производится (нет NAPT).

Примечание. Во всех случаях нет необходимости знать, какая была предыдущая команда.

2.3. Коррекция заголовков пакетов управляющих соединений

Трансляция данных управляющих команд может привести к изменению размера пакета. Это приводит к изменению порядковых номеров TCP (sequence and acknowledgment numbers в обоих направлениях) в управляющем соединении и к изменению полей размеров пакета в заголовках IP (IPv4 Total Length и IPv6 Payload Length). Потому FTP-ALG должен хранить дельты порядковых номеров для обоих направлений управляющего соединения, чтобы корректировать порядковые номера. Величина дельты получается путем вычитания длины команды после трансляции от длины команды до трансляции и прибавляется к предыдущему сохраненному значению этой дельты. Заметим, что разница может быть отрицательной.

Обозначим дельту для исходящих соединений (из v6 в v4) как Dout, а для входящих (из v4 в v6) как Din. Если пришедший пакет является пакетом из v6 в v4 область (исходящий) и в нем изменяется длина команды FTP, то разница между длиной v4 результирующей команды (после трансляции) и оригинальной v6 командой (до трансляции) прибавляется к Dout. При этом если длина v4 будет меньше длины v6 команды, то разница будет отрицательная.

Если пришедший пакет является пакетом из v4 в v6 область (входящий) и в нем изменяется длина команды FTP, то разница между длиной результирующей v6 команды и длиной v4 команды прибавляется к Din. При этом если длина v6 команды будет меньше длины v4 команды, то разница будет отрицательная. Коррекция дельты производиться после трансляции пакета, так порядковый номер пакета указывает на первый байт данных, а номер подтверждения - на следующий байт, который противоположная сторона ожидает принять.

Тогда при приходе пакета от v6 хоста (и это исходящий пакет управляющего соединения) порядковый номер (sequence number) этого пакета увеличивается на Dout, а порядковый номер подтверждения (acknowledgment number) этого же пакета уменьшается на Din. При приходе пакета от v4 хоста (и это входящий пакет управляющего соединения) порядковый номер (sequence number) этого пакета увеличивается на Din, а порядковый номер подтверждения (acknowledgment number) этого же пакета уменьшается на Dout.

Поясним выше сказанное примером:

1) (OUT) IPv4 	<--	IPv6	Dout = 0, Din = 0
L4 = 20	<--	L6 = 50	D'out = -30, D'in = 0
SN'out = SNout + Dout = 100	<--	SNout = 100
AN'out = ANout - Din = 500	<--	ANout = 500
Результирующие дельты: Dout += D'out = -30, Din += D'in = 0

2) (IN) IPv4 	-->	IPv6	Dout = -30, Din = 0
L4 = 40	-->	L6 = 60	D'out = 0, D'in = 20
SNin = 500	-->	SN'in = SNin + Din = 500
ANin = 120	-->	AN'in = ANin - Dout = 150
Результирующие дельты: Dout += D'out = -30, Din += D'in = 20

3) (OUT) IPv4 	<--	IPv6	Dout = -30, Din = 20
L4 = 40	<--	L6 = 40	D'out = 0, D'in = 0
SN'out = SNout + Dout = 120	<--	SNout = 150
AN'out = ANout - Din = 540	<--	ANout = 560
Результирующие дельты: Dout += D'out = -30, Din += D'in = 20

4) (IN) IPv4 	-->	IPv6	Dout = -30, Din = 20
L4 = 40	-->	L6 = 60	D'out = 0, D'in = 20
SNin = 540	-->	SN'in = SNin + Din = 560
ANin = 160	-->	AN'in = ANin - Dout = 190
Результирующие дельты: Dout += D'out = -30, Din += D'in = 40

5) (OUT) IPv4 	<--	IPv6	Dout = -30, Din = 40
L4 = 30	<--	L6 = 40	D'out = -10, D'in = 0
SN'out = SNout + Dout = 160	<--	SNout = 190
AN'out = ANout - Din = 580	<--	ANout = 620
Результирующие дельты: Dout += D'out = -40, Din += D'in = 40
3. Особенности реализации

Для обработки FTP соединений используется специальная таблица, записи которой хранят информацию о командном соединении FTP. Заметим, что соединение данных протокола FTP не подвергается воздействию FTP-ALG. Приходящие TCP пакеты предварительно обрабатываются модулем обработки TPC соединений и пакеты, относящиеся к командным соединениям FTP (эти пакеты имеют порт источника или порт назначения 21), направляются в модуль FTP-ALG.

Если соединение новое, заводится новая запись в таблицу, если нет, производится необходимая корректировка существующей записи и трансляция команды FTP, если необходимо. Далее транслированные данные возвращаются в модуль обработки TCP, который выполняет окончательное формирование выходного пакета TCP. Для соединений, порождаемые IPv4 клиентом, реализация ведет себя, как описано в п.2.1. Соединения, порождаемые IPv6 клиентом, обрабатываются способом, который является гибридом двух подходов, описанных в п.2.2. Сначала реализация действует как в первом подходе, и ожидает ответа от сервера. Если сервер возвращает сообщение об ошибке, что команды EPRT и EPSV не поддерживаются, выполняются действия, описанные во втором подходе.

Список литературы

[RFC 2428]
"FTP Extensions for IPv6 and NATs", 
M. Allman, S. Ostermann, C. Metz, September 1998
[RFC 2766]
"Network Address Translation - Protocol Translation (NAT-PT)", 
G. Tsirtsis, P. Srisuresh, February 2000
[RFC 2663]
"IP Network Address Translator (NAT) Terminology and Considerations", 
P. Srisuresh, M. Holdrege, August 1999