Implémentation des requêtes et réponses ARP sur plateforme LPC1850

Format du paquet ARP Ethernet

Le protocole ARP (Address Resolution Protocol) permet de mapper une adresse IP vers une adresse MAC dans un réseau local. Le format d’un paquet ARP encapsulé dans une trame Ethernet suit la structure suivatne :

  • Adresse MAC destination : 6 octets – généralement l’adresse physique du destinataire, ou l’adresse de diffusion FF:FF:FF:FF:FF:FF si inconnue.
  • Adresse MAC source : 6 octets – identifie l’émetteur.
  • Type de trame : 2 octets – indique le protocole supérieur. Pour ARP, la valeur est 0x0806.
  • Type de matériel : 2 octets – pour Ethernet, fixé à 0x0001.
  • Type de protocole : 2 octets – spécifie le protocole réseau (ex. IPv4 = 0x0800).
  • Taille de l'adresse matérielle : 1 octet – vaut 6 pour une adresse MAC.
  • Taille de l'adresse protocole : 1 octet – vaut 4 pour IPv4.
  • Opération : 2 octets – 0x0001 pour requête ARP, 0x0002 pour réponse.
  • Adresse MAC émetteur : 6 octets.
  • Adresse IP émetteur : 4 octets.
  • Adresse MAC cible : 6 octets – mise à zéro dans les requêtes.
  • Adresse IP cible : 4 octets.

Gestion des opérations ARP sur microcontrôleur LPC1850

Le code ci-dessous illustre l’initialisation d’un périphérique basé sur le microcontrôleur NXP LPC1850 afin d’émettre des requêtes ARP et traiter les réponses reçues via l’interface EMAC (Ethernet MAC).

Fonction principale d'initialisation

#include "LPC18xx.h"
#include "led.h"

extern void taskEth(void);

int main(void)
{
    SystemInit();
    ledInit();
    SysTick_Config(SystemCoreClock / 1000);  // Intervalle d'interruption toutes les ms

    taskEth();

    while (1);
}

void SysTick_Handler(void)
{
    static uint32_t tick = 0;
    if (++tick >= 1000) {
        tick = 0;
        // Exemple : clignotement LED toutes les secondes
    }
}

Tâche principale réseau

La fonction taskEth configure l’interface Ethernet, assigne une adresse MAC/IP locale, puis envoie une requête ARP au démarrage. Elle surveille ensuite les trames entrantes.

#include <stdlib.h>
#include "lpc18xx_emac.h"
#include "lpc18xx_debug.h"

extern void ipInit(uint8_t *mac, uint32_t ip);
extern uint32_t ipRcvMacFrame(uint8_t *buf, uint32_t len);
extern void arpSndRsp1(uint32_t dstIp, uint32_t srcIp, uint8_t *dstMac, uint8_t *srcMac);

static volatile uint8_t frameReady = 0;
static uint8_t rxBuffer[2048];
static uint8_t localMac[6] = {0x11, 0x1F, 0xE0, 0x12, 0x1E, 0x0F};
static uint8_t broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

void ethDataReadyCb(void)
{
    frameReady = 1;
}

void taskEth(void)
{
    debugComInit();
    uartPrint("UART ready\r\n");

    while (!ethInit(ethDataReadyCb, localMac));

    uartPrint("EMAC initialized\r\n");
    ipInit(localMac, 0xC0A801BE);  // 192.168.1.190

    arpSndRsp1(0xC0A80102, 0xC0A801BE, broadcastMac, localMac);  // Requête vers 192.168.1.2

    while (1) {
        if (!frameReady) continue;

        uint32_t length = ethRead(rxBuffer, sizeof(rxBuffer));
        if (length > 0) {
            ipRcvMacFrame(rxBuffer, length);
        }
        frameReady = 0;
    }
}

Émission d'une requête ou réponse ARP

La fonction arpSndRsp1 construit manuellement une trame ARP selon les spécifications. Elle gère aussi bien les requêtes que les réponses.

static uint32_t txPacket[64];

void arpSndRsp1(uint32_t targetIp, uint32_t senderIp, uint8_t *dstMac, uint8_t *srcMac)
{
    uint8_t *frame = (uint8_t*)txPacket;

    memcpy(frame + 0,  dstMac, 6);      // Dest MAC
    memcpy(frame + 6,  srcMac, 6);     // Source MAC
    *(uint16_t*)(frame + 12) = htons(0x0806);  // Type ARP

    // Entête ARP
    *(uint16_t*)(frame + 14) = htons(0x0001);  // Ethernet
    *(uint16_t*)(frame + 16) = htons(0x0800);  // IPv4
    frame[18] = 6;  // Taille MAC
    frame[19] = 4;  // Taille IP
    *(uint16_t*)(frame + 20) = htons(0x0001);  // Requête ARP

    memcpy(frame + 22, srcMac, 6);           // Expéditeur MAC
    *(uint32_t*)(frame + 28) = senderIp;     // Expéditeur IP
    memcpy(frame + 32, dstMac, 6);           // Cible MAC (inconnue → 0)
    *(uint32_t*)(frame + 38) = targetIp;     // Cible IP

    memset(frame + 42, 0, 18);  // Remplissage jusqu'à 60 octets minimum

    ethWrite(frame, 60);
    uartPrint("ARP sent\r\n");
}

Traitement des trames reçues

La fonction ipRcvMacFrame filtre les trames entrantes par type (ARP ou IP) et déclenche le traitement approprié.

uint32_t ipRcvMacFrame(uint8_t *buf, uint32_t len)
{
    if (len < 14) return 0;

    uint16_t etherType = ntohs(*(uint16_t*)(buf + 12));

    switch (etherType) {
        case 0x0806:  // ARP
            arpRcv(buf + 14, len - 14);
            break;
        case 0x0800:  // IPv4
            ipRcv(buf + 14, len - 14, buf + 6);
            break;
        default:
            break;
    }
    return 1;
}

Analyse d’une requête ARP entrante

La fonction arpRcv extrait les adresses IP/MAC et vérifie si la requête concerne l’appareil local. Si une réponse est nécessaire, elle peut être générée via arpSndRsp.

void arpRcv(uint8_t *payload, uint32_t plen)
{
    if (plen < 28) return;

    uint16_t operation = ntohs(*(uint16_t*)(payload + 6));
    uint32_t targetIp = *(uint32_t*)(payload + 24);

    // Vérifier si l'IP cible correspond à celle du périphérique
    if (targetIp != g_ethIpAddr) return;

    if (operation == 0x0001) {  // Requête ARP
        uint8_t *senderMac = payload + 8;
        uint32_t senderIp = *(uint32_t*)(payload + 14);

        uartPrint("ARP Request from %d.%d.%d.%d\r\n",
            (senderIp>>0)&0xFF, (senderIp>>8)&0xFF,
            (senderIp>>16)&0xFF, (senderIp>>24)&0xFF);

        // Envoyer une réponse ARP
        arpSndRsp(senderIp, senderMac);
    }
}

Réponse ARP automatique

Lorsqu’une requête ARP est reçue, une réponse est envoyée avec l’adresse MAC locale.

void arpSndRsp(uint32_t dstIp, uint8_t *dstMac)
{
    uint8_t *frame = (uint8_t*)txPacket;

    memcpy(frame + 0,  dstMac, 6);
    memcpy(frame + 6,  g_ethMacAddr, 6);
    *(uint16_t*)(frame + 12) = htons(0x0806);

    // En-tête ARP
    *(uint16_t*)(frame + 14) = htons(0x0001);
    *(uint16_t*)(frame + 16) = htons(0x0800);
    frame[18] = 6; frame[19] = 4;
    *(uint16_t*)(frame + 20) = htons(0x0002);  // Réponse

    memcpy(frame + 22, g_ethMacAddr, 6);
    *(uint32_t*)(frame + 28) = g_ethIpAddr;
    memcpy(frame + 32, dstMac, 6);
    *(uint32_t*)(frame + 38) = dstIp;

    memset(frame + 42, 0, 18);
    ethWrite(frame, 60);
}

Initialisation des paramètres réseau

Associe une adresse MAC et IP au contexte global du module réseau.

void ipInit(uint8_t *mac, uint32_t ip)
{
    memcpy(g_ethMacAddr, mac, 6);
    g_ethIpAddr = ip;
}

Utilitaires de conversion et checksum

Les fonctions htons, ntohs assurent la conversion entre ordre des octets hôte et réseau. La somme de contrôle IP est calculée sur 16 bits.

uint16_t htons(uint16_t val)
{
    return (val << 8) | (val >> 8);
}

uint16_t calcChecksum(uint16_t *data, uint32_t size)
{
    uint32_t sum = 0;
    while (size > 1) {
        sum += *data++;
        size -= 2;
    }
    if (size) sum += *(uint8_t*)data;

    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    return ~sum;
}

Ce système permet à un microcontrôleur LPC1850 de participer activement à la résolution d’adresses dans un réseau Ethernet local via le protocole ARP, en émettant des requêtes et en répondant correctement aux sollicitations.

Étiquettes: ARP Ethernet LPC1850 EMAC embedded-networking

Publié le 8 août à 19h47