204 lines
7.5 KiB
ReStructuredText
204 lines
7.5 KiB
ReStructuredText
..
|
|
Copyright (C) 2023 Jeremie Salvi.
|
|
Permission is granted to copy, distribute and/or modify this document
|
|
under the terms of the GNU Free Documentation License, Version 1.3
|
|
or any later version published by the Free Software Foundation;
|
|
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
|
A copy of the license is included in the section entitled "GNU
|
|
Free Documentation License".
|
|
|
|
Interface ethernet avec QEMU
|
|
============================
|
|
|
|
Y a pas à dire, j'ai bien galéré pour cette partie 😅. Bon, faut dire q'il y avait des choses qui
|
|
ne fonctionnent pas. J'y refient à la fin de cette section.
|
|
|
|
Après pas mal de tatonnements entre la doc et les exemples fournis par espressif, j'ai finalement
|
|
réussi à interagir entre firefox et la machine QEMU. Il y a plusieures choses à bien comprendre
|
|
dans la façon dont fonctionne l'IDF, notament comment sont gérées les
|
|
`différentes couches du modèle OSI <https://fr.wikipedia.org/wiki/Mod%C3%A8le_OSI>`_.
|
|
|
|
Principes
|
|
---------
|
|
|
|
dans l'ESP, il y à trois étapes principales pour le réseau :
|
|
|
|
#. La partie physique (couches physique + mac),
|
|
#. La partie network (couches network + transport),
|
|
#. La partie link entre physique et network.
|
|
|
|
La partie physique
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
C'est la couche physique plus la couche mac. On la configure, on la démare, et ensuite on
|
|
la link à la partie network. Côté wifi, comme le driver de la carte est toujours le même,
|
|
on à la fonction ``esp_netif_create_default_wifi_sta()`` qui gère tout, elle créer tout.
|
|
Elle gère la partie physique, la link à la partie network, et nous retourne un handle de la
|
|
partie network.
|
|
|
|
Pour l'ethernet, c'est différent, comme plusieurs extensions ethernet sont disponible pour
|
|
l'esp32, il y a plusieurs drivers disponibles, il nous faut donc choisir le bon, et configurer ça à la main.
|
|
|
|
Dans notre cas, pour QEMU, le driver à utiliser est le driver openeth. (`cf wiki qemu <https://github.com/espressif/qemu/wiki>`_)
|
|
La configuration de la partie physique se fait à l'aide des fonctions suivantes :
|
|
|
|
.. code-block:: cpp
|
|
:linenos:
|
|
|
|
// Pysical layer configuration
|
|
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
|
phy_config.phy_addr = -1;
|
|
phy_config.reset_gpio_num = -1;
|
|
phy_config.autonego_timeout_ms = 4000;
|
|
s_phy = esp_eth_phy_new_dp83848(&phy_config);
|
|
// Data link layer configuration
|
|
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
|
mac_config.rx_task_stack_size = 4096;
|
|
mac_config.rx_task_prio = 20;
|
|
s_mac = esp_eth_mac_new_openeth(&mac_config);
|
|
// Install Ethernet driver
|
|
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
|
|
esp_eth_driver_install(&config, &esp_eth_handle);
|
|
|
|
La partie network
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
Can cette partie, on gère la couche ip et la couche transport (TCP/UDP/ICMP). Espressif nous donne pour ça tout un tas
|
|
de fonctions, qui sont un wrapper des librairies opensource `lwIP <https://fr.wikipedia.org/wiki/LwIP>`_ (lightweight IP).
|
|
|
|
Elle nous permet d'initialiser ces couches, de gérer l'attribution de l'adressage ip (statique ou DHCP.)
|
|
|
|
Cette couche s'implémente avec les fonctions suivantes :
|
|
|
|
.. code-block:: cpp
|
|
:linenos:
|
|
|
|
// Initialize TCP/IP layers
|
|
esp_netif_init();
|
|
esp_event_loop_create_default();
|
|
esp_netif_config_t esp_netif_config = ESP_NETIF_DEFAULT_ETH();
|
|
esp_netif_t *eth_netif = esp_netif_new(&esp_netif_config);
|
|
|
|
// Set static ip
|
|
// esp_netif_dhcpc_stop(eth_netif);
|
|
// esp_netif_ip_info_t esp_netif_ip_info;
|
|
// esp_netif_ip_info.ip.addr = ESP_IP4TOUINT32(11,2,168,192);
|
|
// esp_netif_ip_info.netmask.addr = ESP_IP4TOUINT32(0,255,255,255);
|
|
// esp_netif_ip_info.gw.addr = ESP_IP4TOUINT32(1,2,168,192);
|
|
// esp_err_t ret2 = esp_netif_set_ip_info(eth_netif, &esp_netif_ip_info);
|
|
// ESP_ERROR_CHECK(ret2);
|
|
|
|
Lien entre les deux premières couches
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
On peut configurer les deux parties précédentes dans l'ordre de notre choix, mais c'est seulement une fois que
|
|
ces deux étapes sont réalisées qu'on peut attacher la couche TCP/IP à la couche physique/MAC.
|
|
|
|
Assez simple à faire, il suffit d'utiliser le code suivant :
|
|
|
|
.. code-block:: cpp
|
|
:linenos:
|
|
|
|
// Bind TCP/IP to Phy+MAC
|
|
esp_eth_netif_glue_handle = esp_eth_new_netif_glue(esp_eth_handle);
|
|
esp_netif_attach(eth_netif, esp_eth_netif_glue_handle);
|
|
|
|
On peut ensuite démarrer ethernet avec la fonction suivante :
|
|
|
|
.. code-block:: cpp
|
|
:linenos:
|
|
|
|
// Start ethernet
|
|
esp_eth_start(esp_eth_handle);
|
|
|
|
La partie applicative
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Ici, on aura l'implémentation de notre serveur web. Ça se fait à partir de la librairie ``esp_http_server``
|
|
Rien de bien complexe ici quand on connait bien le fonctionnement de ce protocole.
|
|
|
|
Le code est le suivant :
|
|
|
|
.. code-block:: cpp
|
|
:linenos:
|
|
|
|
esp_err_t get_handler(httpd_req_t *req)
|
|
{
|
|
/* Send a simple response */
|
|
const char resp[] = "URI GET Response\n";
|
|
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
|
|
ESP_LOGI(TAG, "good");
|
|
return ESP_OK;
|
|
}
|
|
|
|
httpd_uri_t uri_get = {
|
|
.uri = "/uri",
|
|
.method = HTTP_GET,
|
|
.handler = get_handler,
|
|
.user_ctx = NULL
|
|
};
|
|
|
|
Une fois ces trois étapes franchies, on peut démarrer QEMU avec la commande suivante :
|
|
|
|
.. code-block:: bash
|
|
:linenos:
|
|
|
|
/opt/qemu/build/qemu-system-xtensa -m 128 -nographic -machine esp32 \
|
|
-drive file=flash_img.bin,if=mtd,format=raw \
|
|
-net nic,model=open_eth \
|
|
-net user,host=192.168.2.1,net=192.168.2.0/24,hostfwd=tcp::80-:80 \
|
|
-net tap,script=no,downscript=no
|
|
|
|
On teste avec un simple ``curl http://127.0.0.1/uri`` et on voit que ça marche.
|
|
|
|
.. image:: ../images/esp32/QEMU_http_response.png
|
|
:width: 600
|
|
|
|
Ce qui ne marche pas
|
|
--------------------
|
|
|
|
L'IPv6
|
|
~~~~~~
|
|
|
|
Ne marche pas ni en wifi avec SLAAC, en wifi avec une ip statique,
|
|
ni avec QEMU. C'est ce qui m'a le plus fait perdre de temps.
|
|
En m'acharnant, je suis tombé sur ça 😤 🤬:
|
|
|
|
.. code-block:: bash
|
|
:linenos:
|
|
|
|
esp_err_t esp_netif_add_ip6_address(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr, uint8_t preference)
|
|
{
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
Il manque clairement du travail du côté d'espressif pour implémenter la couche IPv6. C'est embêtant, ça nous
|
|
oblige à faire pleins de routes dans notre box, alors qu'on pourrait tout simplement pinger notre domotique de
|
|
l'extérieur avec du v6
|
|
|
|
.. note:: Il n'y a que la librairie loobback.c qui manque d'implémentation, dans
|
|
components/lwip/lwip/src/core/netif.c on trouve
|
|
``netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx)``
|
|
qui est bel et bien implémentée. J'ai perdu du temps dessus, ça sera un NAT v4 pour l'instant.
|
|
Si à l'avenir j'arrive à faire marcher l'IPv6, je le documenterai.
|
|
|
|
L'interface tun0 et le bridging
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
auand je lance ma commande QEMU, j'ai deux moyens d'accéder à mon ``http_server`` :
|
|
Soit via le ``hostfwd=tcp::80-:80``, soit via ``-net tap,script=no,downscript=no``.
|
|
|
|
Je fait un ``ip link set tap0 up et brctl addif br0 tap0`` pour que mon tap0 soit
|
|
pingable depuis le bridge en 192.168.2.15.
|
|
|
|
Et bien, un ``curl http://127.0.0.1/uri`` marche très bien, un ``curl http://192.168.2.15/uri``
|
|
ne parche pas, on à une erreur ``W (476492) opencores.emac: emac_opencores_isr_handler: RX frame dropped (0x14)``
|
|
J'ai essayé de mettre au max les buffers RX, testé toutes les configurations possible et imaginables,
|
|
impossible de passer par le bridge sans avoir un frame_drop. Grace au mod débug, j'arrive ici :
|
|
|
|
``components/esp_eth_src/esp_eth_mac_openeth.c``
|
|
|
|
.. image:: ../images/esp32/openeth_busy.png
|
|
:width: 600
|
|
|