Skip to content

File syn_autoip.c

File List > net > syn_autoip.c

Go to the documentation of this file

#include "syntropic/net/syn_autoip.h"

#include <string.h>

SYN_Status syn_autoip_init(SYN_AUTOIP *autoip, const uint8_t mac_addr[6])
{
    if (!autoip || !mac_addr) {
        return SYN_INVALID_PARAM;
    }
    memset(autoip, 0, sizeof(*autoip));
    autoip->state = SYN_AUTOIP_STATE_INIT;

    /* Generate candidate IP in 169.254.1.1 .. 169.254.254.255 seeded by MAC address */
    uint16_t seed = ((uint16_t)mac_addr[4] << 8) | mac_addr[5];
    uint8_t octet3 = (uint8_t)(1 + (seed % 254));
    uint8_t octet4 = (uint8_t)(1 + ((seed >> 4) % 254));

    autoip->ip_addr = 0xA9FE0000UL | ((uint32_t)octet3 << 8) | octet4;
    return SYN_OK;
}

SYN_Status syn_autoip_build_probe(SYN_AUTOIP *autoip, const uint8_t mac_addr[6], uint8_t *buf_out,
                                  size_t *len_out)
{
    if (!autoip || !mac_addr || !buf_out || !len_out) {
        return SYN_INVALID_PARAM;
    }

    memset(buf_out, 0, 60);
    memset(&buf_out[0], 0xFF, 6); /* Broadcast Dst */
    memcpy(&buf_out[6], mac_addr, 6);
    buf_out[12] = 0x08;
    buf_out[13] = 0x06; /* EtherType = ARP */

    buf_out[14] = 0;
    buf_out[15] = 1; /* Ethernet */
    buf_out[16] = 0x08;
    buf_out[17] = 0x00; /* IPv4 */
    buf_out[18] = 6;
    buf_out[19] = 4;
    buf_out[20] = 0;
    buf_out[21] = 1; /* Request */

    memcpy(&buf_out[22], mac_addr, 6);
    /* Sender IP = 0.0.0.0 for ARP Probe */
    buf_out[28] = 0;
    buf_out[29] = 0;
    buf_out[30] = 0;
    buf_out[31] = 0;

    memset(&buf_out[32], 0, 6);
    buf_out[38] = (uint8_t)(autoip->ip_addr >> 24);
    buf_out[39] = (uint8_t)(autoip->ip_addr >> 16);
    buf_out[40] = (uint8_t)(autoip->ip_addr >> 8);
    buf_out[41] = (uint8_t)(autoip->ip_addr);

    autoip->state = SYN_AUTOIP_STATE_PROBE;
    autoip->probe_count++;
    *len_out = 60;

    return SYN_OK;
}

SYN_Status syn_autoip_build_announce(SYN_AUTOIP *autoip, const uint8_t mac_addr[6],
                                     uint8_t *buf_out, size_t *len_out)
{
    if (!autoip || !mac_addr || !buf_out || !len_out) {
        return SYN_INVALID_PARAM;
    }

    memset(buf_out, 0, 60);
    memset(&buf_out[0], 0xFF, 6);
    memcpy(&buf_out[6], mac_addr, 6);
    buf_out[12] = 0x08;
    buf_out[13] = 0x06;

    buf_out[14] = 0;
    buf_out[15] = 1;
    buf_out[16] = 0x08;
    buf_out[17] = 0x00;
    buf_out[18] = 6;
    buf_out[19] = 4;
    buf_out[20] = 0;
    buf_out[21] = 1;

    memcpy(&buf_out[22], mac_addr, 6);
    /* Sender IP = candidate IP for ARP Announcement */
    buf_out[28] = (uint8_t)(autoip->ip_addr >> 24);
    buf_out[29] = (uint8_t)(autoip->ip_addr >> 16);
    buf_out[30] = (uint8_t)(autoip->ip_addr >> 8);
    buf_out[31] = (uint8_t)(autoip->ip_addr);

    memset(&buf_out[32], 0, 6);
    buf_out[38] = buf_out[28];
    buf_out[39] = buf_out[29];
    buf_out[40] = buf_out[30];
    buf_out[41] = buf_out[31];

    autoip->state = SYN_AUTOIP_STATE_ANNOUNCE;
    autoip->announce_count++;
    *len_out = 60;

    return SYN_OK;
}

SYN_Status syn_autoip_process_arp(SYN_AUTOIP *autoip, SYN_ETH *eth, const uint8_t *arp_frame,
                                  size_t len)
{
    if (!autoip || !arp_frame || len < 42) {
        return SYN_INVALID_PARAM;
    }

    uint16_t ethertype = ((uint16_t)arp_frame[12] << 8) | arp_frame[13];
    if (ethertype != SYN_ETHTYPE_ARP) {
        return SYN_OK;
    }

    uint32_t sender_ip = ((uint32_t)arp_frame[28] << 24) | ((uint32_t)arp_frame[29] << 16) |
                         ((uint32_t)arp_frame[30] << 8) | arp_frame[31];

    /* Conflict check */
    if (sender_ip == autoip->ip_addr) {
        autoip->collisions++;
        /* Pick next candidate IP */
        uint8_t octet4 = (uint8_t)(autoip->ip_addr & 0xFF) + 1;
        if (octet4 == 0 || octet4 == 255)
            octet4 = 1;
        autoip->ip_addr = (autoip->ip_addr & 0xFFFFFF00UL) | octet4;
        autoip->state = SYN_AUTOIP_STATE_INIT;
        return SYN_BUSY;
    }

    /* No conflict detected -> transition to BOUND */
    autoip->state = SYN_AUTOIP_STATE_BOUND;
    if (eth) {
        eth->ip_addr = autoip->ip_addr;
        eth->netmask = SYN_AUTOIP_NETMASK;
        eth->gateway = 0;
    }

    return SYN_OK;
}