Написание стека TCP в Python
Недавно я начал посещать занятия по сетевым технологиям, и хотя концепция сетевых технологий меня очень увлекла, мне было трудно понять протокол управления передачей данных (Transmission Control Protocol).
Несколько основных понятий, которые мы будем использовать:
- Открытие сетевого сокета, позволяющего отправлять TCP-пакеты
- Отправка HTTP-запроса на сайт google.com с использованием
GET
- Получение и чтение полученного ответа
Также следует отметить, что корректная обработка ошибок при этом не была предусмотрена.
Установление связи TCP
Первое, что нам понадобится, - это установить связь с google. Вот как работает TCP handshake:
Предположим, что у нас есть двухслоговое слово index, которое разбивается на IN-DEX.
Пользователь, посылающий HTTP-запрос, получает возможность использовать: IN. Google, принимающему этот запрос, присваивается: INDEX. А мне, пользователю, присваивается: DEX
В простом коде это будет выглядеть следующим образом:
# My local network IP
src_ip = "192.168.0.11"
# Google's IP
dest_ip = "96.127.250.29"
# IP header: this is coming from me, and going to Google
ip_header = IP(dst=dest_ip, src=src_ip)
# Specify a large random port number for myself (59333),
# and port 80 for Google The "S" flag means this is
# a SYN packet
syn = TCP(dport=80, sport=59333,
ack=0, flags="S")
# Send the SYN packet to Google
# scapy uses '/' to combine packets with headers
response = srp(ip_header / syn)
# Add the sequence number
ack = TCP(dport=80, sport=self.src_port,
ack=response.seq, flags="A")
# Reply with the ACK
srp(ip_header / ack)
Что такое порядковые номера?
Идея TCP заключается в том, чтобы обеспечить возможность повторной отправки пакетов в случае, когда некоторые пакеты пропали. Порядковые номера — это способ проверить, не пропущены ли пакеты. В данном случае google отправляет 3 пакета размером 100, 200 и 300 байт. При этом предполагается, что начальный порядковый номер равен 0. Теперь эти пакеты будут иметь номера 0, 100, 300 и 600.
TCP Sequence Number - это 4-байтовое поле в заголовке TCP, которое показывает первый байт исходящего сегмента. Оно также позволяет отслеживать, сколько данных было передано и получено. Поле TCP Sequence Number всегда установлено.
Например, порядковый номер пакета - X. Длина этого пакета - Y. Если пакет эффективно передается на другую сторону, то порядковый номер следующего пакета будет X+Y.
Когда мы отправляем или пересылаем пакеты, как Google узнает о пропущенном пакете? Каждый раз, когда google получает пакет, мы также должны отправлять ACK о том, что получили пакет с порядковым номером. Если в этот момент сервер заметит, что пакет не был получен (ACK-пакет — это любой TCP-пакет, подтверждающий получение сообщения или серии пакетов), он отправит его повторно.
Что происходит при наличии стека TCP?
Если вы запустили приведенный выше код, то вы можете заметить, что произошла ошибка и мы получили другой пакет. В этом случае происходит следующее:
Python prgram: IN
Google: INDEX
Kernel: Didn't ask for this
Python Program: ..
Вопрос заключается в том, как обойти ядро? Одним из способов является ARP-спуфинг, который заключается в том, чтобы сделать вид, будто у нас другой IP-адрес 192.168.0.129
.
Теперь обмен выглядит следующим образом:
me: sends packets for 192.168.0.129 to the address
router: goes through with it
my Python program: IN (from 192.168.0.129)
google: INDEX
kernel: this isn't my IP address! <ignore>
my Python program: uses ACK
Если вы заметили, это работает, и теперь мы можем посылать пакеты для получения ответов без вмешательства ядра.
Как создать веб-страницу?
Чтобы предотвратить отправку html Google, нам необходимо учесть следующее:
- Обеспечение формирования пакета с запросом HTTP GET
- Обеспечение возможности прослушивания большого количества пакетов при наличии одного пакета
- Исправление ошибок с порядковыми номерами
- Правильное закрытие соединения
Регресс с Python
Если вы заметили, то после того, как все заработало, использование wireshark для просмотра отправляемых пакетов выглядит следующим образом:
User/google: <tcp handshake>
User: GET google.com
google: 100 packets
User: 3 ACKs
google: <starts resending packets>
User: a few more ACKs
google: <reset connection>
В приведенном выше сценарии google будет отправлять пакеты быстрее, чем это может сделать программа python, посылая ACK. Сервер Google предположит, что были возможные проблемы с сетью, из-за которых пользователь не получил ACK-пакеты.
Это приведет к сбросу соединения, поскольку Google решит, что были проблемы с подключением. Но мы знаем, что с соединением все в порядке и программа адекватно реагировала. Проблема заключалась в том, что программа python медленно принимала пакеты.
Заключение
Одной из неудач, с которой мы столкнулись, стала медлительность программы python. Также важно правильно понимать основные понятия, связанные с TCP, как они работают и как обрабатывают запросы, поскольку это поможет обеспечить глубокое понимание того, что влечет за собой TCP, и как решать ошибки при их возникновении.