Введение в LwIP

Введение в LwIP

LwIP – простой и компактный TCP/IP стек.

  • LwIP небольшая независимая реализация стека протоколов TCP/IP, которая была первоначально разработана Адамом Данкелсом.
  • Центром внимания реализации LwIP TCP/IP является сокращение использования ресурсов и при этом сохранение полномасштабной TCP. Это делает LwIP возможным для использования во встраиваемых системах с десятками килобайт свободной оперативной памяти и с местом для около 40 килобайт кода ROM.

Основные функции включают в себя:
протоколы: IP, ICMP, UDP, TCP, IGMP, ARP, PPPoS, PPPoE

  • DHCP-клиент, DNS-клиент, AutoIP / APIPA (Zeroconf), SNMP агент (частная поддержка MIB)
  • API: специализированные API-интерфейсы для повышения производительности, по желанию Беркли сокеты API
  • Расширенные возможности: IP-переадресация на несколько сетевых интерфейсов, управление перегрузкой TCP, оценка RTT и быстрое восстановление/быстрый повтор
  • Аддон приложения: HTTP-сервер, клиент SNTP, SMTP-клиент, пинг, NetBIOS сервер имен

LwIP имеет BSD лицензию (подробнее).

LwIP предлагает три различных API-интерфейса, предназначенных для различных целей:

  • Raw API является основной API LwIP. Этот API направлен на обеспечение наилучших характеристик при использовании минимального размера кода. Один из недостатков этого API является то, что он обрабатывает асинхронные события с использованием обратных вызовов, которые усложняют разработку приложений.
  • Netconn API представляет собой последовательный API интерфейс построенный на базе Raw API. Это позволяет выполнять многопоточную обработку данных, следовательно,
    требуется наличие операционной системы. Это проще в использовании, чем Raw API за счет более низкой реализации исполнения и увеличения объема памяти.
  • BSD API сокеты, сокеты Беркли, как сокет реализации (Posix / BSD), построенный на базе API Netconn. Данных факт увеличивает основы для переносимости кода. Он имеет те же недостаток, что и API Netconn.

Проект будем создавать с помощью кодогенератора STM32CubeMX для отладочной платы stm32f746g-disco.

lwip_cubemx_1

lwip_cubemx_2

lwip_cubemx_3

lwip_cubemx_4

lwip_cubemx_5

Для FreeRTOS увеличиваем размер HEAP до ~50K и для потока ~2K.
Необходимые заголовочные файлы для работы функций tcpecho и tcpcounter.

#include "lwip/opt.h"
#include "lwip/arch.h"
#include "lwip/api.h"
#include <string.h>

Пример – “эхо для TCP”

static void tcpecho(void)
{
    struct netconn *conn, *newconn;
    err_t err, accept_err;
    struct netbuf* buf;
    void* data;
    u16_t len;
    err_t recv_err;

    /* Create a new connection identifier. */
    conn = netconn_new(NETCONN_TCP);
    if (conn != NULL)
    {
        /* Bind connection to well known port number 80. */
        err = netconn_bind(conn, NULL, 80);

        if (err == ERR_OK)
        {
            /* Tell connection to go into listening mode. */
            netconn_listen(conn);

            while (1)
            {
                /* Grab new connection. */
                accept_err = netconn_accept(conn, &newconn);

                /* Process the new connection. */
                if (accept_err == ERR_OK)
                {
                    while ((recv_err = netconn_recv(newconn, &buf)) == ERR_OK)
                    {
                        do
                        {
                            netbuf_data(buf, &data, &len);
                            netconn_write(newconn, data, len, NETCONN_COPY);

                        } while (netbuf_next(buf) >= 0);

                        netbuf_delete(buf);
                    }

                    /* Close connection and discard connection identifier. */
                    netconn_close(newconn);
                    netconn_delete(newconn);
                }
            }
        }
        else
        {
            netconn_delete(newconn);
            printf(" can not bind TCP netconn");
        }
    }
    else
    {
        printf("can not create TCP netconn");
    }
}

Функции printf можно закомментировать, если нету условия или обработчика для вывода информации.

Пример – “счетчик отправленных сообщений”

void tcpcounter(void)
{
    struct netconn *conn, *newconn;
    err_t err, accept_err;
    /* Create a new connection identifier. */
    conn = netconn_new(NETCONN_TCP);
    if (conn != NULL)
    {
        /* Bind connection to well known port number 80. */
        err = netconn_bind(conn, NULL, 80);
        if (err == ERR_OK)
        {
            /* Tell connection to go into listening mode. */
            netconn_listen(conn);
            /* Grab new connection. */
            accept_err = netconn_accept(conn, &newconn);
            /* Process the new connection. */
            if (accept_err == ERR_OK)
            {
                while (1)
                {
                    char buffer[32] = { 0x00 };
                    memset(buffer, sizeof(buffer), 0x00);
                    static int cntr = 0;
                    sprintf(buffer, "Сounter = %d\r", cntr++);
                    netconn_write(
                        newconn, (const unsigned char*)buffer, strlen(buffer), NETCONN_COPY);
                    osDelay(1000);
                }
            }
        }
    }
}

Создаем один поток который будет работать с LwIP.

/* StartDefaultTask function */
void StartDefaultTask(void const * argument)
{
  /* init code for LWIP */
  MX_LWIP_Init();

  /* USER CODE BEGIN 5 */
  /* Infinite loop */
	tcpecho();
	//tcpcounter();
  /* USER CODE END 5 */ 
}

Инициализация LwIP.

void MX_LWIP_Init(void)
{
  /* IP addresses initialization */
  IP_ADDRESS[0] = 192;
  IP_ADDRESS[1] = 168;
  IP_ADDRESS[2] = 0;
  IP_ADDRESS[3] = 10;
  NETMASK_ADDRESS[0] = 255;
  NETMASK_ADDRESS[1] = 255;
  NETMASK_ADDRESS[2] = 255;
  NETMASK_ADDRESS[3] = 0;
  GATEWAY_ADDRESS[0] = 0;
  GATEWAY_ADDRESS[1] = 0;
  GATEWAY_ADDRESS[2] = 0;
  GATEWAY_ADDRESS[3] = 0;
  
  /* Initilialize the LwIP stack with RTOS */
  tcpip_init( NULL, NULL );

  /* IP addresses initialization without DHCP (IPv4) */
  IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
  IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
  IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);

  /* add the network interface (IPv4/IPv6) with RTOS */
  netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);

  /* Registers the default network interface */
  netif_set_default(&gnetif);

  if (netif_is_link_up(&gnetif))
  {
    /* When the netif is fully configured this function must be called */
    netif_set_up(&gnetif);
  }
  else
  {
    /* When the netif link is down this function must be called */
    netif_set_down(&gnetif);
  }

/* USER CODE BEGIN 3 */

/* USER CODE END 3 */
}

Так же необходимо настроить приоритет прерываний для совместной работы с FreeRTOS (в контексте CMSIS RTOS). Устанавливаем приоритет 5 HAL_NVIC_SetPriority(ETH_IRQn, 5, 0) в файле ethernetif.c.

Описания инструментов для визуализации данных.

Ping является утилитой для проверки доступности узла в компьютерной сети, работающей по межсетевому протоколу (Internet Protocol) и измерения времени обращения сообщений, отправленных с компьютера исследователя на целевой компьютер. Название утилиты пришло из области активной эхолокации[1].

Функционирование утилиты ping связано с отправкой пакетов эхо-запроса протокола межсетевых управляющих сообщений (Internet Control Message Protocol (ICMP)) к целевому узлу и ожиданием ICMP-ответов. Во время работы происходит измерение времени от отправки запроса до получения ответа (времени обращения) и запись информации обо всех потерях пакетов. Результаты проверки выводятся в форме статистической сводки, включающей в себя количество принятых пакетов, минимальное, максимальное и среднее время обращения, а также иногда среднеквадратичное отклонение времени обращения.

Использование утилиты ping обычно описывается как тестовый опрос компьютера.

Ping может использоваться с различными параметрами командной строки, зависящими от конкретной реализации утилиты, которые позволяют использовать различные режимы работы, такие, как установка размера пакета для исследования, включение автоматического повторения отправки заданного количества пакетов, параметры учета времени или проведение атаки “наводнения пакетами ping” (ping-flood). Эта атака является простейшей разновидностью атаки отказа в обслуживании при которой атакующий блокирует работу целевой системы обработкой большого количества присланных пакетов с эхо-запросами ICMP.

Базовый синтаксис:
ping < имя узла | IP-адрес узла>

Telnet является протоколом из набора протоколов TCP/IP. Он практически полностью идентичен протоколу программы rlogin из UNIX. Программа telnet позволяет управлять удаленным компьютером с вашего компьютера. Она является программным эмулятором терминала. В прошлом жесткие диски были сложны и дороги (я говорю об очень давнем времени) и не было персональных компьютеров. Для использования имеющихся компьютеров необходимо было выделить часть жесткого диска и использовать терминал для управления системой. Для разработчиков это было прекрасным решением, так как работа с компьютером обходилась дешевле. Необходим был только сервер, с которым можно было создать и поддерживать множество соединений. С telnet вы можете имитировать этот тип распределенных вычислений и, например, управлять суперкомпьютером на расстоянии.

TCP/IP работает с портами, и один из них зарезервирован за telnet. Его номер 23. Существует несколько спецификаций RFC. Спецификация номер 854 от 1983 года носит название “Спецификация протокола telnet” (“Telnet protocol specification”).

С telnet вы можете делать множество вещей, например, отправлять электронную почту, использовать IRC-конференции или прокси-сервера, и даже (хотя, конечно, сложнее) просматривать и изменять содержимое сайтов. Существуют сервисы, предоставляющие возможность поиска в огромных базах данных при помощи telnet. Благодаря telnet, вы можете использовать всю мощь удаленного компьютера, не тратя ценных ресурсов своей системы.

А теперь давайте рассмотрим, что мы сможем сделать при помощи telnet. Синтаксис вызова:
telnet < имя узла | IP-адрес узла>
Telnet должен создать TCP-соединение с системой, IP-адрес и порт которой указаны пользователем для проверки возможности соединения. Если время соединения истекает, telnet не может установить соединение с узлом (возможно, соединение блокируется межсетевым экраном). Если соединение отклонено, telnet может установить соединение с узлом, но либо служба не работает на заданном порту, либо доступ к службе заблокирован.

Настраиваем соединение в linux (Ubuntu).
LwIP config_network

Проверяем успешность соединения.

LwIP ping

Результат работы примера “Счетчик сообщений”.
LwIP counter

Результат работы примера “Эхо”.
LwIP eho

Пример “Введение в LwIP”

1. Инструменты для исследования сетей с интерфейсом командной строки, часть 1

One thought on “Введение в LwIP

  1. Здравствуйте. Очень интересная статья, многое прояснилось. Но не все: в какой файл (файлы) поместить предоставленный на сайте код?

    В Си /Си++ главная функция, в которой выполняется код при запуске – main. В нее инициализацию надо поместить, а про ip – в отдельный файл?

    Если честно, запутался немного в этом… Не понятно, где что в проекте должно находиться?

Comments are closed.