#include "lwip/opt.h"
#if LWIP_IPV4 && LWIP_AUTOIP
#include "lwip/mem.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/autoip.h"
#include "lwip/acd.h"
#include "lwip/etharp.h"
#include "lwip/prot/autoip.h"
#include <string.h>
#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
lwip_htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
#endif
static void autoip_restart(struct netif *netif);
static void autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr);
static err_t autoip_bind(struct netif *netif);
static void autoip_conflict_callback(struct netif *netif,
acd_callback_enum_t state);
void
autoip_set_struct(struct netif *netif, struct autoip *autoip)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("autoip != NULL", autoip != NULL);
LWIP_ASSERT("netif already has a struct autoip set",
netif_autoip_data(netif) == NULL);
memset(autoip, 0, sizeof(struct autoip));
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
}
void
autoip_remove_struct(struct netif *netif)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("netif has no struct autoip set",
netif_autoip_data(netif) != NULL);
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, NULL);
}
static void
autoip_restart(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
autoip->tried_llipaddr++;
autoip_start(netif);
}
static void
autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr)
{
struct autoip *autoip = netif_autoip_data(netif);
u32_t addr = lwip_ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
addr += autoip->tried_llipaddr;
addr = AUTOIP_NET | (addr & 0xffff);
if (addr < AUTOIP_RANGE_START) {
addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
}
if (addr > AUTOIP_RANGE_END) {
addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
}
LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
(addr <= AUTOIP_RANGE_END));
ip4_addr_set_u32(ipaddr, lwip_htonl(addr));
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
(u16_t)(autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
}
static err_t
autoip_bind(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
ip4_addr_t sn_mask, gw_addr;
autoip->state = AUTOIP_STATE_BOUND;
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
(void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
IP4_ADDR(&sn_mask, 255, 255, 0, 0);
IP4_ADDR(&gw_addr, 0, 0, 0, 0);
netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr);
return ERR_OK;
}
static void
autoip_conflict_callback(struct netif *netif, acd_callback_enum_t state)
{
struct autoip *autoip = netif_autoip_data(netif);
switch (state) {
case ACD_IP_OK:
autoip_bind(netif);
break;
case ACD_RESTART_CLIENT:
autoip_restart(netif);
break;
case ACD_DECLINE:
ip4_addr_set_any(&autoip->llipaddr);
autoip_stop(netif);
break;
default:
break;
}
}
err_t
autoip_start(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
err_t result = ERR_OK;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
if (autoip == NULL) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_start(): starting new AUTOIP client\n"));
autoip = (struct autoip *)mem_calloc(1, sizeof(struct autoip));
if (autoip == NULL) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_start(): could not allocate autoip\n"));
return ERR_MEM;
}
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip\n"));
}
if (autoip->state == AUTOIP_STATE_OFF) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("autoip_start(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0],
netif->name[1], (u16_t)netif->num));
acd_add(netif, &autoip->acd, autoip_conflict_callback);
if (!ip4_addr_islinklocal(&autoip->llipaddr)) {
autoip_create_addr(netif, &(autoip->llipaddr));
}
autoip->state = AUTOIP_STATE_CHECKING;
acd_start(netif, &autoip->acd, autoip->llipaddr);
} else {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("autoip_start(): already started on netif=%p %c%c%"U16_F"\n",
(void *)netif, netif->name[0],
netif->name[1], (u16_t)netif->num));
}
return result;
}
void
autoip_network_changed_link_up(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
if (autoip && (autoip->state != AUTOIP_STATE_OFF) && !LWIP_DHCP_AUTOIP_COOP) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_network_changed_link_up(): start acd\n"));
autoip->state = AUTOIP_STATE_CHECKING;
acd_start(netif, &autoip->acd, autoip->llipaddr);
}
}
void
autoip_network_changed_link_down(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
if (autoip && (autoip->state != AUTOIP_STATE_OFF) && LWIP_DHCP_AUTOIP_COOP) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_network_changed_link_down(): stop autoip\n"));
autoip_stop(netif);
}
}
err_t
autoip_stop(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
LWIP_ASSERT_CORE_LOCKED();
if (autoip != NULL) {
autoip->state = AUTOIP_STATE_OFF;
if (ip4_addr_islinklocal(netif_ip4_addr(netif))) {
netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
}
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,("autoip_stop()\n"));
}
return ERR_OK;
}
u8_t
autoip_supplied_address(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
return (autoip != NULL)
&& (ip4_addr_eq(netif_ip4_addr(netif), &(autoip->llipaddr)))
&& (autoip->state == AUTOIP_STATE_BOUND);
}
u8_t
autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr)
{
struct autoip *autoip = netif_autoip_data(netif);
return (autoip != NULL)
&& (ip4_addr_eq(addr, &(autoip->llipaddr)))
&& (autoip->state == AUTOIP_STATE_BOUND);
}
#endif