Как направить udp пакеты в интернет

от admin

Как отправить TCP или UDP пакет в Linux?

Часто при тестировании каких-либо приложений может возникнуть необходимость проверить, доходят ли определенные пакеты по udp/tcp до адресата, например, при проверке функционирования фаервола или же проверки работоспособности проброса портов. В данной статье будет описан простой способ это сделать с помощью командой строки.

Как отправить TCP пакет на определенный ip:порт в Linux

Для отправки tcp пакета на определенный IP адрес и определенный порт, можно воспользоваться следующей командой:

1.2.3.4 — это IP адрес, на который мы будем посылать наш tcp пакет.
12345 — это порт, на который мы будем посылать наш tcp пакет

Альтернативным вариантом может быть использование утилиты nmap:

Как отправить UDP пакет на определенный ip:порт в Linux

Для отправки udp пакета на определенный IP адрес и определенный порт, можно воспользоваться следующей командой:

1.2.3.4 — это IP адрес, на который мы будем посылать наш udp пакет.
12345 — это порт, на который мы будем посылать наш udp пакет

Альтернативным вариантом может быть использование утилиты nmap:

eth0 — название сетевого интерфейса, который мы будем прослушивать
12345 — номер порта

Сетевое программирование для разработчиков игр. Часть 3: виртуальные соединения поверх UDP

Привет. Меня зовут Гленн Фидлер и я приветствую вас в третьей статье из цикла “Сетевое программирование для разработчиков игр”.

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

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


В качестве первого шага к реализации системы соединений, мы рассмотрим наиболее простой случай: создание виртуального соединения между двумя компьютерами.

Но сначала, нам нужно более плотно разобраться, как работает интернет.

Интернет — это не набор труб

В 2006 году сенатор Тед Стивенс вошел в историю интернета со своей знаменитой речью об Акте Сетевого Нейтралитета:

Когда я только начинал пользоваться интернетом, я думал точно так же, как Тед. Сидя в компьютерном классе Сиднейского Университета в 1995 году, я “серфил в интернете” с помощью новомодной штуки, которая называлась Netscape Navigator, и не имел ни малейшего понятия, что там происходило на самом деле.

Тогда я думал, что каждый раз, когда я подключался к какому-либо сайту, создавалось настоящее подключение, как при телефонном разговоре. И я думал — сколько стоит подключиться к сайту? Тридцать центов? Доллар? Станет ли кто-нибудь из университета требовать с меня деньги за подключения по межгороду? 🙂

Конечно, сейчас это все звучит просто смешно.

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

Без прямых подключений

Вместо всего этого, ваши данные пересылаются по протоколу IP в пакетах, которые бегут от компьютера к компьютеру.

Пакет может пройти через несколько узлов, пока не достигнет адресата. Вы не можете знать заранее количество этих узлов, так как оно меняется динамически, в зависимости от того, как сеть решает направить пакеты. Даже если отправите два пакета A и B по одному и тому же адресу, они могут пойти разными путями. В этом, кстати говоря, и заключается причина отсутствия гарантии доставки пакетов по порядку в UDP.

В unix-подобных системах можно изучить маршруты пакетов утилитой “traceroute”, передав ей имя хоста или IP адрес.

В windows вместо “traceroute” используется “tracert”.

Попробуйте проанализировать маршруты до нескольких сайтов, например:

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

Как происходит доставка пакетов

В первой статье цикла я привел простую аналогию процесса доставки пакетов — как передача записки от одного человека к другому в комнате, полной народу.

Хотя эта аналогия и отображает общую идею, но она слишком упрощена. Интернет — это не простая одноранговая сеть, а сеть сетей. И, конечно, нам нужно передавать записки не в пределах комнаты, а в любую точку мира!

Очевидно, много лучшей аналогией является… почтовая служба!

Когда вы хотите отправить кому-то письмо, вы кладете его в почтовый ящик, и при этом вы уверены, что оно придет по адресу. Для вас не важно, как именно оно будет доставлено, а важен сам факт доставки. Конечно, кто-то должен физически доставить письмо — но каким образом?

Очевидно, что почтальон не сам будет доставлять ваше письмо — почтовая служба это тоже не набор труб :). Вместо этого, он отнесет ваше письмо в почтовое отделение для последующей обработки.

Если адресат письма живет в том же районе, что и вы, то в почтовом отделении ваше письмо просто отдадут другому почтальону, и он сам отнесет его. Но если нет, то начинается уже более интересный процесс. Ваше местное почтовое отделение не может доставить письмо самостоятельно, и оно передает его “выше” по иерархии — в региональное отделение или почтовый центр в аэропорту, в случае, если адресат находится далеко. В идеальном случае ваше письмо повезут в большом грузовике (отсылка к речи того сенатора — прим. перев.).

Давайте рассмотрим сложный случай — скажем, мы отправляем письмо из Лос-Анджелеса в Сидней в Австралии. Местное почтовое отделение получает письмо, определяет, что оно должно быть доставлено за рубеж, и пересылает его в почтовый центр в аэропорту Лос-Анджелеса. Там снова обрабатывают адрес назначения письма, и отправляют его ближайшим рейсом до Сиднея.

Самолет с письмом садится в Сиднее, где вступает в работу новая почтовая служба, и проделывает все те же операции, но в обратном порядке. Письмо идет “вниз” по иерархии, от общего к частному. Из пункта сортировки почты в аэропорту оно попадает в региональный офис, который, в свою очередь, пересылает его местному почтовому отделению, и, в конце концов, почтальон (с забавным акцентом) отдает письмо в руки адресату. Чудесно! 🙂

Подобно тому, как почтовое отделение определяет, как доставлять письмо, на основе анализа адреса получателя, пакеты в сети доставляются на основе анализа IP адреса. Сам процесс определения маршрута передачи пакета довольно сложен, но основная идея заключается в том, что каждый роутер — это такой же компьютер, у которого имеется таблица маршрутизации, которая определяет, куда отправлять пакеты с определенными адресами назначения, и адресом шлюза по умолчанию, которому надо отправлять пакеты, для которых не нашлось соответствующей записи в таблице маршрутизации. Все это вместе и составляет ту самую “сеть сетей” — интернет.

Настройка таблиц маршрутизации — это задача сетевых администраторов, а не разработчиков (то есть нас). Но если вы хотите больше об этом узнать, то в этой статье из журнала ars technica есть много интересной информации о том, как сети обмениваются пакетами с использованием пиринга и пиринговых соглашений. Также можно еще почитать о таблицах маршрутизации в этом linux faq, и о протоколе граничного шлюза (BGP) в wikipedia, который автоматически определяет, как переправлять пакеты между сетями — что делает интернет по-настоящему распределенной системой с возможностью динамического обхода поврежденных каналов связи.

Виртуальные соединения

Теперь вернемся к теме соединений.

Если вы уже работали с TCP сокетами, то вы знаете, что работа с ними похожа на работу с соединениями, однако, так как TCP работает поверх IP, а IP может только пересылать отдельные пакеты, в TCP должен быть реализован механизм виртуальных соединений.

А если в TCP реализован механизм виртуальных соединений, значит, мы можем реализовать его и с помощью UDP.

Также, давайте определим термин “виртуальное соединение” как обмен UDP пакетами между двумя компьютерами с фиксированной частотой — скажем, десять пакетов в секунду. Пока обмен пакетами продолжается, виртуальное соединение считается установленным.

У соединения есть два конца:

  • Первый компьютер ожидает соединения от другого компьютера — этот компьютер будет называться сервером.
  • Второй компьютер подключается к серверу с определенным IP адресом и портом. Этот компьютер будет называться клиентом.
ID протокола

Так как UDP сам по себе не поддерживает соединения, UDP сокет может принимать пакеты с любого компьютера.

Нам нужно ограничить это поведение таким образом, чтобы сервер принимал пакеты только от своих клиентом, а клиент — только от сервера. И при этом мы не можем просто фильтровать пакеты по адресу отправителя, так как сервер не может знать адреса клиентов заранее. Поэтому в начало каждого UDP пакета мы добавим небольшой заголовок длиной в 32 бита, в котором будет хранится уникальный идентификатор протокола.

Идентификатор протокола — это просто уникальное число, выбранное для нашего протокола. Как только UDP сокет примет пакет, он сразу должен проанализировать первые четыре байта пакета. Если данные не соответствуют нашему идентификатору, то пакет отбрасывается. Если соответствуют, то эти четыре байта обрезаются, и остальные данные пакета передаются на обработку.

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

Определение наличия соединения

Далее нам необходимо придумать способ определения наличия соединения.

Конечно, мы могли бы придумать какую-нибудь сложную схему с “рукопожатиями” и пересылкой нескольких UDP пакетов туда и обратно. Например, такую: клиент посылает серверу пакет “Запрос соединения”, и сервер отвечаем ему либо пакетом “Соединение установлено”, либо “Занят”, если клиент пытается подключится к серверу, у которого уже есть соединение с другим клиентом.

… или же мы могли бы просто запрограммировать сервер таким образом, чтобы после приема первого пакета с правильным идентификатором протокола он сразу считал, что соединение установлено.

В этом случае клиент может просто начинать пересылать серверу пакеты (считая, что соединение уже установлено), и, когда сервер примет первый пакет, он сохранит IP адрес и порт клиента, и тоже начнет отсылать пакеты.

При этом сам клиент, естественно, заранее знает IP адрес и порт сервера, так как он подключается первым. Поэтому когда клиент получает пакеты в ответ от сервера, он может фильтровать их уже по адресу. Аналогично и сервер после получения первого пакета от клиента может взять адрес и порт клиента из функции “recvfrom”, и фильтровать все остальные пакеты, которые будут приходить не от клиента.

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

Но пока не будем усложнять все больше, чем нужно.

Определение отключения

А как нам определить отключение?

Ну, раз уж мы определили подключение как процесс передачи пакетов, то мы можем и определить отключение как отсутствие передачи пакетов.

Чтобы определить, в свою очередь, отсутствие передачи пакетов, мы должны следить за количеством секунд с момента приема последнего пакета от другой стороны, причем на обоих компьютерах.

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

Если счетчик превысит какой-то порог, например, десять секунд, мы считаем это “тайм-аутом” соединения и закрываем его.

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

Заключение

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

Наше подключение настолько же реально, как и любое TCP подключение, и пакета UDP пакетов, которого оно обеспечивает, вполне достаточно для использования в нашем будущем многопользовательском экшене.

В статье мы также немного разобрали, как работает маршрутизация пакетов в интернете. К примеру, мы узнали причину, по которой UDP пакеты иногда приходят не в правильном порядке — потому что они могут пойти разными маршрутами на уровне IP. Посмотрите на карту интернета — не чудо ли, что пакеты вообще куда-либо доходят? Если вы хотите получше разобраться во всем этом, хорошей отправной точкой может стать эта статья на wikipedia.

Теперь, когда у нас есть механизм виртуальных подключений по UDP, мы можем легко реализовать обмен данными между клиентом и сервером для нашей многопользовательской игры — и без использования TCP.

Пример реализации вы можете посмотреть в исходном коде для этой статьи.

Это простое клиент-серверное приложение, которое производит обмен пакетами с частотой в 30 пакетов в секунду. Вы можете запустить сервер на любой машине, но с публичным IP адресом, так как “проброс” пакетов через NAT пока еще не поддерживается.

Запустить клиент можно следующим образом:

В этом случае он будет пытаться подключиться к серверу по адресу, который вы укажете в командной строке. По умолчанию, если вы не укажете ничего, он будет пытаться подключиться к 127.0.0.1.

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

Также вы можете попробовать остановить клиент или сервер, когда между ними установлено подключение, и тогда вы заметите, что через десять секунд приложение на другой стороне отключится по тайм-ауту. Когда отключается клиент, управление возвращается командной оболочке, а когда отключается сервер — он возвращается в состояние ожидания подключений от других клиентов.

Как направить udp пакеты в интернет

Как отправить TCP или UDP пакет в Linux?

Часто при тестировании каких-либо приложений может возникнуть необходимость проверить, доходят ли определенные пакеты по udp/tcp до адресата, например, при проверке функционирования фаервола или же проверки работоспособности проброса портов. В данной статье будет описан простой способ это сделать с помощью командой строки.

Как отправить TCP пакет на определенный ip:порт в Linux

Для отправки tcp пакета на определенный IP адрес и определенный порт, можно воспользоваться следующей командой:

1.2.3.4 — это IP адрес, на который мы будем посылать наш tcp пакет.
12345 — это порт, на который мы будем посылать наш tcp пакет

Альтернативным вариантом может быть использование утилиты nmap:

Как отправить UDP пакет на определенный ip:порт в Linux

Для отправки udp пакета на определенный IP адрес и определенный порт, можно воспользоваться следующей командой:

1.2.3.4 — это IP адрес, на который мы будем посылать наш udp пакет.
12345 — это порт, на который мы будем посылать наш udp пакет

Читать:
Где стоят гаишники ярославль

Альтернативным вариантом может быть использование утилиты nmap:

eth0 — название сетевого интерфейса, который мы будем прослушивать
12345 — номер порта

Отправка и получение UDP-пакетов через Linux CLI

Отправка и получение UDP-пакетов через Linux CLI

Команда Netcat (nc) по умолчанию установлена ​​в ОС Linux. Откройте один терминал [сочетание клавиш Alt + Ctrl + t] и используйте команду ниже, чтобы проверить, присутствует ли NC или нет.

Вот ожидаемый результат

Это nc из пакета netcat-openbsd. Доступен альтернативный NC
в традиционном пакете netcat.
использование: nc [-46bCDdhjklnrStUuvZz] [-I длина] [-i интервал] [-O длина]
[-P proxy_username] [-p source_port] [-q секунды] [-s source]
[-T toskeyword] [-V rtable] [-w timeout] [-X proxy_protocol]
[-x proxy_address [: порт]] [пункт назначения] [порт]

Это означает, что команда nc уже существует в Linux.

Схема общей настройки:

Отправить пакет UDP:

Давайте возьмем пример, как мы отправим пакет UDP из системы A в систему B. Итак, в концепции сервер-клиент мы должны запустить сервер на стороне системы B и клиент на стороне системы A.

Также у нас есть действующие IP-адреса.

Система А IP: 192.168.1.6
Система B IP: 192.168.1.102

Стартовый сервер:

Чтобы запустить сервер с помощью команды nc, используйте команду ниже в терминале системы B

На данный момент у этой команды нет выходных данных для отображения. Это просто режим прослушивания порта 9999.

Стартовый клиент:

Чтобы подключиться к серверу с помощью команды nc, используйте команду ниже в терминале системы A

$ NC -U 192.168.1.102 9999

Теперь система A должна подключиться к системе B. Итак, мы предоставили IP-адрес сервера и номер порта.

Проверьте подключение:

Мы можем проверить приведенную ниже команду для подтверждения о подключении клиента к порту сервера.

$ netstat | grep 9999

Отправлять UDP-пакеты:

Теперь мы можем отправить пакет udp из системы A в систему B и наоборот.

Шаг 1:

Теперь перейдите в систему A и отправьте любые предложения вроде

«Привет, я из LinuxHint [Система A 192.168.1.6] »

Шаг 2:

Мы должны увидеть это на стороне Системы B. Вот скриншот

Мы также можем отправлять UDP-пакеты из Системы B в Систему A.

Шаг 1:

Перейдите в систему B и отправьте предложение вроде

«Привет, я из LinuxHint [Система B 192.168.1.102] «

Вот скриншот из Системы B

Шаг 2:

Вот скриншот из Системы А

Проверяем пакеты в Wireshark:

Теперь, когда мы отправляем UDP-пакеты из системы A в систему B и наоборот, мы можем запустить Wireshark либо в системе A, либо в системе B. Здесь у нас есть файл захвата, давайте проведем некоторый анализ и подтвердим, использует ли этот сервер и клиент для связи протокол UDP.

Обратите внимание, что мы проанализируем только первое сообщение:

Система A отправила:

«Привет, я из LinuxHint [Система A 192.168.1.6] »

К:

Система B [192.168.1.102].

Мы будем использовать фильтр «UDP.порт == 9999 ” чтобы получить только связанные пакеты в Wireshark. См. Снимок экрана ниже для анализа захвата Wireshark:

Чтобы узнать, как использовать Wireshark, перейдите по ссылке ниже

https: // linuxhint.ru / wirehark_basics_how_to_use /

Другая команда для отправки пакетов UDP:

Есть еще один способ отправки UDP-пакетов

Запустите сервер в системе B:

Выполните команду ниже в системе A:

$ echo -n «привет»> / dev / udp / 192.168.1.102/8000
192.168.1.102: IP системы B
8000: порт сервера
Сообщение отправлено: «привет»

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

Заключение:

Из приведенного выше упражнения мы узнали механизм отправки некоторых сообщений по протоколу UDP. И лучший способ — использовать NC команда в Linux.

Рекомендации:

Чтобы понять TCP: https: // linuxhint.ru / tcp_packet_capture_analysis /
Чтобы понять UDP: https: // linuxhint.ru / udp_wireshark_analysis /

Мышь Как изменить указатель мыши и размер курсора, цвет и схему в Windows 10
Игры Бесплатные движки с открытым исходным кодом для разработки игр для Linux
Игры Shadow of the Tomb Raider для Linux Учебное пособие

Свежие статьи об операционных системах. Множество интересных гайдов и полезных советов. Почувствуйте себя своим в мире современных технологий

Теория

Я получаю доступ в интернет через роутер, то есть, в отличии от роутера, не имею внешнего IP. Так же имеется сервер, но уже с внешним IP.

Верно ли, что отправленный мной UDP пакет, проходя через роутер, претерпит преобразование исходящих адреса и порта, и пробросит новый порт наружу чтобы сервер смог прислать мне ответ? То есть если я отправляю пакет с порта 5000, то, достигнув роутера, номер исходящего порта поменяется на случайное x и роутер запомнит, что все входящие пакеты на порт x нужно перебрасывать на мой IP к 5000’ому порту.

Практика

Если это верно, то почему не подтверждается следующим кодом?

Пакет отправляю так

Сразу после отправки начинаю слушать

Но прослушка ничего не выводит. Почему?

Всё тоже самое, но в рамках локальной сети, работает корректно.

Nelson Tatius's user avatar

Верно ли, что отправленный мной UDP пакет, проходя через роутер, претерпит преобразование исходящих адреса и порта, и пробросит новый порт наружу чтобы сервер смог прислать мне ответ? То есть если я отправляю пакет с порта 5000, то, достигнув роутера, номер исходящего порта поменяется на случайное x и роутер запомнит, что все входящие пакеты на порт x нужно перебрасывать на мой IP к 5000’ому порту.

Вам, вероятно, нужен не порт, который слушается сервером, а внешний порт, на который роутер отобразил внутренний порт, на котором вы реально слушаете. Иначе говоря, вам нужно:

  1. Послать пакет на машину с известным IP (назову сервером). Роутер пробросит порты. Сервер должен запомнить, с какого порта ему пришел пакет.
  2. Ответить клиенту на этот порт, который он запомнил на предыдущем шаге.

Кстати, можете почитать: Peer-to-Peer Communication Across Network Address Translators. Когда-то я реализовывал описанную там методику (аналогичная вашей теории) и все более-менее работало.

Подключение микроконтроллера к локальной сети: UDP-клиент

В этой части мы продолжим писать наш стек протоколов. Добавим возможность отправлять UDP-пакеты на любой IP-адрес и научимся получать данные с удалённого сервера.

Краткое содержание:

  • Введение в роутинг
  • ARP-ресолвер
  • Отправка пакетов
  • Пример работы со стеком
  • Заключение
Маршрутизация в Internet

Немножко теории. Рассмотрим основы роутинга в Internet. Думаю, большинство читателей могут эту часть спокойно пропустить)

Итак, возьмём, для примера, мою домашнюю сеть.

Домашняя сеть

Допустим, комп знает IP-адрес девайса и хочет отправить ему пакет. При этом происходит следующее:

  • Комп убеждается, что девайс находится в той же локальной сети, что и он сам.
  • С помощью ARP, комп определяет MAC-адрес девайса.
  • Комп заворачивает IP-пакет в Ethernet-фрейм и отрпавляет на MAC-адрес девайса.
  • Девайс получает фрейм, видит в нём IP-пакет, в котором в качестве адреса получателя указан его собственный IP-адрес.
  • Полученный IP-пакет передаётся протоколу транспортного уровня (UDP, etc.) и, затем, приложению.

IP-адрес и маска подсети

Для выделения адреса подсети из IP-адреса служит маска подсети. Накладываем на IP-адрес маску и получаем адрес подсети.

В моей домашней сетке маска равна 255.255.255.0. Соответственно, адрес подсети равен 192.168.0.0, а допустимые IP-адреса — от 192.168.0.1 до 192.168.0.254 (первый и последний адреса зарезервированы). Видя, что адрес девайса относится к этому промежутку, комп и понимает — девайс находится с ним в одной локальной сети.

Но что, если мы хотим отправить пакет узлу, который находится не в нашей локальной сети, а вообще неизвестно где? Рассмотрим вот такую сеть (случай не то, чтобы очень жизненный, зато наглядный):

Пример сети

Запись вроде 192.168.3.0/24 означает подсеть с адресом 192.168.3.0 и маской 255.255.255.0 (24 бита установлено).

Допустим, 192.168.0.33 хочет отправить пакет узлу 192.168.3.3, находящемуся в сети 192.168.3.0. Происходит следующее:

  • 192.168.0.33 записывает в IP пакет адрес отправителя 192.168.0.33 (свой), а адрес получателя — 192.168.3.3. Ничего необычного.
  • Заворачивает пакет в Ethernet-фрейм и отправляет его на MAC-адрес узла 192.168.0.22.
  • 192.168.0.22 получает фрейм, видит в нём пакет, предназначеный 192.168.3.3. Поскольку он подключен к обоим локальным сетям, он просто пересылает пакет узлу 192.168.3.3.

Таблица роутов

Когда узел хочет отправить пакет в другую сеть, он просматривает свою таблицу роутов. В ней он находит адрес узла (гейт), на который нужно переслать пакет, чтобы он попал в нужную сеть. Последняя запись — роут по-умолчанию (default route). Она определяет основной гейт (default gateway) — узел, на который пересылаются все пакеты, роут для которых не прописан в явном виде.

Вернёмся к нашей сети. Все пакеты, выходящие за пределы сети проходят через роутер (192.168.0.1), он-то и является основным гейтом. Дополнительные роуты нам прописывать ни к чему, для отправки пакетов наружу достаточно знать адрес основного гейта.

Собирая вместе всё вышесказанное, можно отметить: для полноценной работы наш девайс должен знать три вещи — свой IP-адрес, маску подсети и IP-адрес основного гейта. Маска подсети используется, чтобы определять, относится ли определённый IP-адрес к нашей локальной сети. Все пакеты, выходящие за пределы локальной сети мы пересылаем основному гейту.

ARP-ресолвер

В предыдущей части мы научились отвечать на ARP-запросы. Чтобы пересылать пакеты другим узлам, нам понадобится также написать ARP-ресолвер.

Алгоритм работы нашего ARP-ресолвера будет следующий:

  • Когда нам нужно определить MAC-адрес узла, ищем его в ARP-кэше по IP-адресу.
  • Если узел найден в кэше, просто возвращаем его MAC-адрес.
  • Если узел в кэше не найден, посылаем широковещательный ARP-запрос, чтобы найти нужный узел.
  • Получив ответ на наш запрос, добавляем узел в ARP-кэш.

Проверять валидность записей кэша мы не будем — врядли MAC-адрес какого-либо узла внезапно изменится.

Отправка пакетов

Теперь мы готовы отправлять IP-пакеты любым узлам, подключенным к интернету. Для начала, нам понадобится определить наш IP-адрес, маску подсети и основной гейт.

Алгоритм отправки IP-пакета будет простой:

  • Определяем IP-адрес узла, на который будем отправлять Etheret-фрейм, содержащий пакет. Если пакет пересылается в пределах локальной сети, сразу посылаем его нужному узлу. Иначе будем отправлять фрейм основному гейту.
  • Ресолвим MAC-адрес узла.
  • Заворачиваем IP-пакет в Ethernet-фрейм и посылаем.

Ну и отправка UDP-пакета.

Пишем приложение

Чисто чтобы потестить наш улучшенный стек, попробуем получить какую-нибудь информацию из интернета. Например, точное время по NTP.

NTP реализуем самым тупым способом — отрпавляем серверу запрос, получаем ответ с точным временем. Для правильной работы NTP, локальный UDP-порт не должен равняться UDP-порту сервера (123). Также, нужно учитывать, что NTP возвращает неправильный timestamp — количество секунд, прошедших с 1 января 1900 года. Чтобы получить нормальный timestamp, считающийся с 1 января 1970, года, нужно отнять от NTP-timestamp’а ровно 2208988800 секунд.

С помощью NTP, будем запрашивать время каждые 12 часов.

Потестируем то, что получилось.

Заключение

Скачать проект можно тут.

В следующей части мы немного отвлечёмся от микроконтроллеров и посмотрим как можно общаться с сетевыми девайсами со стороны компа.

Подключение микроконтроллера к локальной сети: UDP-клиент

В этой части мы продолжим писать наш стек протоколов. Добавим возможность отправлять UDP-пакеты на любой IP-адрес и научимся получать данные с удалённого сервера.

Краткое содержание:

  • Введение в роутинг
  • ARP-ресолвер
  • Отправка пакетов
  • Пример работы со стеком
  • Заключение
Маршрутизация в Internet

Немножко теории. Рассмотрим основы роутинга в Internet. Думаю, большинство читателей могут эту часть спокойно пропустить)

Итак, возьмём, для примера, мою домашнюю сеть.

Домашняя сеть

Допустим, комп знает IP-адрес девайса и хочет отправить ему пакет. При этом происходит следующее:

  • Комп убеждается, что девайс находится в той же локальной сети, что и он сам.
  • С помощью ARP, комп определяет MAC-адрес девайса.
  • Комп заворачивает IP-пакет в Ethernet-фрейм и отрпавляет на MAC-адрес девайса.
  • Девайс получает фрейм, видит в нём IP-пакет, в котором в качестве адреса получателя указан его собственный IP-адрес.
  • Полученный IP-пакет передаётся протоколу транспортного уровня (UDP, etc.) и, затем, приложению.

IP-адрес и маска подсети

Для выделения адреса подсети из IP-адреса служит маска подсети. Накладываем на IP-адрес маску и получаем адрес подсети.

В моей домашней сетке маска равна 255.255.255.0. Соответственно, адрес подсети равен 192.168.0.0, а допустимые IP-адреса — от 192.168.0.1 до 192.168.0.254 (первый и последний адреса зарезервированы). Видя, что адрес девайса относится к этому промежутку, комп и понимает — девайс находится с ним в одной локальной сети.

Но что, если мы хотим отправить пакет узлу, который находится не в нашей локальной сети, а вообще неизвестно где? Рассмотрим вот такую сеть (случай не то, чтобы очень жизненный, зато наглядный):

Пример сети

Запись вроде 192.168.3.0/24 означает подсеть с адресом 192.168.3.0 и маской 255.255.255.0 (24 бита установлено).

Допустим, 192.168.0.33 хочет отправить пакет узлу 192.168.3.3, находящемуся в сети 192.168.3.0. Происходит следующее:

  • 192.168.0.33 записывает в IP пакет адрес отправителя 192.168.0.33 (свой), а адрес получателя — 192.168.3.3. Ничего необычного.
  • Заворачивает пакет в Ethernet-фрейм и отправляет его на MAC-адрес узла 192.168.0.22.
  • 192.168.0.22 получает фрейм, видит в нём пакет, предназначеный 192.168.3.3. Поскольку он подключен к обоим локальным сетям, он просто пересылает пакет узлу 192.168.3.3.

Таблица роутов

Когда узел хочет отправить пакет в другую сеть, он просматривает свою таблицу роутов. В ней он находит адрес узла (гейт), на который нужно переслать пакет, чтобы он попал в нужную сеть. Последняя запись — роут по-умолчанию (default route). Она определяет основной гейт (default gateway) — узел, на который пересылаются все пакеты, роут для которых не прописан в явном виде.

Вернёмся к нашей сети. Все пакеты, выходящие за пределы сети проходят через роутер (192.168.0.1), он-то и является основным гейтом. Дополнительные роуты нам прописывать ни к чему, для отправки пакетов наружу достаточно знать адрес основного гейта.

Собирая вместе всё вышесказанное, можно отметить: для полноценной работы наш девайс должен знать три вещи — свой IP-адрес, маску подсети и IP-адрес основного гейта. Маска подсети используется, чтобы определять, относится ли определённый IP-адрес к нашей локальной сети. Все пакеты, выходящие за пределы локальной сети мы пересылаем основному гейту.

ARP-ресолвер

В предыдущей части мы научились отвечать на ARP-запросы. Чтобы пересылать пакеты другим узлам, нам понадобится также написать ARP-ресолвер.

Алгоритм работы нашего ARP-ресолвера будет следующий:

  • Когда нам нужно определить MAC-адрес узла, ищем его в ARP-кэше по IP-адресу.
  • Если узел найден в кэше, просто возвращаем его MAC-адрес.
  • Если узел в кэше не найден, посылаем широковещательный ARP-запрос, чтобы найти нужный узел.
  • Получив ответ на наш запрос, добавляем узел в ARP-кэш.

Проверять валидность записей кэша мы не будем — врядли MAC-адрес какого-либо узла внезапно изменится.

Отправка пакетов

Теперь мы готовы отправлять IP-пакеты любым узлам, подключенным к интернету. Для начала, нам понадобится определить наш IP-адрес, маску подсети и основной гейт.

Алгоритм отправки IP-пакета будет простой:

  • Определяем IP-адрес узла, на который будем отправлять Etheret-фрейм, содержащий пакет. Если пакет пересылается в пределах локальной сети, сразу посылаем его нужному узлу. Иначе будем отправлять фрейм основному гейту.
  • Ресолвим MAC-адрес узла.
  • Заворачиваем IP-пакет в Ethernet-фрейм и посылаем.

Ну и отправка UDP-пакета.

Пишем приложение

Чисто чтобы потестить наш улучшенный стек, попробуем получить какую-нибудь информацию из интернета. Например, точное время по NTP.

NTP реализуем самым тупым способом — отрпавляем серверу запрос, получаем ответ с точным временем. Для правильной работы NTP, локальный UDP-порт не должен равняться UDP-порту сервера (123). Также, нужно учитывать, что NTP возвращает неправильный timestamp — количество секунд, прошедших с 1 января 1900 года. Чтобы получить нормальный timestamp, считающийся с 1 января 1970, года, нужно отнять от NTP-timestamp’а ровно 2208988800 секунд.

С помощью NTP, будем запрашивать время каждые 12 часов.

Потестируем то, что получилось.

Заключение

Скачать проект можно тут.

В следующей части мы немного отвлечёмся от микроконтроллеров и посмотрим как можно общаться с сетевыми девайсами со стороны компа.

Похожие статьи